Wednesday, 17 March 2021
Summary: I have recently rethought the way I
configure Emacs. In this text I explain why I have replaced
use-package
with my own configuration macro
and went back from using a literate Org mode file to a regular
init.el
.
Playing with Emacs is certainly an unusualy hobby, but I like it. From time to time, that is. Just like with any hobby, it’s not fun if you have to do it. So one important aspect is always having a stable configuration I can play with, but don’t have to worry about.
This wasn’t always given. I recently found a “old” configuration of mine from around
July of 2015, which was to my knowledge a few months after I started
using Emacs. It is easy to recognzie that I had no idea what I was
doing. Just consider indicate-empty-lines
: I first disable
indicate-empty-lines via customize, toggle it, if it was previously
disabled (in effect turning it on), and then activate it again using
setq-default
.
In my case, most of it was copied from random blogs, fora or wikis. My guess is that this is the most common kind of configuration there is. You start with a few lines a friend or colleges might have recommend, and add whatever is necessary with some vague conception of causality between the code and what Emacs actually does.
On the spectrum between a ball of mud and a diamond, this approach probably resembles a stack of paper: It does the job, won’t break if you don’t touch it but a breeze might make if fall over, and it isn’t easy to fix if afterwards.
Some time later I decided to clean things up, as I had heard a lot of
people liked John Wiegley’s use-package
.
In case you are unfamiliar with the package, it basically provides a
macro that simplifies common tasks: Installing packages, configuring
variables, binding commands, managing hooks, etc.
Around Feburary of 2018, I made a second step: Using org-mode
to write a literate configuration. The
idea is to have a configuration that is text-first, code-second. And
instead of your regular init.el
, you’d just “tangel”
(extract) out the code and evaluate it.
Eventually I started using Git, to keep my configuration under
version control. One advantage is that I can measure how my
conf.org
grew over time:
I regularly exported my configuration, the last export from April 2020 can be found here.
About a year ago I got fed up with all of this. It might have been because I had some spare time on my hands, but I decided to restructure everything again, now with more Elisp experience than I had before.
use-package
The first change was dropping use-package
. This was
mainly motivated by it’s internal complexity, partially by it’s
restrictiveness. The package has changed a lot, and requires a lot of
code to maintain backwards compatibility and syntactic flexibility. A
priority I don’t care about it speed, as long as nothing is slowed down.
Another annoyance is the difficulty adding new keywords. But it is still
a great idea for structuring a configuration and making it easier to
automatically install everything you need.
After a few experiments I managed to create my own alternative,
simply called setup
. Instead of the keyword approach that
requires normalization, it just defines a set of local macros that can
be used in a setup
form. This drastically simplifies the
implementation, as most of the functionality is reduced to macro
expansion.
(I apologize for the advertising) Here are a few examples from my current configuration:
(setup shellappend display-buffer-alist)
(:option ("\\`\\*shell\\*\\(?:<[[:digit:]]+>\\)?\\'"
'(
(display-buffer-reuse-window
display-buffer-same-window)))let ((shell-key "C-c s"))
(
(:global shell-key shell) (:bind shell-key bury-buffer)))
This appends a rule to display-buffer-alist
. Then I
binds a global key and a mode-local (the mode is automatically guessed
from the feature name) to some commands. Note that let
is
just a regular Elisp let
, and shell-key
a
regular lexical variable.
(setup (:package modus-themes)
(:only-if (display-graphic-p))
(:option modus-themes-fringes 'subtle
modus-themes-mode-line '3dt
modus-themes-variable-pitch-ui
modus-themes-region 'bg-only)t)
(load-theme 'modus-operandi "C-c t" modus-themes-toggle)) (:global
This installs Modus
Themes, but only if this is being evaluated by a graphical Emacs
session. It then configures a few user options using
customize-set-variable
. The theme is loaded and finally a
global keybinding is installed.
(setup (:package go-mode)"go")
(:needs "goimports")
(:option gofmt-command
(:local-hook before-save-hook gofmt-before-save)"go build")
(:local-set compile-command (:hook subword-mode))
This last one configures go-mode
, a major mode for the
Go programming language. A single
option is set, a hook and a variable are configured to be modified
whenever go-mode
is loaded, and finally a function is added
to go-mode-hook
.
Of course this is not a 1:1 replacement for use-package
,
but it has managed to provide a very satisfactory replacement for my
needs.
For those interested in trying the package out for yourself, version 0.1 can be found on GNU ELPA. I have to mention Stefan Monnier’s name at this point, to thank him for his invaluable input and suggestions on how to improve both the user-interface and the implementation.
org-mode
The idea of using org-mode
is to explain what you are
configuring, as is done with literate programming. And by using Org, you
also get simple folding, easy exporting and the ability to link sections
for free.
But after using it for over two years, I had come to the conclusion that it is usually more of a gimmick than a feature. I say “usually”, because there are exceptions. The most prominent example would probably be Protesilaos Stavrou’s configuration. He goes into great detail to explain his options and decisions.
Most literate configurations I see don’t even try. All you see is header, code block, header, code block, …. While I tried better, it didn’t make a lot of sense to just copy the manual in describing what a hook or user option does. The higher complexity, the overhead and the loss being able to jump to definitions doesn’t seem to be worth it.
It turned out that in my case, a simple
~/.emacs.d/init.el
was totally sufficient. I didn’t need
the force myself into explaining what I am doing.
outline-minor-mode
has replaced Erg’s structuring folding
functionality.
Personally I don’t think I have lost any functionality or advantage
that org-mode
provided.
There were a few other changes I have made (switching to default completion, trying to prefer ELPA over MELPA, making more use of customize, auto-configuring local packages) that are not that interesting here. The main intention here was to explain what I think I did wrong, and how I managed to “improve” it.
I’ll be uploading my configuration from time to time here. I have decided to stop
publicly publishing my init.el, as I believe it incentives copy-pasting
snippets without putting thought into what is being changed.