Emacs configuration

Table of Contents

Basics

Lexical binding

By default, Emacs uses dynamic binding. In order to use closures, I enable lexical binding.

;; -*- lexical-binding: t -*-

Package manager and repositories

First things first, set up the package manager. Define the list of repos and initialize installed packages. More and more, I only use Emacs packages from my distro, GNU GuixSD. But, from time to time, I still need MELPA.

(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))
(package-initialize)

bind-keys

bind-keys is a useful macro from use-package1 that makes binding keys easy. I make extensive use of it in my Emacs configuration.

Autoloading

Autoloading lets you speed up Emacs startup and reduce memory usage by postponing loading files until the last moment when some command requiring that file is invoked.

Wherever necessary, I have wrapped up forms in the built-in with-eval-after-load macro so that they run only after some file is loaded.

Emacs as a window manager

I totally live in Emacs, so much so that I run exwm on Emacs to use it as my window manager. This is, at least at the moment, not the easiest or the most reliable approach to window management, but there are benefits. For example, with Emacs as my window manager, I can bind any key sequence on my keyboard in Emacs. Other window managers tend to block some key sequences. I can switch to and from external programs as though they were just Emacs buffers. I don't need an external program such as xbindkeys to handle keybindings to media keys, function keys, etc. I can instead use the excellent programmability of Emacs Lisp combined with clean integration to the exwm window manager. Not only does this centralize more of my system configuration in one place, it also lets me write more complex and cleanly integrated functions for my keybindings than would be possible with a bunch of shell scripts.

bind-exwm-keys

In order to get keybindings to work across all buffers (both external program buffers managed by exwm and other Emacs buffers), I need to bind them using exwm-input-set-key. To make this convenient, I have a macro bind-exwm-keys similar in spirit to bind-keys described earlier.

bind-exwm-keys might have to bind key sequences to not just Emacs commands, but also to external programs. For this, the external program needs to be wrapped up in an Emacs command. This wrapping is handled by make-external-command. make-external-command returns a command that invokes the external program if it isn't already running, switches to the buffer of the external program if it is already running, and switches to the last used buffer2 if the external program is already running and is the current buffer. This makes it easy to start an external program, switch to it, and switch back from it all using the same keybinding.

