First stab at literate config with Doom Emacs

· 852 words · 4 minute read

My historic pattern for descending into Emacs hell has always started with the kitchen-sink init, and the path to recovery has always involved a patient refactoring into multiple files: Some kind of “the basics,” something just for org, something for odd little quality of life things, and a quarantine file where new stuff can enjoy a probation period where I can bisect it first when something goes wrong. If I add a big chunk of functionality from a new mode, that might get its own file, too.

That has always helped me feel a little in control, at least.

I noticed Doom Emacs has a literate module in its init.el, so I did some reading. The very high level summary is that Doom lets you use literate programming principles via org-mode to build your config.el file from an org file:

  • You enclose your actual configuration code in src blocks:
#+begin_src emacs-lisp
(setq doom-font (font-spec :family "Fira Code Retina" :size 14 )
      doom-variable-pitch-font (font-spec :family "Fira Sans" :size 13))
#+end_src

… and those blocks are “tangled” into a config.el file.

You get to use all of org-mode’s affordances for document structure, so you can add headings, and your comments can just be prose:

** Base Appearance

*** Line spacing 

#+begin_src emacs-lisp
(setq-default line-spacing 0.5)
#+end_src

*** Font Settings

#+begin_src emacs-lisp
(setq doom-font (font-spec :family "Fira Code Retina" :size 14 )
      doom-variable-pitch-font (font-spec :family "Fira Sans" :size 13))
#+end_src

So this morning I moved my still-pretty-simple config.el into config.org to see what I thought.

It’s pretty cool!

The traditional ugliness of Emacs configs, for me, has always been the slow drift out of organization. Maybe for a while all the UI stuff, personalization stuff, appearance stuff is traveling together, but something makes its way in at the bottom of the file, then something else, and you’re left with a bunch of things that aren’t near their logical neighbors. There are the things you comment out that just become a big chunk of … something … it’s hard to read because they aren’t syntax highlighted anymore. There’s verbose documentation that makes it hard to scan. etc.

Literate config in Emacs allows you to bring semantically meaningful structure to the configuration file: Broad categories of options can go under headings, commentary is written as prose, and you can use all of org mode’s structure editing tools to quickly move chunks of configuration around into a more readable, logical order.

Doom’s default config.el comes with a ton of comments. I’m glad they’re there when I need them, I hate having to scroll through and past them. Moving everything into config.org let me just move the comments into a Docs heading for each section, so they stay folded away unless I need them.

I’ve got a few chunks of config that need some work. I can research how to use a given module, toss in reference links and sample snippets, and only allow certain code blocks to be compiled into my final config.el.

And I can also evaluate a src block by hitting enter on the last line of the block, so if you’re in an iterative mode, trying things out and testing them, it’s a few keystrokes less to make sure something evaluates cleanly, returns the value I was hoping for, etc.

I’ve known about literate programming in the context of org for a while. A very long while ago, when I was first playing around with web analytics reporting, I had some simple code blocks embedded in an org file that allowed me to pull basic website numbers into my org file by getting the output of a script. That could all then be exported into a status report. It was cool but also trivial. It didn’t feel like a huge quality of life improvement. This feels like something that, with very little time spent getting it into basic shape, will be more maintainable over time. It’s easy to see where to insert something in the document, so things are more likely to stay organized, and it’s easy to test.

It was very little work to set up in the Doom Emacs context:

  1. I enabled the functionality in init.el by uncommenting literate
  2. I copied my config.el file into config.org wholesale.
  3. I put every config stanza under a level 2 heading to start.
  4. Short (5 lines or fewer) comments became leading prose blocks.
  5. Long comments went into level 3 Docs subheads.
  6. I collapsed my view to headings only and moved everything related to each other into proximity of each other with org’s section up/down keys.
  7. I added top-level headings for basic config, my more extensive org mode stuff, and utility functions.

I’m still building up my Doom config, so it didn’t take long. Maybe 15 minutes to get to something much more easy to scan:

Screenshot of an org-based config file’s heading hierarchy

Sitting here thinking about it, I guess it reminds me a little of the first time I sat down and wrote a real Puppet manifest for a real purpose and not a “how does this thing work?” purpose. It felt like clarity was emerging from the writing process.

Seems like a keeper.