ċaŋt̄e waṡt̄eya nap̄e ċiyuze

23 May 2021

Literate Emacs Configuration

Getting Started With Org Babel

So you've been hacking with Emacs for some time, and your dot-emacs has grown to be a bit hairy. There's some lines in there copy-pasted from somewhere, but are they even being used anymore? What exactly were you thinking when you wrote that macro you don't quite understand anymore. There's got to be a better way!

There is indeed, a better way. In this article, I'd like to share how to get started with Org Babel's literate programming features. It can get quite complex, but because you're writing literate Emacs Lisp, all the tools are at your disposal immediately, assuming your version of Emacs is relatively modern. I know offhand that this works for Emacs 26 and up. If you find otherwise, please let me know.

Quick and Dirty: Migrating an Existing Configuration

Assuming your configuration lives in the default .emacs file in your home directory, the quickest way to get started with a literate config is to just put the whole thing in an org source block and go from there. This will allow you to incrementally organize existing code without breaking your setup. That being said, it is worth keeping a backup of the original working configuration since, as you probably already know, even changing the order of certain lines of Elisp can break something.

Go ahead and create a file like configuration.org somewhere. I have mine in a dotfiles git repo. Within that file, create an elisp source block and paste the contents of your .emacs file into it. It should look something like this:

#+begin_src emacs-lisp
  ... your config here ...
#+end_src 

Now replace the contents of your .emacs file with this elisp:

(org-babel-load-file "/full/path/to/your/configuration.org")

Feel free to use expand-file-name or whatever else makes it easy to get the correct path to the org file.

Now restart Emacs (actually, if your configuration is complex at all, please read the next section before you do), and the magic will happen! The TLDR is that org-babel-load-file "tangles" out all the source blocks in the org file given and puts them in an .el file of the same name. Then, the usual load-file function is used to load the lisp. Additionally, it will only tangle the code if the org file has been touched more recently than the tangled elisp file. This was a source of frustration for me, as I had inadvertently been tangling into the wrong place and comparing against a symlink to my org configuration which didn't get touched when I updated anything.

Some Housekeeping

Its quite possible something broke for you when moving everything into the org source block.

Package Initialization

Many configurations must start off by configuring their package manager of choice before beginning to call other elisp functions. You may use a packaged version of Org rather than the built in one, but here we're directly using an org function to load the config. Which version of org is actually running? This article doesn't intend to solve all these problems for you, I will assume you already a solution in your existing configuration. I just want to point out that there may be crucial pieces of it that might need to be moved into the "initialize" step of configuration. This is probably familiar if you use init.el with your .emacs.d directory rather than putting everything in .emacs.

I can say that in my current setup, I have the bootstrapping code for the Straight package manager as the first thing that runs when starting up. If I want to ensure I'm not using the built in Org, I need to set it up with the package manager before calling org-babel-load-file.

Move Custom-Set to its Own File

The other piece to take care of is managing the custom-set-* sections that Emacs will add to your init file. Its likely you already have some copied over into the org source block earlier. The problem is now the init file has changed and as is, you may end up with two copies of the custom-set-* sections. The solution is to use custom-file variable to put all the custom variables into their own file. So in your new init file (the one that loads the org file) add something like this:

(setq custom-file "~/.my-custom.el")
(load custom-file)

If you have anything from your existing configuration in the org source block, cut and paste it into this custom file and you should be good to go. Any new custom configuration will be written to this file. Note: my current setup loads the custom config before loading the org file. I recall running into some issues the other way around, but apparently I didn't keep notes about why.

Setup a Quick Reload

One downside to migrating to an org file for your config is that it makes it a bit more cumbersome to edit and reload your configuration. You can still evaluate elisp expressions in org and once you've broken things into blocks, execute whole chunks of your configuration at once. But sometimes you just want to reload the whole thing. In this case, you can reach for org-babel-load-file once again to ensure your latest changes get tangled out and loaded. I recommend adding something like this to your config and binding it to a key:

(defun my-reload-config ()
  "Tangles and reloads my literate config with `org-babel-load-file'"
  (interactive)
  (org-babel-load-file
   (expand-file-name "~/my-configuration.org")))

Org-anizing Your Config

Hopefully now you can get started by arranging parts of your dotfile into top level headings and subtrees. My org file currently looks something like this:

* Packages
  sets up a list of packages and installs them with straight.el
* Emacs Editing Configurations
  setup xah-fly-keys and other basic editing/keybinds
* Programming Languages
  configure programming languages and adjacent packages
* Emacs Applications
  things like email, completion, magit, projects, emms, etc
* Emacs GUI
  this comes last because its most likely to be order dependent
  configure gui preferences, themes, modeline, fonts, etc

I plan to continue this series with a more involved example in the future. Stay tuned!

Tags: emacs org-mode literate-programming configuration
Other posts
Other posts
Creative Commons License
Content by Grant Shoshin Shangreaux is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.