#+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? The sole purpose of the =~/.emacs.d/init.el= file is now to load up and interpret this =org-mode= file. (We don’t execute this code here.) #+BEGIN_SRC elisp :tangle no (require 'org) ;; Load the actual configuration file (org-babel-load-file (expand-file-name (concat user-emacs-directory "init.org"))) #+END_SRC Alternatively, we can take all code blocks in this file and assemble an =init.el= from it. This is what the =org-babel-tangle= procedure does for us. I actually prefer doing it this way, because startup times are faster this way as org-mode doesn’t have to be loaded first. We can even byte-compile the init file. * Initialise packages :PROPERTIES: :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 bash :noweb-ref nil guix package -i \ emacs \ emacs-auctex \ emacs-better-defaults \ emacs-clojure-mode \ emacs-company \ emacs-debbugs \ emacs-emms \ emacs-god-mode \ emacs-haskell-mode \ emacs-ido-ubiquitous \ emacs-js2-mode \ emacs-lispy \ emacs-markdown-mode \ emacs-mmm-mode \ emacs-multiple-cursors \ emacs-org-bullets \ emacs-pdf-tools \ emacs-projectile \ emacs-smex \ emacs-typo \ emacs-undo-tree \ emacs-web-mode \ emacs-wget \ emacs-zenburn-theme \ paredit \ geiser \ magit \ sicp \ #+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 org-beautify-theme dired-details dired+ erc-hl-nicks expand-region fill-column-indicator guide-key hungry-delete ido-vertical-mode info+ page-break-lines paren-face perspective rich-minority scss-mode shell-switcher skewer-mode smart-mode-line sublime-themes tagedit visual-regexp-steroids znc)) #+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) (mapcar (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 * TODO Initial stuff :PROPERTIES: :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) ;; FIXME: This is needed to be able to use IBus Pinyin with Emacs (setenv "LC_CTYPE" "zh_CN.utf8") ;; 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: :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 'paren-face) (global-paren-face-mode 1) (require 'paren) (setq show-paren-delay 0) (show-paren-mode 1) (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 t) (set-default-font "DejaVu Sans Mono") (add-hook 'haskell-mode-hook (lambda () (turn-on-haskell-indentation) (turn-on-haskell-doc))) #+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 "~/.emacs.d/init.el" <> <> <> (load "~/.emacs.d/old-init.el") #+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") (load-file "~/.emacs.d/init.elc"))) # End: