Updated: 2022-01-25

My Emacs Init

GNU Emacs is a free (as in freedom) text editor for writing and code. You can read more about Emacs and it's history on Wikipedia.

If you are interested in learning Emacs check out the guided tour and the internal Emacs Tutorial (from within Emacs type Ctrl + h then type t). In addition, if you are interested in using Emacs for general writing (not programming) check out Org-mode, its great!

1. Why use GNU Emacs?

As a professional email writer (lol), amateur blogger, and programmer my primary input/output to the computer is through text. Emacs provides me with a rock solid text editor that is able to stand out from the competition on the following five key points

  1. Extensibility — a complete programming interface for enhancing my own user experience and automating repetitive tasks.
  2. Portable — a portable environment that works on all major operating systems. (Not all of us have the luxury of using GNU/Linux at work)
  3. Clean Interface — a distraction free interface for writing and coding.
  4. Community — Great community developing high quality packages. (magit, org-mode, eshell, etc.)
  5. Free Software — The ability to access and modify all the source code of Emacs allows for complete customization. The source code also provides an excellent resource for those wishing to learn Lisp, programming, and programming language development1. This factor combined with a rich history and an enthusiastic community ensure that Emacs will not disappear any time soon.

1.1. Emacs Lisp

One aside on Emacs Lisp, which is often disparaged as the worst surviving Lisp dialect. I will not argue that point directly, but I will posit that Emacs Lisp is a more than capable Lisp and very pleasant work in.

The surprising strength of Emacs Lisp is well captured in this excerpt from the Emacs calculator (calc) manual:

I chose Emacs Lisp, a) because I had always been curious about it and b) because, being only a text editor extension language after all, Emacs Lisp would surely reach its limits long before the project got too far out of hand.

To make a long story short, Emacs Lisp turned out to be a distressingly solid implementation of Lisp, and the humble task of calculating turned out to be more open-ended than one might have expected.

1.2. Shortcomings

