Why create a “literate configuration”?

In a previous post, I explained the concept and origin of literate programming. A literate configuration is simply applying the literate programming concept to configurations files. This is useful for configuration files typically require more context around the settings and why you chose to do something in a particular manner. Sometimes this could include links to where you found the configuration settings or ‘work in progress’ settings. Another reason literate configurations are useful is because configuration files are not necessarily modified every day. When jumping back into your config to fix, add, or modify a feature it might be useful to have a short explanation to quickly catch yourself up.

For this particular post the goal is to create a literate configuration for Spacemacs. It is a heavily modded distribution of Emacs. This is mainly because I use Spacemacs myself and find having a literate configuration for it very useful. Literate configuration itself is an .org file where the weaved code is defined in code blocks. Using org-mode headings also makes it much easier to navigate your configuration file. Extracting the file code from the literate configuration, or tangling the literate configuration, will yield an elisp file.

Spacemacs is also well suited for a literate configuration since it by default loads a single .spacemacs file, which tends to steadily grow as it increasingly takes over your life. At the time of writing this my current ‘tangled’ spacemacs.el configuration (just the code) is 1500 lines long.

Correctly loading Spacemacs literate config file

When I was attempting to create a literate Spacemacs config, there were several helpful links to figure out how to tangle and generate output files from an .org file. I struggled to figure out the best way to load the file itself. There are two default locations for your Spacemacs configurations with the following loading priority:

  1. ~/.spacemacs: A single dotfile in the home directory
  2. ~/.spacemacs.d/init.el: A dotfile directory in the home directory

For a literate configuration we will need to have mulitple files, therefore I find it more orderly to go with the second option and create a ~/.spacemacs.d directory.

Initially, it seemed to make sense to create a spacemacs.org literate configuration file, and tangle directly to either .spacemacs or init.el file. It could be done this way, but Spacemacs adds custom content at the end of the init.el (or ~/.spacemacs) file in the following function:

(defun dotspacemacs/emacs-custom-settings ()
  "Emacs custom settings.
This is an auto-generated function, do not modify its content directly, use
Emacs customize menu instead.
This function is called at the very end of Spacemacs initialization."

This is where Emacs stores custom-set-variables and custom-set-faces generated during runtime. If we were to tangle our literate configuration file directly to the init.el file it would overwrite this function and variables every time it was generated. Therefore, instead the literate configuration can be tangled to an intermediate file, for example spacemacs.el, and then the init.el file can load its contents:

  • spacemacs.org: The literate configuration file
  • spacemacs.el: The tangled configuration file
  • init.el: The file that Spacemacs loads as a default configuration file.

An overview of how this works is shown in figure 1.


                       Figure 1: An overview of the concept of how to make a literate config for spacemacs that doesn’t overwrite the auto generated settings from Spacemacs. Created in Krita.

Figure 1: An overview of the concept of how to make a literate config for spacemacs that doesn’t overwrite the auto generated settings from Spacemacs. Created in Krita.

The ‘spacemacs.org’ file

This is the literate configuration file itself. When creating this you have to ensure every part of your configuration file is copied over in the code snippets. Below is the start of my configuration file. Most of these settings are not particularly important for generating the configuration file, but has served me well thus far.

#+TITLE: Spacemacs Literate User Configuration
#+STARTUP: headlines
#+STARTUP: nohideblocks
#+STARTUP: noindent
#+OPTIONS: toc:4 h:4
#+PROPERTY: header-args:emacs-lisp :comments link

An example of a code block from a part of my configuration below. The important part is the that the configuration is in the code blocks.

 ** Function start and default settings

   This is just the start of some configuration options copied directly
   over from the default configuration.

#+BEGIN_SRC emacs-lisp :tangle spacemacs.el
  (defun dotspacemacs/layers ()
    (setq-default
     dotspacemacs-distribution 'spacemacs
     dotspacemacs-enable-lazy-installation 'unused
     dotspacemacs-ask-for-lazy-installation t
     dotspacemacs-configuration-layer-path '()
#+END_SRC

What defines the code block:

#+BEGIN_SRC emacs-lisp :tangle spacemacs.el
#+END_SRC

The emacs-lisp option indicates what language the code block is written in. The :tangle spacemacs.el sets the output target file to spacemacs.el when the file is tangled. My complete spacemacs.org, with all its warts, can be found in my dotfiles repository on github.

The ‘spacemacs.el’ file

In order to generate the tangled file, spacmacs.el, the function org-bable-tangle() has to be run on the literate configuration file. For this function to be run every time you save the file, the following ‘local variables’ can be added to the bottom of spacemacs.org. This will add a function to the after-save-hook. A hook is a variable that holds a list of functions at a specific time.

 * Local Variables                                                   :ARCHIVE:
# Local Variables:
# eval: (add-hook 'after-save-hook (lambda ()(org-babel-tangle)) nil t)
# End:

The ‘init.el’ file

This is the file Spacemacs loads as the configuration. You only have to add the following line to the file.

(load-file "~/.spacemacs.d/spacemacs.el")

Spacemacs should then load your configuration and you should be up and running! As you continue to use Spacemacs, it will populate the init.el file. It will start to populate such as:

(load-file "~/.spacemacs.d/spacemacs.el")
(defun dotspacemacs/emacs-custom-settings ()
  "Emacs custom settings.
This is an auto-generated function, do not modify its content directly, use
Emacs customize menu instead.
This function is called at the very end of Spacemacs initialization."
(custom-set-variables
;; custom-set-variables was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(evil-want-Y-yank-to-eol nil)
...
...

That’s it!

Now when you change your spacemacs.org file and tangle the output to spacemacs.el it will not overwrite the variables generated by Spacemacs found in init.el. It is not that complicated, but I hope this helps someone fairly new to org-babel or Spacemacs.