#+Title: Emacs Customizations #+Author: Ricardo Wurmus #+PROPERTY: noweb tangle #+PROPERTY: mkdirp t #+OPTIONS: tasks:nil toc:1 * Introduction My Emacs configuration is a mess. As I’m writing this my Emacs configuration stretches across multiple files, each containing various snippets of code that seemed like a good idea to group. Unfortunately, there are a some things that don’t have a “natural” home. Enabling the same minor mode in various major modes is one of these cases—do I duplicate the hook and place it in a file for each major mode? Or do I write a new file for the minor mode in which I add it to the major modes at once? With multiple files I spend too much time trying to find the best place for any bit of configuration I add. This slows me down and sometimes I just append to the main =init.el=, so I often feel that my configuration is in need of reorganisation. But configuring Emacs should be fun! I don’t want it to create an uncomfortable clean-up task as a side-effect. This is why I’m now trying to use a literate approach with =org-mode=. My Emacs configuration should be prose first and code second. In my experience, finding the right spot in prose for a new paragraph requires a lot less effort as the text itself acts as a connection between unrelated bits of code. * How to use this? :PROPERTIES: :header-args: :noweb-ref compile-init :END: We take all code blocks in this file and assemble an =init.el= from it if the source file =init.org= is younger. At startup time we check if the =init.el= has to be regenerated. #+BEGIN_SRC elisp (let ((orgfile (expand-file-name (concat user-emacs-directory "init.org"))) (target (expand-file-name (concat user-emacs-directory "init.el")))) (when (not (file-newer-than-file-p target orgfile)) (progn (require 'org) (org-babel-tangle-file orgfile) (byte-compile-file target) (load target)))) #+END_SRC * Initialise packages :PROPERTIES: :header-args: :noweb-ref packages :END: Emacs is an operating system and I use it as such (see [[http://elephly.net/posts/2016-02-14-ilovefs-emacs.html][this blog post]]). I rely on quite a few extensions that have been made available on various ELPA repositories. Recently, I have moved to installing and managing Emacs packages like any other software package on my system with the functional package manager [[https://gnu.org/s/guix][GNU Guix]]. I find this more reliable, although at first it is slightly less convenient as I can no longer just use =package.el= but first need to package the Elisp code for Guix. To install all packages via GNU Guix I can either use a manifest file or use the following invocation: #+BEGIN_SRC sh :noweb-ref nil guix package -i \ emacs \ emacs-auctex \ emacs-better-defaults \ emacs-clojure-mode \ emacs-company \ emacs-debbugs \ emacs-emms \ emacs-emms-mode-line-cycle \ emacs-emms-player-mpv \ emacs-ess \ emacs-expand-region \ emacs-fill-column-indicator \ emacs-god-mode \ emacs-ido-ubiquitous \ emacs-js2-mode \ emacs-lispy \ emacs-markdown-mode \ emacs-mmm-mode \ emacs-multiple-cursors \ emacs-org \ emacs-org-bullets \ emacs-page-break-lines \ emacs-paren-face \ emacs-pdf-tools \ emacs-perspective \ emacs-projectile \ emacs-rich-minority \ emacs-shell-switcher \ emacs-solarized-theme \ emacs-skewer-mode \ emacs-smart-mode-line \ emacs-smex \ emacs-tagedit \ emacs-typo \ emacs-undo-tree \ emacs-web-mode \ emacs-wget \ emacs-zenburn-theme \ emacs-znc \ emacs-paredit \ emacs-geiser \ emacs-magit \ emacs-sicp \ mu #+END_SRC There are some packages I use that are not yet packaged for Guix. Here’s a list of them: #+BEGIN_SRC elisp (defvar my/packages '(centered-cursor-mode guide-key ido-vertical-mode scss-mode)) #+END_SRC I want these packages to be installed automatically. First I need to define in what repositories Emacs should look for the packages. I’m using packages from both the “melpa” and “marmalade” repositories. #+BEGIN_SRC elisp (require 'package) (add-to-list 'package-archives '("melpa" . "http://melpa.milkbox.net/packages/")) (add-to-list 'package-archives '("marmalade" . "http://marmalade-repo.org/packages/")) (package-initialize) #+END_SRC If this is a fresh Emacs installation melpa needs to be initialised first: #+BEGIN_SRC elisp (unless (file-exists-p "~/.emacs.d/elpa/archives/melpa") (package-refresh-contents)) #+END_SRC Now we are ready to install packages if they aren’t yet installed. #+BEGIN_SRC elisp (defun packages-install (packages) (mapc (lambda (package) (when (not (package-installed-p package)) (package-install package))) packages) (delete-other-windows)) (defun init--install-packages () (packages-install my/packages)) #+END_SRC Install packages as soon as this configuration is evaluated. If there’s an error (e.g. because a package by this name cannot be found) ask Emacs to refresh the list of packages and retry. If there’s an error again we just ignore it. It could be that it’s because there’s no Internet connection. #+BEGIN_SRC elisp (condition-case e (init--install-packages) (error (ignore-errors (package-refresh-contents) (init--install-packages)))) #+END_SRC * Default fonts :PROPERTIES: :header-args: :noweb-ref default-fonts :END: I like pretty faces. For coding I like to use the DejaVu Sans Mono font. In =org-mode= and in =eww= I like to use a font with variable pitch instead of the default mono-spaced font. I find Linux Biolinum pretty, especially when it’s rendered large. #+BEGIN_SRC elisp (set-frame-font "DejaVu Sans Mono") (set-face-attribute 'variable-pitch nil :height 1.25 :family "Linux Biolinum") #+END_SRC * Manuals :PROPERTIES: :header-args: :noweb-ref manuals :END: Also in Info manuals I want to use variable-pitch fonts where possible. Unfortunately, Info manuals don’t contain enough semantic markup, so I cannot selectively use a monospace font for examples or inline code and use a variable pitch font for everything else. So I just use variable pitch in headings. #+BEGIN_SRC elisp (require 'info) (set-face-attribute 'info-title-1 nil :inherit 'variable-pitch :height 1.3) (set-face-attribute 'info-title-2 nil :inherit 'variable-pitch :height 1.3) (set-face-attribute 'info-title-3 nil :inherit 'variable-pitch :height 1.3) (set-face-attribute 'info-menu-header nil :inherit 'variable-pitch :height 1.1) #+END_SRC Since Emacs 25 there is a new face for quoted expressions in Info manuals. By default it uses the “courier” font, which looks terrible. #+BEGIN_SRC elisp (set-face-attribute 'Info-quoted nil :inherit 'fixed-pitch :family "Monospace") #+END_SRC * Org-mode :PROPERTIES: :header-args: :noweb-ref org-mode :END: This is my org mode configuration. Document it. #+BEGIN_SRC elisp (require 'org-indent) (setq org-ellipsis "⤵") (setq org-src-fontify-natively t) (global-set-key (kbd "C-c o l") 'org-store-link) (global-set-key (kbd "C-c o a") 'org-agenda) ;; TODO: make these available in org-mode only (global-set-key (kbd "C-c o s") 'org-schedule) (setq org-log-done t) (setq org-return-follows-link t) (setq org-directory "~/Documents/org") (setq org-agenda-files (mapcar (lambda (x) (concat org-directory x)) (list "/master.org" "/work.org" "/email.org" "/study.org" "/home.org"))) (setq org-default-notes-file (concat org-directory "/notes.org")) (setq org-agenda-custom-commands '(("w" todo "WAITING" nil) ("n" todo "NEXT" nil) ("d" "Agenda + Next Actions" ((agenda) (todo "NEXT"))))) (setq org-fontify-done-headline t) (require 'org-bullets) (setq org-bullets-bullet-list '("◉" "○" "◇" "◇")) (add-hook 'org-mode-hook (lambda () (org-bullets-mode 1) (variable-pitch-mode 1) (visual-line-mode 1))) (defun gtd () (interactive) (find-file (concat org-directory "/master.org"))) #+END_SRC I don’t like the way org-mode looks by default. It’s noisy, too colourful and seems old-fashioned to the point of being somewhat unattractive. This is why I find it important to change some of the default faces. Since I enable =variable-pitch-mode= in my org-mode buffers I also need to explicitly make a few faces inherit from the =fixed-width= face to be rendered with a monospaced font. #+BEGIN_SRC elisp (set-face-attribute 'org-done nil :strike-through t) (set-face-attribute 'org-headline-done nil :strike-through t :foreground "light gray") (set-face-attribute 'org-document-title nil :height 1.6 :foreground "#073642") (set-face-attribute 'org-level-1 nil :height 1.0 :foreground "#586e75") (set-face-attribute 'org-level-2 nil :height 0.9 :foreground (face-attribute 'default :foreground)) (set-face-attribute 'org-level-3 nil :height 0.8 :foreground (face-attribute 'default :foreground)) (set-face-attribute 'org-level-4 nil :height 0.8 :foreground (face-attribute 'default :foreground)) (set-face-attribute 'org-block nil :height 0.8 :foreground (face-attribute 'default :foreground) :background "#efe9d6") (set-face-attribute 'org-verbatim nil :background "#efe9d6") (dolist (face '(org-meta-line org-document-info-keyword org-special-keyword)) (set-face-attribute face nil :foreground "#93a1a1" :weight 'normal)) (dolist (face '(org-block-begin-line org-block-end-line org-block org-table org-meta-line org-document-info-keyword org-special-keyword org-verbatim org-todo org-tag org-done org-hide org-indent org-checkbox)) (set-face-attribute face nil :inherit 'fixed-pitch) ;; TODO: this is ugly. When scaling up the variable-pitch face the ;; fixed-pitch face will become even larger. (set-face-attribute face nil :height 0.8)) (set-face-attribute 'org-tag nil :foreground (face-attribute 'default :foreground) :weight 'normal :height (face-attribute 'default :height) :overline nil :underline nil :box '(:line-width -1 :color "#859900")) #+END_SRC To ensure that indented blocks line up with their headings despite using =variable-pitch-mode= we set the indentation character to =*= and hide it by setting the foreground colour to the same as the default background colour. #+BEGIN_SRC elisp (setq org-indent-boundary-char ?*) (set-face-attribute 'org-indent nil :foreground (face-attribute 'default :background)) #+END_SRC =variable-pitch-mode= also makes it impossible to align tags at a fixed column, so I don’t. Instead I just let tags appear right behind the heading. #+BEGIN_SRC elisp (setq org-tags-column 0) #+END_SRC The following snippet is an attempt to prettify the somewhat ugly headers of source code blocks in =org-mode=. The snippet was taken from [[https://pank.eu/blog/pretty-babel-src-blocks.html][the blog of Rasmus Pank]] and slightly modified to suit my needs. #+BEGIN_SRC elisp (defvar-local my/org-at-src-begin -1 "Variable that holds whether last position was an org source code block.") (defvar-local my/org-src-begin-regexp "^[ \t]*#\\+begin_src[ \t]+[^ \f\t\n\r\v]+[ \t]*") (defvar my/ob-header-symbol ?☰ "Symbol used for babel headers") (defun my/org-prettify-src--update () (let ((case-fold-search t) (re my/org-src-begin-regexp) found) (save-excursion (goto-char (point-min)) (while (re-search-forward re nil t) (goto-char (match-end 0)) (let ((args (org-trim (buffer-substring-no-properties (point) (line-end-position))))) (when (org-string-nw-p args) (let ((new-cell (cons args my/ob-header-symbol))) (cl-pushnew new-cell prettify-symbols-alist :test #'equal) (cl-pushnew new-cell found :test #'equal))))) (setq prettify-symbols-alist (cl-set-difference prettify-symbols-alist (cl-set-difference (cl-remove-if-not (lambda (elm) (eq (cdr elm) my/ob-header-symbol)) prettify-symbols-alist) found :test #'equal))) ;; Clean up old font-lock-keywords. (font-lock-remove-keywords nil prettify-symbols--keywords) (setq prettify-symbols--keywords (prettify-symbols--make-keywords)) (font-lock-add-keywords nil prettify-symbols--keywords) (while (re-search-forward re nil t) (font-lock-flush (line-beginning-position) (line-end-position))) ;; Toggle prettify-symbols-mode to restore composition of ;; regions on which the "composition" text-property was deleted. (prettify-symbols-mode -1) (prettify-symbols-mode +1)))) (defun my/org-prettify-src () "Hide src options via `prettify-symbols-mode'. `prettify-symbols-mode' is used because expanding is simple. It’s not very efficient and maybe should be implemented using overlays." (let* ((case-fold-search t) (at-src-block (save-excursion (beginning-of-line) (looking-at my/org-src-begin-regexp)))) ;; Test if we moved out of a block. (cond ((or (and my/org-at-src-begin (not at-src-block)) ;; File was just opened. (eq my/org-at-src-begin -1)) (my/org-prettify-src--update)) ;; Remove composition if at line (at-src-block (with-silent-modifications (remove-text-properties (match-end 0) (1+ (line-end-position)) '(composition))))) (setq my/org-at-src-begin at-src-block))) (defun my/org-prettify-symbols () (mapc (apply-partially 'add-to-list 'prettify-symbols-alist) (cl-reduce 'append (mapcar (lambda (x) (list x (cons (upcase (car x)) (cdr x)))) `(("#+begin_src" . ?✎) ;; ➤ 🖝 ➟ ➤ ✎ ("#+end_src" . ?□) ;; ⏹ ("#+header:" . ,my/ob-header-symbol) ("#+begin_quote" . ?«) ("#+end_quote" . ?»))))) (turn-on-prettify-symbols-mode) (add-hook 'post-command-hook 'my/org-prettify-src t t)) (add-hook 'org-mode-hook #'my/org-prettify-symbols) #+END_SRC I use the capture feature to quickly record ideas and tasks, and to create links to emails that I need to work on. This is much better than just leaving these emails in my Inbox. #+BEGIN_SRC elisp (global-set-key (kbd "C-c o c") 'org-capture) (setq org-capture-templates '(("t" "Task" entry (file+headline (concat org-directory "/home.org") "Tasks") "* %?\n %i\n %a") ("m" "Email" entry (file+headline (concat org-directory "/email.org") "Email") "* Reply to %:fromname%? :email:\n [%:date]\n To: %:to\n %a"))) #+END_SRC For exporting org documents to PDF I use =lualatex= instead of the default =pdflatex=. I also use =biber= to refresh the bibliography. This requires me to change the value of =org-latex-pdf-process=. #+BEGIN_SRC elisp (setq org-latex-pdf-process '("lualatex -interaction nonstopmode -output-directory %o %f" "biber %b" "lualatex -interaction nonstopmode -output-directory %o %f" "lualatex -interaction nonstopmode -output-directory %o %f")) #+END_SRC Org mode is an excellent environment for literate programming through Babel. Here I configure a couple of common languages to be used with Babel. #+BEGIN_SRC elisp (require 'ob-sh) (require 'ob-scheme) (require 'ob-R) #+END_SRC All of this should be loaded lazily. #+BEGIN_SRC elisp :noweb-ref org-mode-lazy (with-eval-after-load "org" <> ) #+END_SRC * Editing files on remote systems :PROPERTIES: :header-args: :noweb-ref tramp :END: TRAMP is a really convenient way to edit files on remote systems from within the comfort of my cozy customised local Emacs session. I use it to edit files at work, to edit things on my server =elephly.net=, and even to edit things as root on the local system. #+BEGIN_SRC elisp (require 'tramp) (setq tramp-default-method "ssh") (setq tramp-default-proxies-alist (list ;; Do not use a proxy on the same system. '((regexp-quote (system-name)) nil nil) ;; For root connections to remote hosts, log in via ssh with normal ;; user account first, then su/sudo to root '("elephly\\.net\\'" "\\`root\\'" "/ssh:%h:") ;; Pass through ssh1 as user ‘rwurmus’ to reach remote hosts on ;; MDC network. '("mdc-berlin\\.net" "\\`rwurmus\\'" "/ssh:rwurmus@ssh1.mdc-berlin.de:"))) ;; ssh1 runs a restricted shell session, so "exec ssh" cannot be used. (add-to-list 'tramp-restricted-shell-hosts-alist "\\`ssh1\\.mdc-berlin\\.de\\'") ;; respect the PATH variable on the remote machine (add-to-list 'tramp-remote-path 'tramp-own-remote-path) (setq tramp-verbose 3) #+END_SRC * Shell :PROPERTIES: :header-args: :noweb-ref shell :END: The default prompt face makes it hard to see the prompt. #+BEGIN_SRC elisp (set-face-attribute 'comint-highlight-prompt nil :foreground "#859900" :weight 'bold) #+END_SRC * Magit :PROPERTIES: :header-args: :noweb-ref magit :END: #+BEGIN_SRC elisp ;; full screen magit-status (defadvice magit-status (around magit-fullscreen activate) (window-configuration-to-register :magit-fullscreen) ad-do-it (delete-other-windows)) (defun my/magit-quit-session () "Restores the previous window configuration and kills the magit buffer" (interactive) (kill-buffer) (jump-to-register :magit-fullscreen)) (defun my/magit-toggle-whitespace () "Toggles git option -w" (interactive) (if (member "-w" magit-diff-options) (my/magit-dont-ignore-whitespace) (my/magit-ignore-whitespace))) (defun my/magit-ignore-whitespace () "Adds git option -w" (interactive) (add-to-list 'magit-diff-options "-w") (magit-refresh)) (defun my/magit-dont-ignore-whitespace () "Removes git option -w" (interactive) (setq magit-diff-options (remove "-w" magit-diff-options)) (magit-refresh)) (define-key magit-status-mode-map (kbd "q") 'my/magit-quit-session) (define-key magit-status-mode-map (kbd "W") 'my/magit-toggle-whitespace) (setq magit-diff-refine-hunk 'all) #+END_SRC #+BEGIN_SRC elisp :noweb-ref magit-lazy (with-eval-after-load "magit" <> ) (global-set-key (kbd "C-c m") 'magit-status) #+END_SRC * Smex and Ido :PROPERTIES: :header-args: :noweb-ref ido :END: #+BEGIN_SRC elisp (require 'ido-vertical-mode) (ido-mode 1) (ido-vertical-mode 1) (setq ido-vertical-define-keys 'C-n-and-C-p-only) (setq ido-ubiquitous-max-items 50000) ;; hit ~ to go straight to the home directory in ido mode (add-hook 'ido-setup-hook (lambda () ;; Go straight home (define-key ido-file-completion-map (kbd "~") (lambda () (interactive) (if (looking-back "/") (insert "~/") (call-interactively 'self-insert-command)))))) ;; Use ido everywhere (require 'ido-ubiquitous) (ido-ubiquitous-mode 1) (fset 'yes-or-no-p 'y-or-n-p) ;; open file at point with C-x C-f (setq ido-use-filename-at-point 'guess) #+END_SRC Smex is a very nice replacement for the builtin =M-x= feature. It gives me fuzzy matching and lists recently and most frequently used commands first. #+BEGIN_SRC elisp (require 'smex) (smex-initialize) #+END_SRC Here are some key bindings to use Smex features. #+BEGIN_SRC elisp :noweb-ref smex-keys (global-set-key (kbd "M-x") 'smex) (global-set-key (kbd "M-X") 'smex-major-mode-commands) (global-set-key (kbd "") 'smex) (global-set-key (kbd "S-") 'smex-major-mode-commands) #+END_SRC #+BEGIN_SRC elisp :noweb-ref ido-lazy (with-eval-after-load "ido" <> ) <> #+END_SRC * Completion :PROPERTIES: :header-args: :noweb-ref completion :END: Company mode provides automatic completion. I like to enable it in all programming modes. I don’t use the global company mode because that would enable it in =org-mode= where the pop-up looks terrible with =variable-pitch-mode= enabled. #+BEGIN_SRC elisp :noweb-ref company-hook (add-hook 'prog-mode-hook 'company-mode) #+END_SRC I like automatic completion, but it’s nice to also have a key to trigger completion. #+BEGIN_SRC elisp (setq company-idle-delay 0.5) (define-key company-mode-map (kbd "C-c ") 'company-complete) #+END_SRC #+BEGIN_SRC elisp (require 'color) (let ((bg (face-attribute 'default :background))) (custom-set-faces `(company-tooltip ((t (:inherit default :background ,(color-lighten-name bg 2))))) `(company-scrollbar-bg ((t (:background ,(color-lighten-name bg 10))))) `(company-scrollbar-fg ((t (:background ,(color-lighten-name bg 5))))) `(company-tooltip-selection ((t (:inherit font-lock-function-name-face)))) `(company-tooltip-common ((t (:inherit font-lock-constant-face)))))) #+END_SRC #+BEGIN_SRC elisp :noweb-ref completion-lazy (with-eval-after-load "company" <> ) <> #+END_SRC * Pretty symbols :PROPERTIES: :header-args: :noweb-ref pretty-symbols :END: #+BEGIN_SRC elisp (defun my/pretty-js-symbols () (push '("===" . ?≡) prettify-symbols-alist) (push '("function" . ?𝑓) prettify-symbols-alist)) (defun my/pretty-r-symbols () (push '("%>%" . ?⤚) prettify-symbols-alist) (push '("%$%" . ?⤜) prettify-symbols-alist) (push '("==" . ?≡) prettify-symbols-alist) (push '("function" . ?𝑓) prettify-symbols-alist)) (when (boundp 'global-prettify-symbols-mode) (add-hook 'js2-mode-hook 'my/pretty-js-symbols) (add-hook 'ess-mode-hook 'my/pretty-r-symbols) (add-hook 'inferior-ess-mode-hook 'my/pretty-r-symbols) (global-prettify-symbols-mode +1)) #+END_SRC * Resize buffer margins dynamically :PROPERTIES: :header-args: :noweb-ref resize-dynamically :END: I don’t want to have any margins by default. #+BEGIN_SRC elisp (setq-default left-margin-width 0 right-margin-width 0) #+END_SRC When writing Org-mode documents or when browsing the web with Eww I prefer to see shorter lines. The following procedure takes the current window width and adjust the buffer margins dynamically such that the buffer contents are restricted in width. I find this much more readable when editing Org documents or browsing with Eww. #+BEGIN_SRC elisp (defun my/dynamic-margin-window (window) "Get current window width and adjust margins such that the buffer contents are centered and no more than 80 characters wide." (with-current-buffer (window-buffer window) (when (or (eq major-mode 'org-mode) (eq major-mode 'eww-mode)) (let* ((textwidth 80) (margin (max (- (window-size window t) textwidth) 0))) (setq left-margin-width 0 right-margin-width margin) (set-window-buffer window (current-buffer)))))) (defun my/dynamic-margin (frame) "Dynamically adjust margin for all windows of the frame." (walk-windows 'my/dynamic-margin-window 'no-minibuf frame)) #+END_SRC The procedure is added to the list of functions that are to be evaluated whenever the window size changes. As a result the content width is adjusted dynamically. #+BEGIN_SRC elisp (add-to-list 'window-size-change-functions 'my/dynamic-margin) #+END_SRC * Multimedia with EMMS :PROPERTIES: :header-args: :noweb-ref emms :END: I like to use EMMS as a music player. Unfortunately, the default players like mpg321 or ogg123 don’t support seeking, so I’m using =mpv= for everything. #+BEGIN_SRC elisp (require 'emms-player-mpv) (setq emms-player-list (list emms-player-mpv)) #+END_SRC On my laptop screen the file name in the EMMS modeline cannot be fully displayed. I use =emms-mode-line-cycle= to limit the width of the file name in the modeline. #+BEGIN_SRC elisp (require 'emms-mode-line-cycle) (emms-mode-line 1) (emms-playing-time 1) (emms-mode-line-cycle 1) #+END_SRC Some more initialisation settings: #+BEGIN_SRC elisp (emms-all) (setq emms-playlist-buffer-name "*EMMS*") (setq emms-source-file-directory-tree-function 'emms-source-file-directory-tree-find) #+END_SRC Set up global key bindings for controlling EMMS in any mode. #+BEGIN_SRC elisp (require 'emms-setup) (global-set-key (kbd "C-c e t") 'emms-play-directory-tree) (global-set-key (kbd "C-c e x") 'emms-start) (global-set-key (kbd "C-c e v") 'emms-stop) (global-set-key (kbd "C-c e n") 'emms-next) (global-set-key (kbd "C-c e p") 'emms-previous) (global-set-key (kbd "C-c e o") 'emms-show) (global-set-key (kbd "C-c e h") 'emms-shuffle) (global-set-key (kbd "C-c e e") 'emms-play-file) (global-set-key (kbd "C-c e f") 'emms-play-playlist) (global-set-key (kbd "C-c e SPC") 'emms-pause) (global-set-key (kbd "C-c e a") 'emms-add-directory-tree) (global-set-key (kbd "C-c e r") 'emms-toggle-repeat-track) (global-set-key (kbd "C-c e R") 'emms-toggle-repeat-playlist) ;; playlist-mode-map (define-key emms-playlist-mode-map (kbd "SPC") 'emms-pause) (define-key emms-playlist-mode-map (kbd "+") 'emms-volume-raise) (define-key emms-playlist-mode-map (kbd "-") 'emms-volume-lower) (define-key emms-playlist-mode-map (kbd "") (lambda () (interactive) (emms-seek +10))) (define-key emms-playlist-mode-map (kbd "") (lambda () (interactive) (emms-seek -10))) (define-key emms-playlist-mode-map (kbd "") (lambda () (interactive) (emms-seek +60))) (define-key emms-playlist-mode-map (kbd "") (lambda () (interactive) (emms-seek -60))) #+END_SRC All of this should be loaded lazily. #+BEGIN_SRC elisp :noweb-ref emms-lazy (with-eval-after-load "emms" <> ) #+END_SRC * Lilypond :PROPERTIES: :header-args: :noweb-ref lilypond :END: Activate Lilypond mode when I’m opening a Lilypond score. #+BEGIN_SRC elisp (require 'lilypond-mode) (add-to-list 'auto-mode-alist '("\\.ly\\'" . LilyPond-mode)) #+END_SRC Enable =subword-mode= in Lilypond files because I use CamelCase for music variables. #+BEGIN_SRC elisp (add-hook 'LilyPond-mode-hook 'subword-mode) #+END_SRC I like to render Lilypond snippets in Org mode buffers. To do that I need to load the Lilypond backend first. #+BEGIN_SRC elisp (with-eval-after-load "org" (require 'ob-lilypond)) #+END_SRC * Scheme development :PROPERTIES: :header-args: :noweb-ref scheme :END: Geiser makes Scheme development really nice. It’s also used for Guix development in combination with =guix-devel-mode=, so I’m adding the Guix development directory to Guile’s load path in all Geiser sessions. #+BEGIN_SRC elisp (with-eval-after-load "geiser" (setq geiser-guile-load-path '("~/dev/guix-wip"))) #+END_SRC Parentheses don’t annoy me but I still prefer to have them fade into the background a little. This is what =paren-face-mode= does. #+BEGIN_SRC elisp :noweb-ref paren-face-hook (require 'paren-face) (global-paren-face-mode 1) #+END_SRC Emacs also highlights matching parentheses, but it does so with a delay. Here I’m disabling the delay. #+BEGIN_SRC elisp :noweb-ref show-paren-hook (require 'paren) (setq show-paren-delay 0) (show-paren-mode 1) #+END_SRC Editing lispy languages is no fun without =paredit=, a mode to enforce balanced parentheses. Enable it automatically when editing Scheme or Elisp. #+BEGIN_SRC elisp (add-hook 'scheme-mode-hook (lambda () (paredit-mode 1))) (add-hook 'emacs-lisp-mode-hook (lambda () (paredit-mode 1))) (add-hook 'geiser-repl-mode-hook (lambda () (paredit-mode 1))) #+END_SRC Also enable =paredit= when editing Elisp in the minibuffer. #+BEGIN_SRC elisp ;; Enable `paredit-mode' in the minibuffer, during `eval-expression'. (defun conditionally-enable-paredit-mode () (if (eq this-command 'eval-expression) (paredit-mode 1))) (add-hook 'minibuffer-setup-hook 'conditionally-enable-paredit-mode) #+END_SRC Some customisations for =paredit=. #+BEGIN_SRC elisp (with-eval-after-load "paredit" ;; don't hijack \ please (define-key paredit-mode-map (kbd "\\") nil) ;; keybindings (define-key paredit-mode-map (kbd "M-C-") 'backward-kill-sexp) ;; making paredit work with delete-selection-mode (put 'paredit-forward-delete 'delete-selection 'supersede) (put 'paredit-backward-delete 'delete-selection 'supersede) (put 'paredit-newline 'delete-selection t)) #+END_SRC TODO: the parentheses adjustments should happen only when I’m in programming mode. #+BEGIN_SRC elisp <> <> #+END_SRC * God mode :PROPERTIES: :header-args: :noweb-ref god :END: #+BEGIN_SRC elisp (eval-after-load "god-mode" '(progn (defadvice god-mode-lookup-key-sequence (before my-swap-x-t) "Swap ?x and ?t KEY arguments." (case (ad-get-arg 0) (?x (ad-set-arg 0 ?t)) (?t (ad-set-arg 0 ?x)))) (ad-activate 'god-mode-lookup-key-sequence) (defvar original-color (frame-parameter nil 'cursor-color)) (defun my-update-cursor () (if god-local-mode (set-cursor-color "Red") (set-cursor-color original-color))) (define-key god-local-mode-map (kbd ".") 'repeat) (add-hook 'god-mode-enabled-hook 'my-update-cursor) (add-hook 'god-mode-disabled-hook 'my-update-cursor) ;; reliably toggle god-mode for all modes (setq god-exempt-major-modes nil) (setq god-exempt-predicates nil) (defun god-toggle-on-overwrite () "Toggle god-mode on overwrite-mode." (if (bound-and-true-p overwrite-mode) (god-local-mode-pause) (god-local-mode-resume))) (add-hook 'overwrite-mode-hook 'god-toggle-on-overwrite) (define-key god-local-mode-map (kbd "i") 'god-local-mode) (global-set-key (kbd "C-x C-k") 'kill-this-buffer) (global-set-key (kbd "C-x C-1") 'delete-other-windows) (global-set-key (kbd "C-x C-2") 'split-window-below) (global-set-key (kbd "C-x C-3") 'split-window-right) (global-set-key (kbd "C-x C-0") 'delete-window))) ;; TODO: only do this on the Thinkpad (global-set-key (kbd "") 'god-mode-all) #+END_SRC * TODO Initial stuff :PROPERTIES: :header-args: :noweb-ref initial :END: This is supposed to happen at the very beginning, even before loading packages. I still need to arrange these things nicely. #+BEGIN_SRC elisp ;; No splash screen please ... (setq inhibit-startup-message t) ;; display tool tips in echo area only (tooltip-mode -1) (setq frame-resize-pixelwise t) (defalias 'yes-or-no-p 'y-or-n-p) ;; disable mouse scrolling (mouse-wheel-mode -1) (add-to-list 'load-path (concat user-emacs-directory "lisp")) #+END_SRC * TODO More stuff :PROPERTIES: :header-args: :noweb-ref initial-after-packages :END: This is even more stuff to be done after initialising packages. I still need to process all of this and clean it up. #+BEGIN_SRC elisp ;; better defaults, includes hiding the GUI (require 'better-defaults) (require 'projectile) (projectile-global-mode) (require 'guide-key) (setq guide-key/guide-key-sequence '("C-x r" "C-x 4" "C-c p")) (guide-key-mode) ;; Keep emacs Custom-settings in separate file (setq custom-file (expand-file-name "custom.el" user-emacs-directory)) (load custom-file) (setq frame-background-mode 'light) (load-theme 'solarized-light t) #+END_SRC * TODO And even more :PROPERTIES: :header-args: :noweb-ref old-init :END: #+BEGIN_SRC elisp (setq backup-directory-alist `(;; Do not backup or auto-save remote files to prevent delays. (,tramp-file-name-regexp . nil) ;; Write backup files to a dedicated directory. ("." . ,(expand-file-name (concat user-emacs-directory "backups"))))) ;; Make backups of files, even when they're in version control (setq vc-make-backup-files t) (add-to-list 'auto-mode-alist '("\\.markdown\\'" . markdown-mode)) (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-mode)) (add-to-list 'auto-mode-alist '("\\.js$" . js2-mode)) (setq scss-compile-at-save nil) ;; (setq whitespace-global-modes '(not erc-mode)) ;; (global-whitespace-mode 1) ;; (set-face-attribute 'whitespace-space nil :background nil :foreground "gray20") ;; (set-face-attribute 'whitespace-newline nil :background nil :foreground "gray20") ;; (setq whitespace-style ;; '(face spaces tabs newline space-mark tab-mark newline-mark)) ;; (setq whitespace-display-mappings ;; ;; all numbers are Unicode codepoint in decimal. try ;; ;; (insert-char 182 ) to see it ;; '( ;; (space-mark 32 [183] [46]) ; 32 SPACE, 183 MIDDLE DOT 「·」, ;; ; 46 FULL STOP 「.」 ;; (newline-mark 10 [182 10]) ; 10 LINE FEED ;; (tab-mark 9 [9655 9] [92 9]) ; 9 TAB, 9655 WHITE ;; ; RIGHT-POINTING TRIANGLE 「▷」 ;; )) (desktop-save-mode t) ;; ediff settings (setq ediff-diff-options "-w") ;; cause Emacs to fully redraw the display *before* it processes queued input events. (setq redisplay-dont-pause t) ;; fewer backslashes in regexp builder (require 're-builder) (setq reb-re-syntax 'string) ;; remove prompt on killing process buffer (setq kill-buffer-query-functions (remq 'process-kill-buffer-query-function kill-buffer-query-functions)) ;; enable features that are disabled by default (put 'narrow-to-region 'disabled nil) (put 'erase-buffer 'disabled nil) (put 'narrow-to-page 'disabled nil) (require 'fill-column-indicator) (setq fci-rule-use-dashes t) (setq fci-rule-color "#cccccc") (setq fci-dash-pattern 0.3) (add-hook 'prog-mode-hook 'fci-mode) ;; This is a workaround to display the company-mode completion popup ;; when fci-mode is enabled. It was taken from here: ;; https://github.com/company-mode/company-mode/issues/180#issuecomment-55047120 (defvar-local company-fci-mode-on-p nil) (defun company-turn-off-fci (&rest ignore) (when (boundp 'fci-mode) (setq company-fci-mode-on-p fci-mode) (when fci-mode (fci-mode -1)))) (defun company-maybe-turn-on-fci (&rest ignore) (when company-fci-mode-on-p (fci-mode 1))) (add-hook 'company-completion-started-hook 'company-turn-off-fci) (add-hook 'company-completion-finished-hook 'company-maybe-turn-on-fci) (add-hook 'company-completion-cancelled-hook 'company-maybe-turn-on-fci) ;; keep the cursor centered to avoid sudden scroll jumps (require 'centered-cursor-mode) ;; disable in terminal modes ;; http://stackoverflow.com/a/6849467/519736 ;; also disable in Info mode, because it breaks going back with the backspace key (define-global-minor-mode my-global-centered-cursor-mode centered-cursor-mode (lambda () (when (not (memq major-mode (list 'Info-mode 'term-mode 'eshell-mode 'shell-mode 'erc-mode))) (centered-cursor-mode)))) (my-global-centered-cursor-mode 1) ;; expand region (global-set-key (kbd "M-@") 'er/expand-region) ;; Swap C-t and C-x, so it's easier to type on Dvorak layout ;; `keyboard-translate` does not work when attaching an emacsclient to ;; a running emacs in daemon mode, so instead we define the key in the ;; key-translation-map. ;; http://lists.gnu.org/archive/html/help-gnu-emacs/2009-10/msg00505.html (define-key key-translation-map [?\C-x] [?\C-t]) (define-key key-translation-map [?\C-t] [?\C-x]) ;; Use narrow tab width (set-default 'tab-width 4) (setq tab-width 4) (load "email.el") (setq gnus-select-method '(nntp "news.gmane.org")) (load "init-eshell.el") (load "init-modeline.el") (load "init-my-stuff.el") ;; Revert stale document graphics buffers automatically when the files ;; have changed. (add-hook 'doc-view-mode-hook 'auto-revert-mode) (page-break-lines-mode 1) (global-set-key (kbd "") 'backward-page) (global-set-key (kbd "") 'forward-page) (add-to-list 'auto-mode-alist '("\\.html\\'" . sgml-mode)) (eval-after-load "sgml-mode" '(progn (require 'tagedit) (tagedit-add-paredit-like-keybindings) (tagedit-add-experimental-features) (add-hook 'html-mode-hook (lambda () (tagedit-mode 1))))) (delete-selection-mode 1) ; delete seleted text when typing ;; don't let the cursor go into minibuffer prompt, HT Xah Lee (setq minibuffer-prompt-properties '(read-only t point-entered minibuffer-avoid-prompt face minibuffer-prompt)) (require 'undo-tree) (global-undo-tree-mode 1) (setq debbugs-gnu-default-packages '("emacs" "guix")) (setq erc-join-buffer 'bury) (savehist-mode) ;; PDF view mode (setq pdf-info-epdfinfo-program "~/.guix-profile/bin/epdfinfo") (pdf-tools-install) (add-to-list 'auto-mode-alist '("\\.pdf\\'" . pdf-view-mode)) ;; enable variable-pitch-mode in eww (add-hook 'eww-mode-hook (lambda () (variable-pitch-mode 1))) ;; pretty quotes! (add-hook 'erc-mode-hook (lambda () (require 'typo) (typo-mode 1))) (add-hook 'org-mode-hook (lambda () (require 'typo) (typo-mode 1))) #+END_SRC * Putting it all together Having defined named code blocks in the sections above we can finally put them all together to build the init file #+BEGIN_SRC elisp :noweb yes :tangle yes <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> #+END_SRC # Local Variables: # org-edit-src-content-indentation: 0 # eval: (add-hook 'org-babel-post-tangle-hook (lambda nil (byte-compile-file "~/.emacs.d/init.el"))) # End: