My Emacs config
What's Emacs?
For the uninitiated, Emacs is thought of as a text editor (usually compared against vi/vim as part of the great editor war) but it's actually a lisp interpreter, and text editing is just one of its capabilities. It's capable of countless different things, and the Emacs community is always adding new capabilities.
For anyone curious about learning Emacs, I would highly recommend watching UncleDave's tutorials. It's also a good idea to check out other people's config files or my own config on this page, and putting the parts you like into your own Emacs configuration file (usually /.emacs.d/init.el
by default). There are also Emacs distributions where the configuration has already been done, such as Spacemacs and Doom Emacs. I personally recommend against those though as you will end up learning a lot more and having a much more personalised setup if you build up gradually from default Emacs.
How I use Emacs
Below lists all the things I use Emacs for and how:
Feature | Packages | Notes |
---|---|---|
Calculator | scratch (built-in) | Conventional calculator packages exist also, but scratch uses lisp expressions which are far more powerful |
Configuration | org-babel | Org-babel allows me to configure any program or script (not just Emacs ones) within a single file, making organisation far simpler and more portable |
EPUB reader | nov.el | |
Emails | mu4e | It works fully for Gmail, but I'm still figuring out how to configure it for other email accounts |
File management | dired (built-in), dired-x, openwith, all-the-icons-dired | |
Launching non-emacs programs | dmenu | |
Music player | mingus, mingus-header-mode | |
PDF reader | pdf-tools | |
RSS | elfeed, elfeed-org | I have an elfeed webpage which lists the content I follow and explains how I use elfeed |
Terminal emulation | eshell, ansi-term, term (all built-in) | I use eshell in most cases, and ansi-term for things eshell can't do well (e.g ncurses) |
To-do lists and notes | orgmode (built in) | |
Torrent client | mentor | If I open it manually, it works as intended, but I can't get launching at startup to work |
Web development | org-static-blog, babel | At some point I'll make a blogpost explaining how I made this website in Emacs |
Window manager | exwm |
I try to do as much as I can within Emacs as having everything integrated into a single program configured exactly as I like is really nice. Some things I don't currently do in Emacs but would like to in the future include:
Feature | Packages |
---|---|
Cryptocurrency wallet balance tracker (displaying on the modeline) | I've not found a package for this yet, but it may be possible to do by pulling the current value of crytocurrencies url.el and multipying them by the ammount of cryptocurrency I currently hold |
Git management | magit |
Reading comics/manga (.CBR/.CBZ files) | ? - haven't looked into yet |
Remote file editing | tramp |
IRC | erc |
Private chat | ? - haven't looked into yet |
Search websites without a web browser | ytel/ivy-youtube for YouTube. I haven't found equivalents for other websites |
My config
The original source is an orgmode file, which is then automatically converted into 2 additional files:
- An elisp file so Emacs can run it. This is done via babel.
- The webpage you're currently viewing. This is done via org-static-blog.
Below are the configuration rules I use:
Preamble
Increase rubish collection during startup:
(setq startup/gc-cons-threshold gc-cons-threshold) (setq gc-cons-threshold most-positive-fixnum) (defun startup/reset-gc () (setq gc-cons-threshold startup/gc-cons-threshold)) (add-hook 'emacs-startup-hook 'startup/reset-gc)
Initialise packages:
(require 'package) (setq package-archives '(("org" . "http://orgmode.org/elpa/") ("gnu" . "http://elpa.gnu.org/packages/") ("melpa" . "http://melpa.org/packages/"))) (package-initialize)
Load up use-package:
(unless (package-installed-p 'use-package) (package-refresh-contents) (package-install 'use-package)) (require 'use-package)
Set default web browser to Nyxt:
(setq browse-url-browser-function 'browse-url-generic browse-url-generic-program "nyxt")
Use asynchronous processes wherever possible:
(use-package async :ensure t :init (dired-async-mode 1))
Interface settings
EXWM install and config:
(use-package exwm :ensure t :config ;; necessary to configure exwm manually (require 'exwm-config) ;; fringe size, most people prefer 1 (fringe-mode 1) ;; emacs as a daemon, use "emacsclient <filename>" to seamlessly edit files from the terminal directly in the exwm instance (server-start) ;; a number between 1 and 9, exwm creates workspaces dynamically so I like starting out with 1 (setq exwm-workspace-number 1) ;; this is a way to declare truly global/always working keybindings ;; this is a nifty way to go back from char mode to line mode without using the mouse (exwm-input-set-key (kbd "s-r") #'exwm-reset) (exwm-input-set-key (kbd "s-k") #'exwm-workspace-delete) (exwm-input-set-key (kbd "s-w") #'exwm-workspace-swap) ;; the simplest launcher, I keep it in only if dmenu eventually stopped working or something (exwm-input-set-key (kbd "s-&") (lambda (command) (interactive (list (read-shell-command "$ "))) (start-process-shell-command command nil command))) ;; this just enables exwm, it started automatically once everything is ready (exwm-enable))
Disable menubar, scrollbar, toolbar and fringe:
(menu-bar-mode -1) (tool-bar-mode -1) (fringe-mode -1) (scroll-bar-mode -1)
Disable scratch buffer's initial message:
(setq initial-scratch-message "")
Set UTF-8 encoding:
(setq locale-coding-system 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)
(prefer-coding-system 'utf-8)
Line indicator options:
;;highlight current line (global-hl-line-mode +1) ;; show line numbers only on code files (add-hook 'prog-mode-hook #'display-line-numbers-mode)
Change and 'yes or no' prompts to 'y or n':
(fset 'yes-or-no-p 'y-or-n-p)
Load up Gruvbox theme:
(use-package gruvbox-theme :ensure t :init (load-theme 'gruvbox-dark-hard t)) ;; set default font (set-face-attribute 'default nil :font "DejaVu Sans Mono-8")
Add clock in modeline:
;; format (setq display-time-24hr-format t) (setq display-time-format "%H:%M - %d %B %Y") ;; enable clock (display-time-mode 1)
Set colors for packages:
(custom-set-faces ;; custom-set-faces was added by Custom. ;; If you edit it by hand, you could mess it up, so be careful. ;; Your init file should contain only one such instance. ;; If there is more than one, they won't work right. '(elfeed-search-date-face ((t (:inherit font-lock-builtin-face :foreground "#d3869b" :underline nil)))) '(elfeed-search-feed-face ((t (:foreground "#fabd2f")))) '(elfeed-search-filter-face ((t (:inherit mode-line-buffer-id :weight normal)))) '(elfeed-search-last-update-face ((t (:inherit font-lock-comment-face :foreground "#fdf4c1")))) '(elfeed-search-tag-face ((t (:foreground "#83a598")))) '(elfeed-search-title-face ((t (:foreground "#fdf4c1")))) '(elfeed-search-unread-count-face ((t (:inherit font-lock-comment-face :foreground "#fdf4c1")))) '(elfeed-search-unread-title-face ((t nil))) '(mentor-download-message ((t (:foreground "#e27878")))) '(mentor-download-name ((t (:foreground "#83a598")))) '(mentor-download-size ((t (:foreground "#fabd2f")))) '(mentor-tracker-name ((t (:foreground "#b8bb26")))) '(mingus-album-face ((t (:foreground "#fb4933" :underline t)))) '(mingus-album-stale-face ((t (:foreground "#fb4933")))) '(mingus-artist-face ((t (:foreground "#d3869b")))) '(mingus-mark-face ((t (:foreground "#e2a478" :weight bold)))) '(mingus-song-file-face ((t (:foreground "#8ec07c")))) '(mingus-stopped-face ((t (:foreground "#e27878")))))
When window is split, change focus to the new split:
(defun split-and-follow-horizontally () (interactive) (split-window-below) (balance-windows) (other-window 1)) (global-set-key (kbd "C-x 2") 'split-and-follow-horizontally) (defun split-and-follow-vertically () (interactive) (split-window-right) (balance-windows) (other-window 1)) (global-set-key (kbd "C-x 3") 'split-and-follow-vertically)
Set C-c e
to edit init file:
(defun config-visit () (interactive) (find-file "~/website/posts/config.org")) (global-set-key (kbd "C-c e") 'config-visit)
Reload init file when C-c r
is pressed:
(defun config-reload () "Reloads ~/.emacs.d/config.org at runtime" (interactive) (org-babel-load-file (expand-file-name "~/.emacs.d/init.el"))) (global-set-key (kbd "C-c r") 'config-reload)
Start EXWM:
(require 'exwm) (require 'exwm-config) (exwm-config-default)
Load up powerline, using default theme:
(use-package powerline :ensure t :init (powerline-default-theme))
Define custom layouts:
;; Media layout (defun lmedia () (interactive) (dired "~/") (split-window-below) (mingus) (mingus-cancel-timer) ;; stops mingus default behaviour of redisplaying every second, which caused xwindows to flicker and lose focus (mingus-header-mode) (split-window-right) (elfeed) (other-window 2) (split-window-right) (dired "~/")) ;; Social layout (defun lsocial () (interactive) (mu4e) (split-window-right) (erc) ) ;; Web/blog authoring layout to be added
Interface keybindings
Open a new instance of eshell with Super + Enter
:
(defun eshell-new() "Open a new instance of eshell." (interactive) (eshell 'N)) (exwm-input-set-key (kbd "<s-return>") #'eshell-new) ; the code below allowes setting urxvt as terminal ; (exwm-input-set-key (kbd "<s-return>") ; (lambda () ; (interactive) ; (start-process-shell-command "urxvt" nil "urxvt")))
Lock screen with Super + l
:
(exwm-input-set-key (kbd "s-l") (lambda () (interactive) (start-process-shell-command "slock" nil "slock")))
Screenshot with Super + shift + P
:
(defun take-screenshot () "Takes a fullscreen screenshot of the current workspace" (interactive) (when window-system (loop for i downfrom 3 to 1 do (progn (message (concat (number-to-string i) "...")) (sit-for 1))) (message "Cheese!") (sit-for 1) (start-process "screenshot" nil "import" "-window" "root" (concat (getenv "HOME") "/" (subseq (number-to-string (float-time)) 0 10) ".png")) (message "Screenshot taken!"))) (exwm-input-set-key (kbd "s-P") 'take-screenshot)
Close all buffers with C-M-s-k
:
(defun close-all-buffers () "Kill all buffers without regard for their origin." (interactive) (mapc 'kill-buffer (buffer-list))) (global-set-key (kbd "C-M-s-k") 'close-all-buffers)
Bind C-x 1
to toggle between a single window and previous window splits (useful for things like temporary image viewing, pdf-viewing, etc):
(defvar window-split-saved-config nil) (defun window-split-toggle-one-window () "Make the current window fill the frame. If there is only one window try reverting to the most recently saved window configuration." (interactive) (if (and window-split-saved-config (not (window-parent))) (set-window-configuration window-split-saved-config) (setq window-split-saved-config (current-window-configuration)) (delete-other-windows))) (global-set-key (kbd "C-x 1") 'window-split-toggle-one-window)
General packages
Capture
Allows screen capture recordings:
(require 'capture) (setq capture-video-dest-dir "~/screencasts/SORT/") (global-set-key (kbd "s-c") 'capture-run-mode) (defun my-capture-presets () "Make my presets for capturing." (interactive) (capture-presets-clear) (capture-add-preset 454 74 1280 720 15 "webm" "" "1280px (no audio)")) (my-capture-presets)
Chan
Browse 4chan within Emacs:
(use-package chan :load-path "lisp/chan")
Dired
Add icons:
;;load dired icons (add-hook 'dired-mode-hook 'all-the-icons-dired-mode) ;; reduce lag from dired-icons (setq inhibit-compacting-font-caches t)
List directories before files:
(defun mydired-sort () "Sort dired listings with directories first." (save-excursion (let (buffer-read-only) (forward-line 2) ;; beyond dir. header (sort-regexp-fields t "^.*$" "[ ]*." (point) (point-max))) (set-buffer-modified-p nil))) (defadvice dired-readin (after dired-after-updating-hook first () activate) "Sort dired listings with directories first before adding marks." (mydired-sort))
Show file sizes in KB, MB, GB instead of just bytes:
(setq-default dired-listing-switches "-alh")
Set file asociations:
;; load the packaged named xyz. (load "openwith") ;; best not to include the ending “.el” or “.elc” (setq large-file-warning-threshold nil) (require 'openwith) (openwith-mode t) (setq openwith-associations '( ("\\.3gp\\|.aac\\|.avi\\||.flac\\|.flv\\|.m4a\\|.m4b\\|.m4v\\|.mkv\\|.mp4\\|.mov\\|.mpg\\|.ogg\\|.ogm\\|.wav\\|.webm\\|.wmv\\'" "mpv" (file)) ;;("\\.png\\|.jp?g\\|.gif\\|.webp\\'" "viewnior" (file)) ("\\.exe\\'" "wine" (file)) )) ;; set epub files to open in nov.el (add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode)) (add-to-list 'auto-mode-alist '("\\.\\(cbr\\)\\'" . archive-mode))
Loop animated images by default (e.g .gifs), and scale to fit buffer height by default:
(use-package image :defer t :init (add-hook 'image-mode-hook 'image-transform-fit-to-height) (setq image-toggle-animation t) (setq image-animate-loop t))
Delete opened images fuction (WIP):
(defun image-delete (confirm) (interactive (list (y-or-n-p (format "Delete %s?" buffer-file-name)))) (when confirm (delete-file (buffer-file-name)))) ;; (define-key image-mode-map (kbd "D") 'image-delete)
Hide dotfiles by default, and add super + h
keybinding to toggle:
(add-hook 'dired-load-hook '(lambda () (require 'dired-x))) ; Load Dired X when Dired is loaded. (setq dired-omit-mode t) ; Turn on Omit mode. (require 'dired-x) (setq-default dired-omit-files-p t) ; Buffer-local variable (setq dired-omit-files (concat dired-omit-files "\\|^\\..+$")) ;; keybinding toggle (define-key dired-mode-map (kbd "s-h") 'dired-omit-mode)
Dmenu
Allow external commands to be launched within emacs when keybinding super+d
is pressed:
(use-package dmenu :ensure t :bind ("s-d" . 'dmenu))
Elfeed
Elfeed keyboard shortcut:
(global-set-key (kbd "C-x w") 'elfeed)
Load elfeed-org to allow rss feeds to be set up with an org file:
;; Load elfeed-org (require 'elfeed-org) ;; Initialize elfeed-org (elfeed-org) ;; Optionally specify a number of files containing elfeed ;; configuration. If not set then the location below is used. (setq rmh-elfeed-org-files (list "~/website/posts/elfeed.org"))
Hide tags:
;; (defun anon/elfeed-search-print-entry (entry) ;; "Print ENTRY to anon's buffer without tags" ;; (let* ((date (elfeed-search-format-date (elfeed-entry-date entry))) ;; (title (or (elfeed-meta entry :title) (elfeed-entry-title entry) "")) ;; (title-faces (elfeed-search--faces (elfeed-entry-tags entry))) ;; (feed (elfeed-entry-feed entry)) ;; (feed-title ;; (when feed ;; (or (elfeed-meta feed :title) (elfeed-feed-title feed)))) ;; (title-width (- (window-width) 10 elfeed-search-trailing-width)) ;; (title-column (elfeed-format-column ;; title (elfeed-clamp ;; elfeed-search-title-min-width ;; title-width ;; elfeed-search-title-max-width) ;; :left))) ;; (insert (propertize date 'face 'elfeed-search-date-face) " ") ;; (insert (propertize title-column 'face title-faces 'kbd-help title) " ") ;; (when feed-title ;; (insert (propertize feed-title 'face 'elfeed-search-feed-face) " ")))) ;; (setq elfeed-search-print-entry-function #'anon/elfeed-search-print-entry)
Set default search filter for video updates uploaded up to 5 days ago:
(setq-default elfeed-search-filter "@5-days-ago +videos")
Open youtube videos with mpv:
(defun elfeed-play-with-mpv () "Play entry link with mpv." (interactive) (let ((entry (if (eq major-mode 'elfeed-show-mode) elfeed-show-entry (elfeed-search-selected :single))) (quality-arg "") ) (message "Opening %s with mpv..." (elfeed-entry-link entry)) (start-process "elfeed-mpv" nil "mpv" (elfeed-entry-link entry)))) (defvar elfeed-mpv-patterns '("youtu\\.?be") "List of regexp to match against elfeed entry link to know whether to use mpv to visit the link.")
Open in mpv when o
is pressed:
(eval-after-load 'elfeed-search
'(define-key elfeed-search-mode-map (kbd "o") 'elfeed-play-with-mpv))
Download in ~/downloads/youtube/
when d
is pressed.. in theory. I haven't actually managed to get this to work yet, I get the error Wrong type-argument: elfeed-entry nil
when I press d
currently. If you know why this is, please let me know.
;; Set executable path (setq youtube-dl-path "/usr/bin/youtube-dl") ;; Set video storage path (setq youtube-dl-output-dir "~/Downloads/youtube-dl/") (defun elfeed-download-video (entry) "Download a video using youtube-dl." (interactive (list (elfeed-search-selected :ignore-region))) (async-shell-command (format "%s -o \"%s%s\" -f bestvideo+bestaudio %s" youtube-dl-path youtube-dl-output-dir "%(title)s.%(ext)s" (elfeed-entry-link entry)))) ;; set keybinding (eval-after-load 'elfeed-search '(define-key elfeed-search-mode-map (kbd "d") 'elfeed-download-video))
Appearance settings:
(setq-default elfeed-initial-tags nil) (setq-default elfeed-search-date-format (quote ("%a, %R" 10 :left))) (setq-default elfeed-curl-max-connections 100) (setq-default elfeed-search-trailing-width 30)
Engine-mode
Enable engine-mode, which allows searching across websites directly within Emacs:
(require 'engine-mode) (engine-mode t)
Below are defined search engines and I can add as many as I'd like. To use them I just press C-x /
followed by whatever key I've defined below. For example eBay search is C-x / e
:
(defengine amazon "https://www.amazon.co.uk/s?k=%s" :keybinding "a") (defengine bitchute "https://www.bitchute.com/search/?query=%s" :keybinding "b") (defengine duckduckgo "https://duckduckgo.com/?q=%s" :keybinding "d") (defengine ebay "https://www.ebay.co.uk/sch/i.html?_nkw=%s" :keybinding "e") (defengine github "https://github.com/search?ref=simplesearch&q=%s") (defengine google "http://www.google.com/search?ie=utf-8&oe=utf-8&q=%s" :keybinding "g") (defengine google-images "https://www.google.co.uk/search?tbm=isch&q=%s" :keybinding "i") (defengine google-maps "http://maps.google.com/maps?q=%s" :docstring "Mappin' it up.") (defengine odyseee "https://odysee.com/$/search?q=%s" :keybinding "o") (defengine peertube "https://search.joinpeertube.org/search?search=%s" :keybinding "p") (defengine project-gutenberg "http://www.gutenberg.org/ebooks/search/?query=%s") (defengine qwant "https://www.qwant.com/?q=%s") (defengine rfcs "http://pretty-rfc.herokuapp.com/search?q=%s") (defengine stack-overflow "https://stackoverflow.com/search?q=%s") (defengine startpage "https://searx.info/search?q=%s" :keybinding "s") (defengine twitter "https://twitter.com/search?q=%s") (defengine wikipedia "http://www.wikipedia.org/search-redirect.php?language=en&go=Go&search=%s" :keybinding "w" :docstring "Searchin' the wikis.") (defengine wiktionary "https://www.wikipedia.org/search-redirect.php?family=wiktionary&language=en&go=Go&search=%s") (defengine wolfram-alpha "http://www.wolframalpha.com/input/?i=%s") (defengine youtube "http://www.youtube.com/results?aq=f&oq=&search_query=%s" :keybinding "y")
Ivy
Load up Ivy:
(ivy-mode 1)
Mentor
Keep torrents from previous session:
; (setq-default mentor-rtorrent-keep-session t)
Mu4e
Email for Emacs:
(use-package mu4e :ensure nil ;; :load-path "/usr/share/emacs/site-lisp/mu4e/" :defer 20 ; Wait until 20 seconds after startup :config ;; This is set to 't' to avoid mail syncing issues when using mbsync (setq mu4e-change-filenames-when-moving t) ;; Refresh mail using isync every 10 minutes (setq mu4e-update-interval (* 10 60)) (setq mu4e-get-mail-command "mbsync -a") (setq mu4e-maildir "~/Mail") ;; Configure the function to use for sending mail (setq message-send-mail-function 'smtpmail-send-it) (setq mu4e-contexts (list ;; Old account (make-mu4e-context :name "Old" :match-func (lambda (msg) (when msg (string-prefix-p "/Gmail" (mu4e-message-field msg :maildir)))) :vars '((user-mail-address . "hunt.jonr@gmail.com") (user-full-name . "Jon Hunts Gmail") (smtpmail-smtp-server . "smtp.gmail.com") (smtpmail-smtp-service . 465) (smtpmail-stream-type . ssl) (mu4e-drafts-folder . "/Gmail/[Gmail]/Drafts") (mu4e-sent-folder . "/Gmail/[Gmail]/Sent Mail") (mu4e-refile-folder . "/Gmail/[Gmail]/All Mail") (mu4e-trash-folder . "/Gmail/[Gmail]/Trash"))) ;; Personal account (make-mu4e-context :name "Personal" :match-func (lambda (msg) (when msg (string-prefix-p "/Personal" (mu4e-message-field msg :maildir)))) :vars '((user-mail-address . "jon@jonhunt.uk") (user-full-name . "Jon Hunt") (smtpmail-smtp-server . "mail.jonhunt.uk") (smtpmail-smtp-service . 587) (smtpmail-stream-type . tls) (mu4e-drafts-folder . "/Personal/Drafts") (mu4e-sent-folder . "/Personal/Sent") (mu4e-refile-folder . "/Personal/Archive") (mu4e-trash-folder . "/Personal/Trash"))))) (setq mu4e-maildir-shortcuts '(("/Gmail/Inbox" . ?i) ("/Gmail/[Gmail]/Sent Mail" . ?s) ("/Gmail/[Gmail]/Trash" . ?t) ("/Gmail/[Gmail]/Drafts" . ?d) ("/Gmail/[Gmail]/All Mail" . ?a))) ;; open with first context by default (setq mu4e-context-policy 'pick-first) ;; Run mu4e in the background to sync mail periodically (mu4e t))
MPD and Mingus
Keyboard controls:
(exwm-input-set-key (kbd "<s-tab>") #'mpd-pause) (exwm-input-set-key (kbd "s-,") #'mpd-prev) (exwm-input-set-key (kbd "s-.") #'mpd-next) (exwm-input-set-key (kbd "s-9") #'mingus-vol-down) (exwm-input-set-key (kbd "s-0") #'mingus-vol-up) (exwm-input-set-key (kbd "s-n") #'mingus-seek) (exwm-input-set-key (kbd "s-p") #'mingus-seek-backward)
Mingus settings:
(setq-default mingus-mode-always-modeline t) (setq-default mingus-mode-line-separator " > ") (setq-default mingus-mode-line-string-max 60) (setq-default mingus-use-mouse-p nil)
Enable headers for mingus:
(require 'mingus) (require 'powerline) ;;;; Customization (defgroup mingus-header nil "Customizations for `mingus-header-mode'." :group 'mingus) (defcustom mingus-header-use-powerline t "Use `powerline' to create the header." :group 'mingus-header) ;;;; Header Line Creation (defun mingus-header-line-format () "Create a header line for `mingus-playlist-mode'." (if mingus-header-use-powerline (mingus-header-powerline-format) "not yet implemented")) ;;;;; Powerline version (defun mingus-header-powerline-format () "Create a header line for `mingus-playlist-mode' using the `powerline' package." (let* ((status (mpd-get-status mpd-inter-conn)) ;; Whether to show the current song's information. (show-song (member (plist-get status 'state) '(play pause))) ;; Powerline information. (active (powerline-selected-window-active)) (face1 (if active 'mode-line 'mode-line-inactive)) (face2 (if active 'powerline-active1 'powerline-inactive1)) (face3 (if active 'powerline-active2 'powerline-inactive2)) (face4 (if active 'mode-line-buffer-id 'mode-line-buffer-id-inactive)) (sep-left (intern (format "powerline-%s-%s" (powerline-current-separator) (car powerline-default-separator-dir)))) (sep-right (intern (format "powerline-%s-%s" (powerline-current-separator) (cdr powerline-default-separator-dir)))) ;; MPD state string. (state (case (plist-get status 'state) (play "playing ") (pause "paused ") (stop "stopped ") (t "error "))) ;; Volume string. (volume (if (= (plist-get status 'volume) 100) "Vol: 100%% " (format "Vol: %d%%%% " (plist-get status 'volume)))) ;; Playback string. (single (plist-get status 'single)) (consume (plist-get status 'consume)) (playback (format "%s%s%s%s- " (if (eq (plist-get status 'repeat) 1) "r" "-") (if (eq (plist-get status 'random) 1) "z" "-") (if (and single (string= single "1")) "s" "-") (if (and consume (string= consume "1")) "c" "-"))) ;; Elapsed time string (only when playing/paused). (time (if (not show-song) "" (format "%s/%s " (mingus-sec->min:sec (plist-get status 'time-elapsed)) (mingus-sec->min:sec (plist-get status 'time-total))))) (lhs (list (powerline-raw (propertize state 'face face4) face1 'l) (when show-song (funcall sep-left face1 face2)) (when show-song (powerline-raw time face2 'l)) (funcall sep-left face2 face3))) (rhs (list (funcall sep-right face3 face2) (powerline-raw volume face2 'l) (funcall sep-right face2 face1) (powerline-raw playback face1 'l)))) (concat (powerline-render lhs) (powerline-fill face3 (powerline-width rhs)) (powerline-render rhs)))) ;;;; Minor Mode Definition (define-minor-mode mingus-header-mode "A minor mode for displaying MPD information in the header line of `mingus-playlist-mode' buffers. Note that toggling this mode will fail in all other modes." :group 'mingus-header (if (not (eq major-mode 'mingus-playlist-mode)) (progn ;; Toggling this minor mode only works in `mingus-playlist-mode'. (setq mingus-header-mode (not mingus-header-mode)) (user-error "`mingus-header-mode' can only be enabled in `mingus-playlist-mode' buffers.")) (if (not mingus-header-mode) (setq header-line-format nil) (setq header-line-format '("%e" (:eval (mingus-header-line-format))))))) (provide 'mingus-header-mode)
Org mode
Make orgmode TODO list counters count beyond direct child:
(setq org-hierarchical-todo-statistics nil)
Define languages for org-babel
(org-babel-do-load-languages 'org-babel-load-languages '((emacs-lisp . t) (python . t))) (push '("conf-unix" . conf-unix) org-src-lang-modes) ;; stop it asking for confirmation upon evaluating code blocks (setq org-confirm-babel-evaluate nil)
Structure templates. Allow you to type <el
followed by tab
to start an elisp codeblock for example:
(require 'org-tempo) (add-to-list 'org-structure-template-alist '("sh" . "src shell")) (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp")) (add-to-list 'org-structure-template-alist '("py" . "src python")) (add-to-list 'org-structure-template-alist '("conf" . "src conf"))
PDF tools
Initialise pdf tools:
;;(pdf-tools-install)
Set zooming to be more fine-grained (10% instead of the default 25%):
(setq pdf-view-resize-factor 1.1)
Open PDFs scaled to fit page:
(setq-default pdf-view-display-size 'fit-page)
Website
I have made this website you are on with the org-static-blog extension for orgmode. The code below defines the structure and layout of the blog pages:
(setq org-static-blog-publish-title "Jon's website") (setq org-static-blog-publish-url "https://jonhunt.uk/") (setq org-static-blog-publish-directory "~/website/") (setq org-static-blog-posts-directory "~/website/posts/") (setq org-static-blog-drafts-directory "~/website/drafts/") (setq org-static-blog-use-preview t) (setq org-static-blog-preview-convert-titles t) (setq org-static-blog-preview-ellipsis "...") (setq org-static-blog-enable-tags t) (setq org-export-with-toc t) (setq org-export-with-section-numbers nil) (setq org-src-fontify-natively t org-src-tab-acts-natively t org-confirm-babel-evaluate nil org-edit-src-content-indentation 0)
This header is inserted into the <head> section of every page: (The stylesheet is located at ~/website/blog/static/style.css
and favicon at ~/website/blog/static/favicon.ico
):
(setq org-static-blog-page-header "<meta name=\"author\" content=\"Jon Hunt\"> <meta name=\"referrer\" content=\"no-referrer\"> <meta name=\"referrer\" content=\"no-referrer\"> <meta content=\"width=device-width\" initial-scale=\"1\" name=\"viewport\" /> <link href= \"static/style.css\" rel=\"stylesheet\" type=\"text/css\" /> <link rel=\"icon\" href=\"static/favicon.ico\">")
Below is the config for HTML to be inserted at the begining of the <body>
of every page. In my case I'm using it to insert the navigation header:
(setq org-static-blog-page-preamble "<header> <h1>Jonathan Hunt</a></h1> <span class=\"subtitle\">―― <a href=\"index\">jonhunt.uk</a> ――</span> <nav> <ul> <li><a href=\"index\">Home</a></li> <li><a href=\"https://jonhunt.uk/about-me\">About me</a></li> <li><a href=\"https://jonhunt.uk/resources\">Resources</a></li> <li><a href=\"https://jonhunt.uk/rss.xml\">RSS</a></li> <li><a href=\"https://jonhunt.uk/contact-and-support\">Contact and support</a></li> </ul> </nav> </header> ")
The code below defines what to be inserted at the end of the <body>
of every page. I've left mine blank but it could be used for a footer for example:
(setq org-static-blog-page-postamble "")
Non-emacs settings
It's also possible to edit configurations for non-emacs files within orgmode, using org-babel
. Below are areas I do this for:
MPD
MPD is a server-side application for playing music. I personally use it with the Mingus interface and I'm planning on setting up Polybar to interact with it also. The settings below are automatically exported to ~/.config/mpd/mpd.conf
:
# Required files db_file "~/.config/mpd/database" log_file "~/.config/mpd/log" # Optional music_directory "~/music" playlist_directory "~/.config/mpd/playlists" pid_file "~/.config/mpd/pid" state_file "~/.config/mpd/state" sticker_file "~/.config/mpd/sticker.sql" audio_output { type "pulse" name "mpd pulse-audio-output" } audio_output { type "fifo" name "my_fifo" path "/tmp/mpd.fifo" format "44100:16:2" } bind_to_address "127.0.0.1" port "6600"
MPV
I use MPV to watch videos. When combined with youtube-dl it's capable of streaming videos from websites also, and I regularly use it for that.
MPV keybindings, automatically exported to ~/.config/mpv/input.conf
:
n seek 5 p seek -5 l vf toggle hflip Alt+r no-osd cycle video-rotate 90
General MPV settings, automatically exported to ~/.config/mpv/mpv.conf
:
# Set audio to English alang=en # Prefers English subtitles slang=en # Turn subtitles off sid=no # Hopefully uses the "forced" subtitle track sub-forced-only # Use hardware decoding if possible hwdec=auto # Fancy filter good for reducing resolution (e.g. laptop) dscale=mitchell # Filter for increasing resolution scale=ewa_lanczos # Get rid of banding deband=yes volume=50 loop-file=inf profile=gpu-hq force-window=yes
Nyxt
Nyxt is a highly customisable web browser, similar to Qutebrowser (which is my primary browser), but has the advantage (in my opinion) of being Emacs-like, including being written in lisp. The only reason it's not my main web browser is that I'm still getting to grips with it.
My config rules are below, and exported to ~/.config/nyxt/init.lisp
Disable session restore on startup (currently commented out because I want session restore):
;(define-configuration browser ; ((session-restore-prompt :never-restore)))
Fuction to open videos in MPV:
(define-command play-video-in-current-page (&optional (buffer (current-buffer))) "Play video in the currently open buffer." (uiop:run-program (list "mpv" (object-string (url buffer)))))
Custom keybindings:
Styling configuration:
(define-configuration window ((message-buffer-style (str:concat (cl-css:css '((body :background-color "#1d2021" :color "#fbf1c7" :font-size "9pt" :margin "0px"))))))) (define-configuration internal-buffer ((style (str:concat (cl-css:css '((title :color "#CD5C5C") (body :background-color "#1d2021" :color "#fbf1c7") (hr :color "darkgray") (a :color "#83a598") (.button :color "#FFFFFF" :background-color "#504945")))))))
Search engine setup. Allows me to type "od test" to search the word test in Odysee, "w example" to search for the word example in Wikipedia, etc. Note that whatever is listed as the last search engine will be the default engine (in this case "searx"). For anyone wanting to use Nyxt it's also worth noting that more advanced search functionality (such as filtering results by date, region, language etc) is available through nx-search-engines but I don't personally use those features so I've not included it in my config:
(define-configuration buffer ((search-engines (list (make-instance 'search-engine :shortcut "4" :search-url "https://boards.4channel.org/~a/catalog" :fallback-url "https://boards.4channel.org/") (make-instance 'search-engine :shortcut "8" :search-url "https://8kun.top/~a/catalog.html" :fallback-url "https://8kun.top/") (make-instance 'search-engine :shortcut "a" :search-url "https://www.amazon.co.uk/s?=~a" :fallback-url "https://www.amazon.co.uk/") (make-instance 'search-engine :shortcut "bc" :search-url "https://www.bitchute.com/search/?query=~a" :fallback-url "https://www.bitchute.com/") (make-instance 'search-engine :shortcut "e" :search-url "https://www.ebay.co.uk/sch/~a" :fallback-url "https://www.ebay.co.uk/") (make-instance 'search-engine :shortcut "g" :search-url "https://www.google.co.uk/search?q=~a" :fallback-url "https://www.google.co.uk/") (make-instance 'search-engine :shortcut "i" :search-url "https://www.google.co.uk/search?tbm=isch&q=~a" :fallback-url "https://www.images.google.co.uk/") (make-instance 'search-engine :shortcut "imdb" :search-url "https://www.imdb.com/find?q=~a" :fallback-url "https://www.imdb.com/") (make-instance 'search-engine :shortcut "in" :search-url "https://yewtu.be/search?q=~a" :fallback-url "https://www.yewtu.be/") (make-instance 'search-engine :shortcut "ist" :search-url "https://www.instocktrades.com/search?term=~a" :fallback-url "https://www.instocktrades.com/") (make-instance 'search-engine :shortcut "jh" :search-url "https://jonhunt.uk/~a" :fallback-url "https://jonhunt.uk/") (make-instance 'search-engine :shortcut "oc" :search-url "http://classify.oclc.org/classify2/ClassifyDemo?search-title-txt=~a" :fallback-url "https://www.classify.oclc.org/") (make-instance 'search-engine :shortcut "od" :search-url "https://odysee.com/$/search?q=~a" :fallback-url "https://www.odysee.com/") (make-instance 'search-engine :shortcut "pb" :search-url "https://tpb.wtf/search.php?q=~a&cat=0" :fallback-url "https://tpb.wtf/") (make-instance 'search-engine :shortcut "pt" :search-url "https://search.joinpeertube.org/search?search=~a" :fallback-url "https://search.joinpeertube.org/") (make-instance 'search-engine :shortcut "r" :search-url "https://www.reddit.com/r/~a" :fallback-url "https://www.reddit.com/") (make-instance 'search-engine :shortcut "rbm" :search-url "https://rarbgto.org/torrents.php?search=~a&category%5B%5D=14&category%5B%5D=48&category%5B%5D=17&category%5B%5D=44&category%5B%5D=45&category%5B%5D=47&category%5B%5D=50&category%5B%5D=51&category%5B%5D=52&category%5B%5D=42&category%5B%5D=46&category%5B%5D=54" :fallback-url "https://www.rarbgto.org/") (make-instance 'search-engine :shortcut "rbt" :search-url "https://rarbgto.org/torrents.php?search=~a&category%5B%5D=18&category%5B%5D=41&category%5B%5D=49" :fallback-url "https://www.rarbgto.org/") (make-instance 'search-engine :shortcut "rc" :search-url "https://www.reedcomics.com/catalogsearch/result/?q=~a" :fallback-url "https://www.reedcomics.com/") (make-instance 'search-engine :shortcut "rt" :search-url "https://www.rottentomatoes.com/search?search=~a" :fallback-url "https://www.rottentomatoes.com/") (make-instance 'search-engine :shortcut "sh" :search-url "https://www.speedyhen.com/Search/Keyword?keyword=~a" :fallback-url "https://www.speedyhen.com/") (make-instance 'search-engine :shortcut "w" :search-url "https://en.wikipedia.org/wiki/Special:Search?search=~a" :fallback-url "https://www.en.wikipedia.org/") (make-instance 'search-engine :shortcut "wh" :search-url "https://www.wowhead.com/search?q=~a" :fallback-url "https://www.wowhead.com/") (make-instance 'search-engine :shortcut "wp" :search-url "https://wow.gamepedia.com/~a" :fallback-url "https://www.wow.gamepedia.com/") (make-instance 'search-engine :shortcut "yt" :search-url "https://www.youtube.com/results?search_query=~a" :fallback-url "https://www.youtube.com/") (make-instance 'search-engine :shortcut "sp" :search-url "https://searx.info/search?q=~a" :fallback-url "https://www.searx.info/")))))
Hide statusbar:
(define-configuration status-buffer ((height 0)))
Nx-freestance-handler: Redirect mainstream websites to their privacy-supporting mirrors (Youtube to Invidious, Reddit to Teddit, Instagram to Bibliogram, Medium to Scribe, Twitter to Nitter):
(defvar *my-request-resource-handlers* '()) (load-after-system :nx-freestance-handler (nyxt-init-file "freestance.lisp")) (define-configuration web-buffer ((request-resource-hook (reduce #'hooks:add-hook (mapcar #'make-handler-resource *my-request-resource-handlers*) :initial-value %slot-default%))))
;; to add all handlers/redirectors (setq *my-request-resource-handlers* (nconc *my-request-resource-handlers* nx-freestance-handler:*freestance-handlers*)) ;; alternatively, you may add each separately ;; (push #'nx-freestance-handler:invidious-handler *my-request-resource-handlers*) ;; (push #'nx-freestance-handler:nitter-handler *my-request-resource-handlers*) ;; (push #'nx-freestance-handler:bibliogram-handler *my-request-resource-handlers*) ;; (push #'nx-freestance-handler:teddit-handler *my-request-resource-handlers*) ;; (push #'nx-freestance-handler:scribe-handler *my-request-resource-handlers*) ;; to set your preferred instance, either invoke SET-PREFERRED-[name of website]-INSTANCE ;; command in Nyxt (the effect lasts until you close Nyxt), or write something like this: ;; (setf nx-freestance-handler:*preferred-invidious-instance* "https://invidious.snopyta.org")
Polybar
Note: this polybar configuration is still a work in progress, but once it's set up it will allow me to have things like time and date, volume, email count, CPU/GPU/RAM stats, music playing and much more to appear on a statusbar on the top of the screen.
These settings are automatically exported to ~/.config/polybar/config
:
[settings] screenchange-reload = true [global/wm] margin-top = 0 margin-bottom = 0 [colors] background = #f0232635 background-alt = #576075 foreground = #A6Accd foreground-alt = #555 primary = #ffb52a secondary = #e60053 alert = #bd2c40 underline-1 = #c792ea [bar/panel] width = 100% height = <<get-setting(name="polybar/height")>> offset-x = 0 offset-y = 0 fixed-center = true enable-ipc = true background = ${colors.background} foreground = ${colors.foreground} line-size = 2 line-color = #f00 border-size = 0 border-color = #00000000 padding-top = 5 padding-left = 1 padding-right = 1 module-margin = 1 font-0 = "Cantarell:size=<<get-setting(name="polybar/font-0-size")>>:weight=bold;2" font-1 = "Font Awesome:size=<<get-setting(name="polybar/font-1-size")>>;2" font-2 = "Material Icons:size=<<get-setting(name="polybar/font-2-size")>>;5" font-3 = "Fira Mono:size=<<get-setting(name="polybar/font-3-size")>>;-3" modules-left = exwm exwm-path modules-center = spotify modules-right = mu4e cpu temperature date tray-position = right tray-padding = 2 tray-maxsize = 28 cursor-click = pointer cursor-scroll = ns-resize [module/exwm] type = custom/ipc hook-0 = emacsclient -e "(dw/polybar-exwm-workspace)" | sed -e 's/^"//' -e 's/"$//' initial = 1 format-underline = ${colors.underline-1} format-background = ${colors.background-alt} format-padding = 1 [module/exwm-path] type = custom/ipc hook-0 = emacsclient -e "(dw/polybar-exwm-workspace-path)" | sed -e 's/^"//' -e 's/"$//' format-foreground = #f78c6c initial = 1 [module/spotify] type = custom/script exec = ~/.config/polybar/player-status.sh interval = 3 [module/mu4e] type = custom/ipc hook-0 = emacsclient -e '(dw/polybar-mail-count 500)' | sed -e 's/^"//' -e 's/"$//' initial = 1 format-underline = ${colors.underline-1} click-left = emacsclient -e '(dw/go-to-inbox)' [module/telegram] type = custom/ipc hook-0 = emacsclient -e '(dw/polybar-telegram-chats)' | sed -e 's/^"//' -e 's/"$//' format-padding = 3 initial = 1 [module/xkeyboard] type = internal/xkeyboard blacklist-0 = num lock format-prefix-font = 1 format-prefix-foreground = ${colors.foreground-alt} format-prefix-underline = ${colors.underline-1} label-layout = %layout% label-layout-underline = ${colors.underline-1} label-indicator-padding = 2 label-indicator-margin = 1 label-indicator-underline = ${colors.underline-1} [module/cpu] type = internal/cpu interval = 2 format = <label> <ramp-coreload> format-underline = ${colors.underline-1} click-left = emacsclient -e "(proced)" label = %percentage:2%% ramp-coreload-spacing = 0 ramp-coreload-0 = ▁ ramp-coreload-0-foreground = ${colors.foreground-alt} ramp-coreload-1 = ▂ ramp-coreload-2 = ▃ ramp-coreload-3 = ▄ ramp-coreload-4 = ▅ ramp-coreload-5 = ▆ ramp-coreload-6 = ▇ [module/memory] type = internal/memory interval = 2 format-prefix = "M:" format-prefix-foreground = ${colors.foreground-alt} format-underline = ${colors.underline-1} label = %percentage_used%% [module/date] type = internal/date interval = 5 date = "W%U: %a %b %e" date-alt = "%A %B %d %Y" time = %l:%M %p time-alt = %H:%M:%S format-prefix-foreground = ${colors.foreground-alt} format-underline = ${colors.underline-1} label = %date% %time% [module/battery] type = internal/battery battery = BAT0 adapter = ADP1 full-at = 98 time-format = %-l:%M label-charging = %percentage%% / %time% format-charging = <animation-charging> <label-charging> format-charging-underline = ${colors.underline-1} label-discharging = %percentage%% / %time% format-discharging = <ramp-capacity> <label-discharging> format-discharging-underline = ${self.format-charging-underline} format-full = <ramp-capacity> <label-full> format-full-underline = ${self.format-charging-underline} ramp-capacity-0 = ramp-capacity-1 = ramp-capacity-2 = ramp-capacity-3 = ramp-capacity-4 = animation-charging-0 = animation-charging-1 = animation-charging-2 = animation-charging-3 = animation-charging-4 = animation-charging-framerate = 750 [module/temperature] type = internal/temperature thermal-zone = 0 warn-temperature = 60 format = <label> format-underline = ${colors.underline-1} format-warn = <label-warn> format-warn-underline = ${self.format-underline} label = %temperature-c% label-warn = %temperature-c%! label-warn-foreground = ${colors.secondary}
Qutebrowser
Qutebrowser is one of the web browsers I use, and all the below settings are automatically exported to ~/.config/qutebrowser/config.py
Style default background colour of websites, and use an external stylesheet for anything else:
config.load_autoconfig(False) # config.set('colors.webpage.prefers_color_scheme_dark', True) - This setting no longer works and there doesn't seem to be a working replacement currently config.set('colors.webpage.bg', '#1d2021') c.content.user_stylesheets = str(config.configdir) + '/stylesheets/gruvbox-all-sites.css'
Interface colours, taken from the Gruvbox-dark-hard config of Base16 Qutebrowser:
base00 = "#1d2021" base01 = "#3c3836" base02 = "#504945" base03 = "#665c54" base04 = "#bdae93" base05 = "#d5c4a1" base06 = "#ebdbb2" base07 = "#fbf1c7" base08 = "#fb4934" base09 = "#fe8019" base0A = "#fabd2f" base0B = "#b8bb26" base0C = "#8ec07c" base0D = "#83a598" base0E = "#d3869b" base0F = "#d65d0e" # set qutebrowser colors c.colors.completion.fg = base05 c.colors.completion.odd.bg = base01 c.colors.completion.even.bg = base00 c.colors.completion.category.fg = base0A c.colors.completion.category.bg = base00 c.colors.completion.category.border.top = base00 c.colors.completion.category.border.bottom = base00 c.colors.completion.item.selected.fg = base05 c.colors.completion.item.selected.bg = base02 c.colors.completion.item.selected.border.top = base02 c.colors.completion.item.selected.border.bottom = base02 c.colors.completion.item.selected.match.fg = base0B c.colors.completion.match.fg = base0B c.colors.completion.scrollbar.fg = base05 c.colors.completion.scrollbar.bg = base00 c.colors.contextmenu.disabled.bg = base01 c.colors.contextmenu.disabled.fg = base04 c.colors.contextmenu.menu.bg = base00 c.colors.contextmenu.menu.fg = base05 c.colors.contextmenu.selected.bg = base02 c.colors.contextmenu.selected.fg = base05 c.colors.downloads.bar.bg = base00 c.colors.downloads.start.fg = base00 c.colors.downloads.start.bg = base0D c.colors.downloads.stop.fg = base00 c.colors.downloads.stop.bg = base0C c.colors.downloads.error.fg = base08 c.colors.hints.fg = base00 c.colors.hints.bg = base0A c.colors.hints.match.fg = base05 c.colors.keyhint.fg = base05 c.colors.keyhint.suffix.fg = base05 c.colors.keyhint.bg = base00 c.colors.messages.error.fg = base00 c.colors.messages.error.bg = base08 c.colors.messages.error.border = base08 c.colors.messages.warning.fg = base00 c.colors.messages.warning.bg = base0E c.colors.messages.warning.border = base0E c.colors.messages.info.fg = base05 c.colors.messages.info.bg = base00 c.colors.messages.info.border = base00 c.colors.prompts.fg = base05 c.colors.prompts.border = base00 c.colors.prompts.bg = base00 c.colors.prompts.selected.bg = base02 c.colors.statusbar.normal.fg = base0B c.colors.statusbar.normal.bg = base00 c.colors.statusbar.insert.fg = base00 c.colors.statusbar.insert.bg = base0D c.colors.statusbar.passthrough.fg = base00 c.colors.statusbar.passthrough.bg = base0C c.colors.statusbar.private.fg = base00 c.colors.statusbar.private.bg = base01 c.colors.statusbar.command.fg = base05 c.colors.statusbar.command.bg = base00 c.colors.statusbar.command.private.fg = base05 c.colors.statusbar.command.private.bg = base00 c.colors.statusbar.caret.fg = base00 c.colors.statusbar.caret.bg = base0E c.colors.statusbar.caret.selection.fg = base00 c.colors.statusbar.caret.selection.bg = base0D c.colors.statusbar.progress.bg = base0D c.colors.statusbar.url.fg = base05 c.colors.statusbar.url.error.fg = base08 c.colors.statusbar.url.hover.fg = base05 c.colors.statusbar.url.success.http.fg = base0C c.colors.statusbar.url.success.https.fg = base0B c.colors.statusbar.url.warn.fg = base0E c.colors.tabs.bar.bg = base00 c.colors.tabs.indicator.start = base0D c.colors.tabs.indicator.stop = base0C c.colors.tabs.indicator.error = base08 c.colors.tabs.odd.fg = base05 c.colors.tabs.odd.bg = base01 c.colors.tabs.even.fg = base05 c.colors.tabs.even.bg = base00 c.colors.tabs.pinned.even.bg = base0C c.colors.tabs.pinned.even.fg = base07 c.colors.tabs.pinned.odd.bg = base0B c.colors.tabs.pinned.odd.fg = base07 c.colors.tabs.pinned.selected.even.bg = base02 c.colors.tabs.pinned.selected.even.fg = base05 c.colors.tabs.pinned.selected.odd.bg = base02 c.colors.tabs.pinned.selected.odd.fg = base05 c.colors.tabs.selected.odd.fg = base05 c.colors.tabs.selected.odd.bg = base02 c.colors.tabs.selected.even.fg = base05 c.colors.tabs.selected.even.bg = base02
Font settings:
c.fonts.default_family = [] c.fonts.default_size = '10pt' c.fonts.completion.entry = '8pt default_family' c.fonts.completion.category = 'bold 8pt default_family' c.fonts.debug_console = '8pt default_family' c.fonts.downloads = '8pt default_family' c.fonts.hints = 'bold 8pt default_family' c.fonts.keyhint = '8pt default_family' c.fonts.messages.error = '8pt default_family' c.fonts.messages.info = '8pt default_family' c.fonts.messages.warning = '8pt default_family' c.fonts.prompts = '8pt sans-serif' c.fonts.statusbar = '8pt default_family' c.fonts.tabs.selected = '8pt default_family' c.fonts.tabs.unselected = '8pt default_family' c.fonts.web.family.cursive = None
Tab appearance:
c.tabs.favicons.scale = 1.0 c.tabs.favicons.show = 'never' c.tabs.indicator.width = 0 c.tabs.min_width = -1 c.tabs.max_width = -1 c.tabs.padding = {'bottom': 2, 'left': 5, 'right': 5, 'top': 2} c.tabs.pinned.shrink = True c.tabs.position = 'top' c.tabs.show = 'multiple' c.tabs.show_switching_delay = 800 c.tabs.title.alignment = 'left' c.tabs.title.format = '{audio}{current_title}' c.tabs.title.format_pinned = '{index}' c.tabs.width = '20%' c.hints.border = '0px' c.window.hide_decoration = False c.window.title_format = '{perc}{current_title}{title_sep}qutebrowser'
Miscellaneous appearance settings:
c.completion.height = '30%' c.completion.shrink = True c.completion.scrollbar.width = 0 c.completion.timestamp_format = '%d/%m/%Y'
Aliases for commands:
c.aliases = {'q': 'close', 'qa': 'quit', 'w': 'session-save', 'wq': 'quit --save', 'wqa': 'quit --save'}
Keybindings. I'm intending to move to Nyxt at some point in the future so I've tried to make the keybindings close to Nyxt's default bindings:
# Unbind default keysbindings config.unbind('[[') config.unbind('d') config.unbind('f') config.unbind('F') config.unbind('J') config.unbind('K') config.unbind('M') config.unbind('n') config.unbind('o') config.unbind('O') config.unbind('pp') config.unbind('pP') config.unbind('R') config.unbind('r') config.unbind('u') config.unbind('v') config.unbind(':') config.unbind('<F11>') # Bind new keybindings config.bind('b', 'set-cmd-text -s :buffer') config.bind('H', 'home') config.bind('<Ctrl+0>', 'zoom') config.bind('<Ctrl+[>', 'tab-prev') config.bind('<Ctrl+]>', 'tab-next') config.bind('<Ctrl+b>', 'set-cmd-text -s :bookmark-load') config.bind('<Ctrl+g>', 'hint') config.bind('<Ctrl+l>', 'set-cmd-text -s :open') config.bind('<Ctrl+n>', 'scroll down') config.bind('<Ctrl+p>', 'scroll up') config.bind('<Ctrl+r>', 'reload') config.bind('<Ctrl+s>', 'set-cmd-text /') config.bind('<Ctrl+w>', 'tab-close') config.bind('<Ctrl+Shift+w>', 'close') config.bind('<Ctrl+/>', 'undo') config.bind('<Ctrl+space>', 'set-cmd-text :') config.bind('<Ctrl+Alt+c>', 'devtools') config.bind('<Ctrl+Shift+Tab>', 'tab-prev') config.bind('<Ctrl+Up>', 'scroll-to-perc 0') config.bind('<Ctrl+Down>', 'scroll-to-perc 100') config.bind('<Alt+g>', 'hint all tab') config.bind('<Alt+l>', 'set-cmd-text -s :open -t') config.bind('<Alt+left>', 'back') config.bind('<Alt+right>', 'forward') config.bind('<Alt+[', 'back') config.bind('<Alt+]', 'forward') config.bind('v', 'view-source') config.bind ('<', 'set content.user_stylesheets ""') config.bind ('>', 'set content.user_stylesheets /home/Jon/.config/qutebrowser/stylesheets/gruvbox-all-sites.css') config.bind('c-y', 'rl-yank') config.bind('<Alt+v>', 'scroll-page -1 -1') config.bind('<Ctrl+v>', 'scroll-page 1 1')
Keybindings to download images and videos from a webpage to specific folders:
config.bind(',4a', 'spawn wget -P downloads/wget/anime-and-manga -nd -r -l 1 -H -D i.4cdn.org -A png,gif,jpg,jpeg,webm -R "*s.*" {url:pretty}') config.bind(',4c', 'spawn wget -P downloads/wget/comics-and-cartoons -nd -r -l 1 -H -D i.4cdn.org -A png,gif,jpg,jpeg,webm -R "*s.*" {url:pretty}') config.bind(',4g', 'spawn wget -P downloads/wget/technology -nd -r -l 1 -H -D i.4cdn.org -A png,gif,jpg,jpeg,webm -R "*s.*" {url:pretty}') config.bind(',4i', 'spawn wget -P downloads/wget/international -nd -r -l 1 -H -D i.4cdn.org -A png,gif,jpg,jpeg,webm -R "*s.*" {url:pretty}') config.bind(',4p', 'spawn wget -P downloads/wget/politics -nd -r -l 1 -H -D i.4cdn.org -A png,gif,jpg,jpeg,webm -R "*s.*" {url:pretty}') config.bind(',4t', 'spawn wget -P downloads/wget/television-and-film -nd -r -l 1 -H -D i.4cdn.org -A png,gif,jpg,jpeg,webm -R "*s.*" {url:pretty}') config.bind(',4v', 'spawn wget -P downloads/wget/videogames -nd -r -l 1 -H -D i.4cdn.org -A png,gif,jpg,jpeg,webm -R "*s.*" {url:pretty}') config.bind(',4w', 'spawn wget -P downloads/wget/wallpapers -nd -r -l 1 -H -D i.4cdn.org -A png,gif,jpg,jpeg,webm -R "*s.*" {url:pretty}') config.bind(',w', 'spawn wget -P Downloads/wget -nd -r -l 1 -H -D i.4cdn.org -A png,gif,jpg,jpeg,webm -R "*s.*" {url:pretty}') config.bind(',W', 'hint links spawn wget -P Downloads/wget -nd -r -l 1 -H -D i.4cdn.org -A png,gif,jpg,jpeg,webm -R "*s.*" {hint-url}') config.bind(',y', 'spawn youtube-dl --o "downloads/youtube-dl/%(title)s.%(ext)s" --add-metadata {url:pretty}') config.bind(',Y', 'hint links spawn youtube-dl -o "downloads/youtube-dl/%(title)s.%(ext)s" --add-metadata {hint-url}')
Keybindings to open images and videos from a webpage in external programs:
config.bind(',i', 'spawn feh -F {url:pretty}') config.bind(',I', 'hint links spawn feh -F {hint-url}') config.bind(',m', 'spawn mpv {url}') config.bind(',M', 'hint links spawn mpv {hint-url}')
Custom search engines shortcuts. Allows od test
to search for the word 'test' within Odysee, or rt test
to search for the word 'test' within Rotten Tomatoes for example:
c.url.searchengines = { '4': 'https://boards.4channel.org/{}/catalog', '8': 'https://8kun.top/{}/catalog.html', 'a': 'https://www.amazon.co.uk/s?k={}', 'bc': 'https://www.bitchute.com/search/?query={}', 'DEFAULT': 'https://searx.info/?q={}', 'e': 'https://www.ebay.co.uk/sch/{}', 'g': 'https://www.google.co.uk/search?q={}', 'i': 'https://www.google.co.uk/search?tbm=isch&q={}', 'imdb': 'https://www.imdb.com/find?q={}', 'in': 'https://yewtu.be/search?q={}', 'ist': 'https://www.instocktrades.com/search?term={}', 'jh': 'https://jonhunt.uk/{}', 'oc': 'http://classify.oclc.org/classify2/ClassifyDemo?search-title-txt={}', 'od': 'https://odysee.com/$/search?q={}', 'pt': 'https://search.joinpeertube.org/search?search={}', 'r': 'https://www.reddit.com/r/{}', 'rc': 'https://www.reedcomics.com/catalogsearch/result/?q={}', 'rt': 'https://www.rottentomatoes.com/search?search={}', 'sh': 'https://www.speedyhen.com/Search/Keyword?keyword={}', 's': 'https://searx.info/search?q={}', 'w': 'https://en.wikipedia.org/wiki/Special:Search?search={}', 'wh': 'https://www.wowhead.com/search?q={}', 'wp': 'https://wow.gamepedia.com/{}', 'yt': 'https://www.youtube.com/results?search_query={}'}
Miscellaneous settings:
c.auto_save.session = False c.backend = 'webengine' c.confirm_quit = ['always'] c.content.autoplay = False c.content.cookies.store = False c.content.geolocation = False c.content.headers.accept_language = 'en-GB,en' c.content.headers.custom = {} c.content.blocking.enabled = True c.content.javascript.can_access_clipboard = False c.completion.web_history.exclude = [] c.completion.web_history.max_items = 0 c.hints.auto_follow_timeout = 0 c.qt.args = ['ppapi-widevine-path=/usr/lib/qt/plugins/ppapi/libwidevinecdmadapter.so'] c.tabs.background = True c.tabs.close_mouse_button = 'middle' c.tabs.close_mouse_button_on_bar = 'new-tab' c.tabs.last_close = 'blank' c.tabs.mousewheel_switching = True c.tabs.new_position.related = 'next' c.tabs.new_position.stacking = True c.tabs.mode_on_change = 'normal' c.tabs.undo_stack_size = 100 c.tabs.wrap = True c.url.default_page = 'about:blank' c.url.incdec_segments = ['path', 'query'] c.url.open_base_url = False c.url.start_pages = 'https://jonhunt.uk/' c.url.yank_ignored_parameters = ['ref', 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'] config.set('content.images', True, 'chrome-devtools://*') config.set('content.images', True, 'devtools://*') config.set('content.javascript.enabled', True, 'chrome-devtools://*') config.set('content.javascript.enabled', True, 'devtools://*') config.set('content.javascript.enabled', True, 'chrome://*/*') config.set('content.javascript.enabled', True, 'qute://*/*')
Startup
Now everything is set up, run emacs with elfeed, mingus, mentor and a terminal:
(setq inhibit-startup-screen t) (delete-other-windows) (exwm-workspace-switch-create 2) (lmedia) (exwm-workspace-switch-create 1) (start-process-shell-command "nyxt" nil "nyxt")
End of config
Credits
As free software, most of what you see above has been gathered and modified for my own needs from various talented programmers and tinkerers, so I feel I owe it to them to give them credit. It would be near-impossible to list every single contributor here, but here are some of the more significant ones to my own PC use:
Emacs as a whole
- John McCarthy: Developed the Lisp programming language family, from which Emacs is built.
- David A. Moon: Co-created Emacs with Guy Steele
- Richard Stallman: Created GNU Emacs (which is the main Emacs used today)
- Guy L. Steele Jr: Co-created Emacs with David Moon
Emacs packages I use or intend to use in the future
- Bastian Bechtold: Created
org-static-blog
- Alexander Belikoff: Created
erc
alongside Sergey Berezin - Sergey Berezin: Created
erc
alongside Alexander Belikoff - Dirk-Jan Binnema: Created
mu
andmu4e
- Carsten Dominik: Created
orgmode
- Chris Feng: Created
exwm
- Niels Giesen: Created
mingus
- Remy Honig: Created
elfeed-org
- Aaron Jacobs: Created
mingus-header-mode
- jtbm37: Created
all-the-icons-dired
- Stefan Kangas: Created
mentor
- Ohel Krehel: Created
ivy
,counsel
, andswiper
- Sebastian Kremer: Created
dired
anddired-x
- lujun9972: Created
dmenu
- Eric Schulte: Created
org-babel
- Markus Triska: Created
openwith
- Politza: Created
pdf-tools
- Marius Vollmer: Created
magit
- Christopher Wellons: Created
elfeed
Non-emacs
- Florian Bruhin: Created
qutebrowser
- Daniel Friesel: Created
feh
alongside Tom Gilbert - Ricardo Garcia: Created
youtube-dl
- Árpád Gereöffy: Created
mplayer
, whichmpv
is a fork of - Tom Gilbert: Created
feh
alongside Daniel Friesel - Max Kellermann: Created
mpd
- Levente1 Polyak: Is the current project lead for
archlinux
and helped to create it - Hong Jen Yee: Also known as "PCMan", he created
pcmanfm
People I learned from
- Musa Al-hassy: Introduced me to
org-static-blog
from reading his blogpost on the subject. - David Eckert: Better known as 'Uncle Dave', his video tutorials and Emacs config taught me how to use Emacs when I first started.
- Derek Taylor: Better known as 'DistroTube', his videos helped spark my interest in Emacs.
- Thoughtbot: Their presentations helped to spark my interest in Emacs, particularly the talks from Jay Dixit, Perry Metzger, and Harry Schwartz.
- David Wilson: Better known as 'System Crafters', I learned how to set up
mu4e
andorg-babel-tangle
from his video tutorials. - Mike Zamansky: I was introduced to
elfeed
andelfeed-org
from his video channel.
If there's others you think I should add, or you find I've miscredited someone, please let me know.