A minor problem is the limited support for concurrency which causes some actions complete slowly or lock-up Emacs. However, I rarely find this causes significant annoyances. In most situations computationally intensive actions can be handed off to a separate Emacs process (see info:elisp#Processes) or a separate program entirely (e.g. using isync to retrieve mail).

Beyond technical limitations my main issue with Emacs is I cannot use it all the time. With the huge investment I've made on my computer it would be wonderful to have the power of Emacs in a smartphone type package. However, touchscreen displays do not translate well to the Emacs workflow. That's why I've recently pre-ordered the Pyra Handheld to hopefully use as a Emacs PDA.

1.3. Thoughts from other Emacs Users

I've found both these video essays by Protesilaos Stavrou to be a great summary of the thought process involved for why someone would want to use Emacs:

2. Literate Configuration wih Org Mode

My Emacs configuration file is written in a literate programming-like style which allows for prose to be mixed with source code blocks. From that the relevant source blocks are extracted and copied to my Emacs configuration file.

I've held out for many years creating a literate config, but seeing other users' success has convinced me. The ability to maintain complete notes, links, and the configuration code all in one file is extremely convenient. In addition, the hierarchical structure of Org allows for a clearer organization of the code.

2.1. Setup

I have one org file init.org where I combine all of my Emacs configuration code with prose notes about it. That org file can be placed anywhere on my system because it is not the actual initialization file used by Emacs. From the org file my actual init.el is exported (tangled) from the elisp source blocks. In literate programming terms, the org file is where coded and prose is woven together, then code is tangled out to just elisp that can be evaluated by Emacs. That means using a literate config imposes no startup or runtime performance penalty.

Adding the following to your org file will copy (tangle) the code blocks into your init.el file.

#+PROPERTY: header-args :results silent :tangle "~/.config/emacs/init.el"

To exclude specific source blocks from being tangled add :tangle no to the header.

Adding the following file header will re-tangle the file on save.

# -*- eval: (add-hook 'after-save-hook (lambda nil (org-babel-tangle)))  -*-

This is somewhat wasteful because it re-tangled the entire file every save even the changes to the file do not modify code. I may come up with a better solution for this at some point.

3. Emacs Boilerplate Setup

3.1. Early Init

Using an early-init.el file can speed up Emacs startup, by avoiding loading unnecessary UI elements. Below is my complete early-init.el:

;;; early-init.el --- Early initialization -*- lexical-binding: t -*-

;;; Commentary:

;;; Code:

;; Disable GUI
(tool-bar-mode -1)
(setq use-dialog-box t)
(setq use-file-dialog nil)
(setq-default frame-title-format '("%b  -  GNU Emacs"))
;; Hide the startup screen
(setq inhibit-startup-screen t)
;; Increase font size
(set-face-attribute 'default nil :height 130)

;;; early-init.el ends here

In my regular config, load early-init.el if not loaded already:

(when (version< emacs-version "27")
  (load (concat user-emacs-directory "early-init.el")))

3.2. Prefer UTF-8

(prefer-coding-system 'utf-8)

3.3. Customize

Emacs has its own customization functionality which can be useful for experimenting and finding new Emacs options. Normally these settings are appended to the end of the user's init file, setting custom-variable will save it there.

(setq custom-file (concat user-emacs-directory "customizations.el"))

Some prefer not to use this functionality as they prefer to code elisp themselves, but even if you do not use the customization interface loading it is still useful for setting safe file variables and themes.

(load custom-file 't)

3.4. Disable bell sound

(setq ring-bell-function 'ignore)

4. Emacs Lisp

4.1. Optimizations and elisp loading

Prefer newest elisp files

(setq load-prefer-newer t)

Increase the max amount allowed to be read from a process into Emacs.

(setq read-process-output-max (* 1024 1024))

4.2. Useful functions

(defun my/file-to-string (filename)
  "Convert contents of file FILENAME to a string."
     (insert-file-contents filename)
;; TODO: Could make more flexible with a variable pair for light and
;; dark themes.  Override default pairs with a user defined global
;; light/dark themes.  I'd also like to automate with sunrise/sunset.
(defun my/toggle-light-dark-theme ()
   ((custom-theme-enabled-p 'modus-operandi)
      (disable-theme 'modus-operandi)
      (load-theme 'modus-vivendi)))
   ((custom-theme-enabled-p 'modus-vivendi)
      (disable-theme 'modus-vivendi)
      (load-theme 'modus-operandi)))
   ((message "Unknown theme")))
  (shell-command "change-theme"))

4.3. Package Repositories

Add the new NonGNU ELPA (Emacs Lisp Package Archive). This repository contains only Free Software, but unlike GNU ELPA these packages have not assigned copyright to FSF and cannot be included in mainline Emacs for legal reasons.

Also adding MELPA, a popular third-party repository that contains some packages that haven't been added to NonGNU ELPA yet.

(with-eval-after-load 'package
  (add-to-list 'package-archives '("nongnu" . "https://elpa.nongnu.org/nongnu/"))
  (add-to-list 'package-archives '("melpa"  . "https://melpa.org/packages/")))

4.4. use-package

There is a good chance that =use-package may be included in Emacs 28 and this wont be necessary…

(unless (package-installed-p 'use-package)
  (package-install 'use-package))
(require 'use-package)

4.4.1. Hide minor modes from appearing in the mode-line

Delight is very similar to diminish.el, both are supported by use-package, but delight is included in GNU ELPA.

(use-package delight
  :ensure t)

5. General usage

5.1. Modeline formatting

Improve buffer naming convention when a buffer with a duplicate named buffer is opened.

(require 'uniquify)
(setq uniquify-buffer-name-style 'forward)

5.2. Global Keybindings

(global-set-key (kbd "C-x m") 'man)

5.2.1. Unbind keys

Don't hide the frame

(global-set-key (kbd "C-z") nil)
(global-set-key (kbd "C-x C-z") nil)

5.3. Keybinding reminders

When using Emacs there is always the problem of keeping track of the many keybindings used across multiple modes. One solution I recommend is keeping consistent bindings across modes as possible (many mode already attempt this to varying levels).

When it comes to remembering fringe keybindings that are useful to have, but inconsistent

. I have never quite been happy with which-key as I find the pop-ups overwhelming. Despite that, I do forget keybinding all the time.

(use-package which-key
  :ensure t
  (setq which-key-idle-delay 5)

5.4. Managing buffers

ibuffer offers a number of

(use-package ibuffer
  :bind (("C-x C-b" . ibuffer))
  (add-hook 'ibuffer-mode-hook #'hl-line-mode))

5.5. Remember our history

Track recently opened files.

(recentf-mode 1)

Track minibuffer history.

(savehist-mode 1)

5.6. Fuzzy completion   MELPA

Currently using helm. It's a very powerful package but I've always had mixed feelings about it.

;; (use-package helm
;;   :ensure t
;;   :delight
;;   :bind (("M-x" . helm-M-x)
;;          ("C-x C-f" . helm-find-files)
;;          ("C-x r b" . helm-filtered-bookmarks)
;;          :map helm-map
;;          ("<tab>" . helm-execute-persistent-action))
;;   :config
;;   (helm-mode 1))

5.6.1. Vertico

Trying out vertico.

(use-package vertico
  :init (vertico-mode))

;; Emacs 28: Hide commands in M-x which do not work in the current mode.
;; Vertico commands are hidden in normal buffers.
(when (version<= 28 emacs-version)
  (setq read-extended-command-predicate

(use-package marginalia
  :init (marginalia-mode))

5.7. Dired

(setq dired-listing-switches "-l --group-directories-first")

(add-hook 'dired-mode-hook #'dired-hide-details-mode)

Allow using 'a' key in dired

(put 'dired-find-alternate-file 'disabled nil)

5.7.1. Delete to Trash

(setq delete-by-moving-to-trash t)

5.8. Read only file View

Enables view mode in all read-only files

(setq view-read-only t)

5.9. Passwords

;; Doing this appears to break Gnus.  May need to report a bug...
;; (setq auth-sources '(password-store))

5.10. Email

(setq user-full-name "Thomas Ingram"
      user-mail-address "thomas@taingram.org")
(setq smtpmail-default-smtp-server "mail.gandi.net"
      smtpmail-smtp-server "mail.gandi.net"
      smtpmail-stream-type 'ssl
      smtpmail-smtp-service 465
      message-send-mail-function 'smtpmail-send-it)

5.10.1. Notmuch

I previous used Gnus, but it never really clicked fully for me. I liked the splitting capabilities, but I wanted to more easily mix and match filters as allowed by notmuch.


5.11. Remote file editing

Don't check for version control over tramp, this slows down tramp.

(setq vc-ignore-dir-regexp
      (format "\\(%s\\)\\|\\(%s\\)"

5.12. Terminal

(global-set-key (kbd "C-x t") 'shell)

Compilation in shell

(add-hook 'shell-mode-hook 'compilation-shell-minor-mode)

5.13. Calendar and Diary

(use-package calendar
  (setq calendar-week-start-date 1)     ; Monday
  (setq calendar-date-style 'iso)

  (setq calendar-view-diary-initially-flag t)

  (setq calendar-mark-holidays-flag t)
  (setq calendar-mark-diary-entries-flag t)

  (setq calendar-holidays
        '((holiday-fixed 1 1 "New Year's Day")
          (holiday-float 1 1 3 "Martin Luther King Day")
          (holiday-fixed 2 14 "Valentine's Day")
          (holiday-fixed 3 17 "St. Patrick's Day")
          (holiday-float 5 0 2 "Mother's Day")
          (holiday-float 5 1 -1 "Memorial Day")
          (holiday-float 6 0 3 "Father's Day")
          (holiday-fixed 7 4 "Independence Day")
          (holiday-float 9 1 1 "Labor Day")
          (holiday-fixed 10 31 "Halloween")
          (holiday-float 11 4 4 "Thanksgiving")
          (holiday-fixed 12 25 "Christmas")
          (holiday-fixed 1 6 "Epiphany")
          (holiday-fixed 8 15 "Assumption")
          (holiday-advent 0 "Advent")
          (holiday-sexp calendar-daylight-savings-starts
                        (format "Daylight Saving Time Begins %s"
                                 (/ calendar-daylight-savings-starts-time
                                    (float 60))
          (holiday-sexp calendar-daylight-savings-ends
                        (format "Daylight Saving Time Ends %s"
                                 (/ calendar-daylight-savings-ends-time
                                    (float 60))

  (setq diary-date-forms diary-iso-date-forms)
  (defalias 'diary-birthday #'diary-anniversary))

5.14. Git   MELPA

Magit provides an enhanced git interface over the built in vc-mode.

(use-package magit
  :ensure t
  :bind ("C-x g" . magit))

6. Editing Text

Configuration and packages that are useful wherever text is edited inside Emacs.

6.1. Delete trailing whitespace

(add-hook 'before-save-hook #'delete-trailing-whitespace)

6.2. Allow overwriting of selected text

(require 'delsel)
(delete-selection-mode 1)

6.3. Improve handling long lines

so-long-mode is a new mode in 27.1 that trys to improve Emacs's longstanding issues with handling long lines.

(when (version< "27" emacs-version)
  (global-so-long-mode t))

6.4. Spell Checking

Emacs does not provide internal spellchecking but relies on external programs. The two most popular options are:

  1. GNU Aspell
  2. Hunspell

Aspell is generally faster and returns more results for English. Hunspell provides better spellchecking for other languages and is more widely used in other software (e.g. LibreOffice, Firefox, Google Chrome, and MacOS). On GNU/Linux either of these should be available from your distros package manager (or likely installed already).

Within Emacs there are two main ways to invoke the spellchecker invoke ispell manually check a buffer or region for spelling errors, or enable flyspell-mode to do so automatically like a word processor.

Enable flyspell globally in all text and programming modes:

(add-hook 'text-mode-hook #'flyspell-mode)
(add-hook 'prog-mode-hook #'flyspell-prog-mode)

flyspell-prog-mode will only check spelling in comments and strings.

6.4.1. MS Windows

Aspell has not released a version for Windows since 2002, and in my experience I've found Hunspell to be easier.

  1. Download Hunspell from ezwinports maintained by Eli Zaretskii
  2. Extract the hunspell folder and put it wherever you'd like.
  3. Add the following to your init file:

    ;; Set to wherever you put the hunspell folder
    (add-to-list 'exec-path "C:/hunspell/bin/")
    (setq ispell-program-name
          (locate-file "hunspell" exec-path exec-suffixes 'file-executable-p))

After that you should be all set to use spellchecking in Emacs

For further instructions see this guide by djc on the help-gnu-emacs@gnu.org mailing list. The guide contains some additional steps on setting up multiple dictionaries which is of no use to me as a stereotypical monolingual American.

6.5. Word Wrap

Disable fill-paragraph when visual-line-mode enabled (I have a habit of using it without thinking).

(define-key visual-line-mode-map [remap fill-paragraph] 'ignore)

6.5.1. Wrap at fill column   MELPA

Nice on occasion, but not great for writing in Org mode as even src blocks will be wrapped to this as well. Decided I prefer manually wrapping lines.

(use-package visual-fill-column
  :ensure t
  ;; :hook (visual-line-mode . visual-fill-column-mode)

6.6. TODO Electric Pair

7. Programming

(use-package prog-mode
  :hook ((prog-mode . show-paren-mode)))

7.1. IDE-like features

7.1.1. Expand and collapse functions

Hideshow is a minor mode for hiding and showing contents of functions and comment blocks.

(setq hs-hide-comments-when-hiding-all nil)
(setq hs-isearch-open t)

;; Backtab is equivalent to shift+tab
;; (define-key hs-minor-mode-map (kbd "<backtab>") 'hs-toggle-hiding)

(add-hook 'prog-mode-hook #'hs-minor-mode)
  1. TODO Replace with outline-mode

    Stefan Monnier's talk at Emacs Conf convinced me to try using outline-mode instead of hideshow.

7.1.2. Snippets

(use-package yasnippet
  :ensure t
  :hook ((prog-mode) . yas-minor-mode)
  (setq yas-snippet-dirs (concat user-emacs-directory "snippets/")))

7.1.3. Drop down auto-completion support

(use-package company
  :ensure t
  (global-company-mode 1)
  (company-idle-delay 0)
  (company-minimum-prefex-length 1))

7.1.4. Language Server Protocol (LSP)

LSP is a recently developed standard communication protocol that allows development tools (code completion, documentation retrieval on hover, lookup definition, and linting/diagnostics). The protocol was originally developed by Microsoft for their Visual Studio Code editor.

There are two competing Emacs plugins

While lsp-mode has a larger development community and is more feature rich, eglot is simpler and included in GNU ELPA.

(use-package eglot
  :ensure t)

7.2. Emacs Lisp

(use-package paredit
  :ensure t
  :hook ((emacs-lisp-mode lisp-mode scheme-mode clojure-mode) . paredit-mode))
(use-package emacs-lisp-mode
  :bind (:map emacs-lisp-mode-map
              ("C-c C-r" . eval-region)
              ("C-c C-d" . eval-defun)
              ("C-c C-b" . eval-buffer))
  :hook ((emacs-lisp-mode . flymake-mode)))

7.3. Clojure

Recently started toying with Clojure in the hopes it will motivate me to program more.

(use-package clojure-mode
  :ensure t)

(use-package cider
  :ensure t
  :after clojure-mode)

7.4. Shell

(add-hook 'sh-mode-hook 'flymake-shellcheck-load)
(add-hook 'sh-mode-hook 'flymake-mode)

7.5. C

(use-package c-mode
  :bind (:map c-mode-map
              ("C-c c"   . compile)
              ("C-c C-c" . recompile)
              ("C-c g"   . gdb)
              ("C-c C-r" . gdb-run))
  :hook ((c-mode . electric-pair-mode)
         (c-mode . flymake-mode))
  (setq c-block-comment-prefix "* "))

7.6. Python

(use-package python-mode
  :hook ((python-mode . electric-pair-mode)))

7.7. Go   MELPA

Note that go-mode is an external package.

(use-package go-mode
  :ensure t
  :bind (:map go-mode-map
              ("C-c RET" . compile)
              ("C-c c"   . compile)
              ("C-c C-c" . recompile)
              ("C-c d"   . godoc)
              ("C-c f"   . gofmt)
              ("C-c g"   . gdb)
              ("C-c C-g" . gdb-run))
  (defun my/go-mode-set-local ()
    (set (make-local-variable 'compile-command) "go build -v "))

  ;; Disables links to the web documentation
  (setq-default eglot-workspace-configuration
              '((:gopls . ((linksInHover . :json-false)))))

  :hook ((go-mode . my/go-mode-set-local)
         (go-mode . subword-mode)
         (go-mode . electric-pair-mode)
         (before-save . gofmt-before-save)))

8. Writing

8.1. Org mode

(add-to-list 'load-path (concat user-emacs-directory "org-mode"))

(use-package org
  :bind (("C-x a" . org-agenda)
         ("C-x c" . org-capture))
  :hook ((org-mode . auto-fill-mode))   ; Life is too short to manually wrap lines

  ;; Org src evaluation langauges
  (org-babel-do-load-languages 'org-babel-load-languages
                               '((emacs-lisp . t)
                                 (shell . t)
                                 (latex . t)))

  ;; Templates (e.g. <s)
  (require 'org-tempo)
  (tempo-define-template "my-tempo-template-org-elisp-src"
                         '("#+begin_src emacs-lisp" n p n "#+end_src" n)
                         "Insert an Emacs Lisp source code block template")

  (set-face-attribute 'org-ellipsis nil
                      :inherit '(font-lock-comment-face default)
                      :weight 'normal)

  (set-face-attribute 'org-block-begin-line nil
                      :extend t)

  (set-face-attribute 'org-block-end-line nil
                      :extend t)

  (org-ellipsis "⤵")
  ;; (org-startup-indented t)
  (org-adapt-indentation 'headline-data)
  (org-hide-emphasis-markers t)

  ;; Org agenda setup
  (org-agenda-files '("~/todo.org"))
  (org-agenda-include-diary t)
  (org-agenda-todo-list-sublevels nil)

  ;; If clock is running bug me if I stop working
  (org-clock-idle-time 10)

   `(("t" "Todo" entry (file+headline "~/todo.org")
      "* TODO %^{Todo}\n %i\n %a\n\n%?")
     ("j" "Journal" entry (file+headline
                           ,(substring (current-time-string) -4 nil))
      "* %u %^{Entry title}\n %?\n")
     ("l" "Interesting Links" entry
      (file "~/Documents/taingram.org/org/interesting-links.org")
      "* %l\n%u"
      :prepend t)
     ("w" "Watch List" entry
      (file "~/Documents/taingram.org/org/watchlist.org")
      "* %^{Title}
:RATING:   %^{Rating}p
:YEAR:     %^{Year}p

      :prepend t)))

  (add-hook 'org-mode-hook
            (lambda () (setq-local electric-pair-inhibit-predicate (lambda (c) (char-equal c ?\<))))))

8.1.1. Blogging

Load my blog's configuration file.

(load "~/Documents/taingram.org/publish")

8.1.2. Org Open Movie Database

I maintain my movie watchlist in an org file and orgmdb.el makes it easier to pull in metadata about the movies.

(add-to-list 'load-path (concat user-emacs-directory "manual-packages/orgmdb"))

(use-package orgmdb
  (setq orgmdb-omdb-apikey (my/file-to-string "~/.omdb-apikey"))
  (setq orgmdb-fill-property-list '(year genre director country imdb-id)))

8.2. Markdown   MELPA

I personally am not a huge fan of markdown, org mode's syntax just makes much more sense to me, but it's become nearly unavoidable.

(use-package markdown-mode
  :ensure t)

8.3. LaTeX

Old setup, I haven't used LaTeX since I graduated college and its unlikely that I'll use it any time soon…

(use-package tex
  :ensure auctex
  :mode ("\\.tex\\'" . latex-mode)
  (defun my/latex-compile ()
    "My compile latex function"
    (TeX-command "LaTeX" 'TeX-master-file))

  (setq TeX-command-default 'LaTeX)

  :bind (:map TeX-mode-map
              ("C-c _" . "\\textunderscore "))

  :hook ((TeX-mode . auto-fill-mode)))

8.4. HTML

(use-package web-mode

9. See Also

Other interesting literate configuration files that inspired me.



How Emacs changed my life by Matz Yukihori the creator of Ruby. The talk explains how access to Emacs's source code directly inspired him in the development of Ruby.


Email questions, comments, and corrections to comment@taingram.org.

Submissions may appear publicly on this website, unless requested otherwise in your email.