(require 'exwm)

(defun switch-to-last-used-buffer ()
  (interactive)
  (switch-to-buffer (other-buffer (current-buffer) t)))

(defun make-external-command (command)
  (lambda ()
    (interactive)
    (let ((buffer-name (car (split-string command))))
      (cond
       ((equal buffer-name (buffer-name))
        (switch-to-last-used-buffer))
       ((get-buffer buffer-name)
        (switch-to-buffer (get-buffer buffer-name)))
       (t (start-process-shell-command buffer-name nil command))))))

(defmacro bind-exwm-keys (&rest keybindings)
  `(mapc (cl-function
          (lambda ((keybinding . command))
            (exwm-input-set-key (kbd keybinding)
                                (if (stringp command)
                                    (make-external-command command)
                                  command))))
         ',keybindings))

Binding function keys and media keys

I then bind a lot of external programs to the function and media keys.

(bind-exwm-keys ("<f2>" . "icecat")
                ("<f4>" . "lxterminal")
                ("<f9>" . "gajim")
                ("<f10>" . "mpv")
                ("<f11>" . "xbacklight -dec 5")
                ("<f12>" . "xbacklight -inc 5")
                ("<XF86AudioMute>" . "amixer set Master toggle")
                ("<XF86AudioLowerVolume>" . "amixer set Master 5%-")
                ("<XF86AudioRaiseVolume>" . "amixer set Master 5%+")
                ("<XF86HomePage>" . "icecat")
                ("<XF86Sleep>" . "loginctl suspend")
                ("<S-XF86Sleep>" . "loginctl suspend; slock"))

Running external programs

I might still need to run external programs not covered above with the function and media keys. For these, I have a command run-external-program that prompts for the program to run and starts it using start-process-shell-command. The prompt provides all executables in PATH as completions.

(let ((executables (seq-mapcat
                    (lambda (bin)
                      (directory-files bin nil "^[^.]"))
                    exec-path)))
  (defun run-external-program (command)
    (interactive (list (completing-read "$ " executables)))
    (start-process-shell-command command nil command)))

(bind-exwm-keys ("s-r" . run-external-program))

Rename exwm buffers

I want exwm buffers to be named according to the programs they are running.

(add-hook 'exwm-update-class-hook
          (lambda ()
            (exwm-workspace-rename-buffer
             (downcase exwm-class-name))))

Enable exwm

Finally, I enable exwm.

(exwm-enable)

Customization of built in Emacs features

"yes or no" vs "y or n"

I can't understand why anybody would want to answer a full word "yes", instead of just "y", or "no", instead of just "n". It's certainly too long for me. Therefore, I make all prompts "y or n".

(defalias 'yes-or-no-p 'y-or-n-p)

Backup files

I don't want backup files littering my folders. I use version control systems a lot anyway.

(setq make-backup-files nil)

Menu and tool bars

Coming from a Vim background, I prefer to not have menu bars and tool bars clutter my limited screen real estate. I like all of the screen devoted to just text. Menu bars are very useful to discover new features, but I can always enable them when I need to, instead of keeping them permanently enabled.

(menu-bar-mode 0)
(tool-bar-mode 0)

Visual line mode

A lot of my work in emacs involves writing using org mode and latex. Word wrapping and the use of "visual lines" is absolutely important for this.

(global-visual-line-mode t)

Highlighting parentheses

Highlighting matching parentheses is really important when coding. I don't know why this is not enabled by default in emacs.

(show-paren-mode)

Windows

Splitting windows

My screen is wide. Therefore, I want split windows to be side by side (split horizontally) rather than one on top of another (split vertically).

(setq split-height-threshold nil
      split-width-threshold 75)

Moving between windows

The standard way of moving between windows using C-x o involves too many keystrokes. I prefer the more spatially intuitive s-{left,right,up,down} keybindings of the Windmove package with wrapping around the edge of the frame enabled. The Windmove default "Shift" modifier conflicts with org mode's default keybindings. So, I'm using the "Super" modifier instead.

(setq windmove-wrap-around t)
(windmove-default-keybindings 's)

Window manipulation keybindings

I bind several window and buffer manipulation commands to keybindings in the spirit of the Super key windmove keybindings. The keybindings I have set up are better than the default keybindings for these commands because they only need one keypress instead of two.

I hardly use the window enlargement/shrinking commands. Nevertheless, I keep them just in case I need them.

(bind-exwm-keys ("<s-tab>" . switch-to-last-used-buffer)
                ("s-1" . delete-other-windows)
                ("s-_" . split-window-below)
                ("s-|" . split-window-right)
                ("s--" . shrink-window-horizontally)
                ("<s-kp-subtract>" . shrink-window-horizontally)
                ("s-+" . enlarge-window-horizontally)
                ("<s-kp-add>" . enlarge-window-horizontally)
                ("s-*" . enlarge-window)
                ("<s-kp-multiply>" . enlarge-window)
                ("s-/" . shrink-window)
                ("<s-kp-divide>" . shrink-window))

Evil and Evil plugins

I was using Vim for around 3 to 4 years before switching to Emacs for lisp and its superior programmability. However, I hardly want to retrain my muscle memory by learning all new keybindings. Besides, why not use the best of both worlds? So, I use Evil (the Extensible vi Layer for Emacs) on top of Emacs to emulate Vim.

(evil-mode)

Vim users have come up with nifty plugins to make life more convenient and efficient on Vim. I used to use many of these plugins when I was on Vim. Thankfully, many of these plugins have been ported to Emacs Evil, and I can stay right at home here in Emacs and enjoy their benefits.

evil-surround

evil-surround, a port of vim-surround, helps change surrounding text objects (such as matching parentheses) easily. How did people live without these things!

(global-evil-surround-mode)

evil-commentary

evil-commentary, a port of vim-commentary, helps quickly and easily comment out and uncomment lines. Very useful when coding. Earlier, I used to use regular expression based substitutions to do the same thing, but this is such a commonly performed operation, that it is worth having a separate plugin for it.

I unbind "s-" as it interferes with the /shrink-window keybinding I made earlier.

(evil-commentary-mode)
(unbind-key "s-/" evil-commentary-mode-map)

evil-quickscope

A common character motion in Vim/Evil is to jump to a target word by using "f". For larger sentences, this gets tedious because you are forced to scan through the text trying to decide which character to jump to. evil-quickscope, a port of vim-quickscope, highlights these characters, making it each easier to jump to words without guesswork or tedious scanning.

evil-quickscope-always-mode is distracting with too many colors changing rather rapidly. So, I prefer to use evil-quickscope-mode which highlights target characters only on pressing the "f" key.

(global-evil-quickscope-mode)

Relative line numbering

Relative line numbering (similar to Vim's "set rnu") is very important for the "j" and "k" motions of Evil. In Emacs, relative line numbering is implemented by the Linum-Relative minor mode. While this is not an Evil plugin, I include it along with Evil plugins because it emulates a Vim-like feature.

By default, Linum-Relative operates like Vim's "set ru" displaying 0 on the current line, and the relative line number on other lines. In order to make it operate like Vim's "set rnu" – that is, display the absolute line number on the current line and the relative line number on other lines, I set linum-relative-current-symbol to the empty string "".

Some buffers, such as shell and REPL buffers, tend to have too many lines. The use of Linum-Relative mode in those buffers slows down Emacs very badly. Some other buffers, such as image and PDF buffers, open binary files and do not have a notion of lines. The use of Linum-Relative mode in such buffers makes no sense. I avoid enabling Linum-Relative mode in these buffers.

(add-hook
 'after-change-major-mode-hook
 (lambda ()
   (let ((linum-exceptions
          '(eshell-mode shell-mode term-mode geiser-repl-mode
                        image-mode pdf-view-mode)))
     (unless (member major-mode linum-exceptions)
       (linum-relative-mode)))))
(with-eval-after-load 'linum-relative
  (setq linum-relative-current-symbol ""))

Major modes

Gnuplot mode

Gnuplot mode provides syntax highlighting, indentation and a few other convenient functions for editing gnuplot scripts. Unfortunately, by default, gnuplot mode only recognizes file extensions .gp and .gnuplot as gnuplot scripts, while Vim recognizes the .gpi extension. I don't want to have to rename all my gnuplot scripts. So, I just tell gnuplot mode to recognize the .gpi extension as well.

(add-to-list 'auto-mode-alist '("\\.gpi" . gnuplot-mode))

Octave mode

Octave mode is a major mode for editing Octave programs. I set Octave mode to recognize .m files as Octave source files. Without this, .m files are opened in Objective C mode, which I have no need for.

(add-to-list 'auto-mode-alist '("\\.m" . octave-mode))

Helm

Helm has among the best fuzzy matching I've seen outside of search engines such as Google. The way it asynchronously completes commands just feels magical. It is definitely one of those must have Emacs packages. Check out this wonderfully colorful introduction to Helm.

I have rebound several standard Emacs functions to equivalent (and superior!) Helm functions. Slowly, as my use of Emacs evolves, I'll include more of Helm's features into my workflow.

For commands, both the Emacs style M-x and the Vim/Evil style : key binding require an awkward holding down of two keys at the same time. So, instead, I have bound helm-M-x to the unused and totally neglected menu key.

Screen real estate is precious. So, I've enabled Helm's autoresize feature to make it use as less space as possible on the screen. Besides, an automatically resizing window looks so dynamic and alive!

(bind-keys ("C-x C-f" . helm-find-files)
           ("C-s" . helm-occur)
           ("C-h i" . helm-info))
(bind-exwm-keys ("<menu>" . helm-M-x)
                ("s-b" . helm-mini)
                ("s-k" . kill-buffer))
(with-eval-after-load 'helm
  (helm-mode)
  (helm-autoresize-mode))

Dired

With its convenient emacsy interface and good Emacs integration, Dired is the text based file manager that finally convinced me to quit the GUI file managers I was using earlier. I spend so much time in Emacs (programming, mail, browsing, blogging, getting things done with org mode, etc.) that an Emacs based file manager is just the natural thing to have!

In Dired's file listings, I prefer seeing approximate human readable file sizes (such as 1K, 234M, 2G, etc.) instead of the number of bytes which are usually too large to be meaningfully interpreted by a human being. Hence I've added the -h switch to Dired's ls calls.

I want Dired to automatically mirror any changes to the directory caused by external programs. So, I've enabled the Auto Revert minor mode on Dired buffers.

(with-eval-after-load 'dired
  (setq dired-listing-switches "-alh")
  (add-hook 'dired-mode-hook 'auto-revert-mode)
  (add-hook 'dired-mode-hook 'dired-async-mode))

Smartparens

Smartparens is a minor mode for structured editing of parenthesis pairs. In plain English, smartparens automatically balances your parantheses, helps you easily navigate up and down your S-expressions, extend parentheses (slurping), shrink parentheses (barfing), etc. This is very important for writing lisp S-expressions, and I can't imagine coding without it.

I use evil-smartparens on top of smartparens to make smartparens play nice with Evil. I've rebound various smartparens commands to convenient Vim/Evil like key bindings.

(dolist (hook '(emacs-lisp-mode-hook
                lisp-mode-hook scheme-mode-hook))
  (add-hook hook 'smartparens-strict-mode))
(add-hook 'smartparens-enabled-hook 'evil-smartparens-mode)

(with-eval-after-load 'smartparens
  (evil-define-key 'normal smartparens-mode-map
    "l" 'sp-forward-sexp
    "h" 'sp-backward-sexp
    "L" 'sp-up-sexp
    "H" 'sp-backward-up-sexp
    "<" 'sp-backward-slurp-sexp
    ">" 'sp-forward-slurp-sexp
    "{" 'sp-forward-barf-sexp
    "}" 'sp-backward-barf-sexp)
  (require 'smartparens-config))

PDFView

PDFView mode is much more of a full-featured PDF reader compared to the built-in DocView mode. Hence, I use it to read PDFs.

While typesetting and building LaTeX documents, it is convenient to have the opened PDF file auto-refresh from disk. Hence, I have the Auto Revert minor mode enabled in the PDFView major mode.

(add-to-list 'auto-mode-alist '("\\.pdf" . pdf-view-mode))
(with-eval-after-load 'pdf-view
  (add-hook 'pdf-view-mode-hook 'auto-revert-mode))

Emacs Multimedia System

The Emacs Multimedia System (EMMS) lets you play audio/video from Emacs. The actual playing is delegated to external players. EMMS merely holds the controls, handles the media player user interface, etc.

I use EMMS primarily to play videos I have downloaded into my Videos directory. For this purpose, I have set up a handy command play-media bound to a Play/Pause media key on my keyboard. When EMMS is not playing anything, play-video prompts me for the file to start playing. The files in my Videos directory are listed as completions, though it is possible to navigate to other directories and play files from those directories. When EMMS is already playing something, play-video pauses/resumes the playing.

For the actual playing, I use mpv. The emms-player-mpv package interfaces EMMS to mpv.

(bind-exwm-keys ("<XF86AudioPlay>" . play-media))
(autoload 'play-media "emms")

(with-eval-after-load 'emms
  (require 'emms-setup)
  (emms-all)
  (emms-mode-line nil)
  (require 'emms-player-mpv)
  (add-to-list 'emms-player-list 'emms-player-mpv)
  (require 'helm)
  (defun play-media ()
    (interactive)
    (if emms-player-playing-p
        (emms-pause)
      (emms-play-url (read-file-name "Play file: " "~/Videos/")))))

Footnotes:

1

use-package also provides the use-package macro that helps you organize your various customizations in a performance oriented and tidy way. While I have outgrown it, I would still recommend it to beginners trying to grok Emacs configuaration best practices.

2

The switch-to-last-used-buffer function that I use for this, I credit to http://emacsredux.com/blog/2013/04/28/switch-to-previous-buffer/