Add Blog Post Text Template Expansion to Emacs with Org Babel


In my quest of total immersion into Emacs, I am trying to write blog posts in Emacs instead of TextMate. That means my TextMate macros are gone for good, including insertion of YAML header information. On this very blog and on, I used to type cthead and zkhead respectively, then expanded the YAML header with tab. TextMate’s great template feature even allows you to specify multiple points inside the templates to jump to by tabbing forward. Emacs works a bit differently, so I adapted the templates to request information up front instead and fill in the values.

What I needed for the Zettelkasten blog was:

  • title
  • creation date (auto-generated)
  • author (Sascha or I)
  • tags
  • preview image, if any

I found a way to specify template code in Emacs Org mode using Babel: Babel is used for literate programming, and I wanted to embed the template as a template. I could also write this in pure Emacs Lisp, but I don’t enjoy maintaining multi-line strings and favor heredocs whereever possible. So with Babel’s named BEGIN_EXPORT, I exported the template with the name ct/blog-header-template:

#+NAME: ct/blog-header-template
title: "%s"
created_at: %s
kind: article
author: %s
tags: [  ]

The %s designates a printf-style placeholder. So the template would allow to be formatted with 3 strings. I didn’t add the image because I usually add, rename, and manage these much later in the process.

Now I can reference this named template block in other Babel blocks using source blocks’s header arguments. This time I want to have access to the template string as a variable, and Babel allows you to pass this in as context. This is much like any other template language, be it Ruby’s Erb, Handlebars.js or whatever, only it works on a per-block basis, not per-file.

#+BEGIN_SRC emacs-lisp :var template=ct/blog-header-template :lexical t
(defun ct/blog-insert-header ()
    (format template
           (read-string "Title: ")
           (format-time-string "%Y-%m-%d %H:%M:%S +0100")
           (ido-completing-read "Author: " '("christian" "sascha")))))

Update 2021-02-09: I used to put the whole function definition in (eval \(defun …)) to access the variable in the function body. But reader [Aravind SV]( pointed out that I can get the same effect with a :lexical t` parameter to the source block. I used eval to literally put the YAML header inside the function body, but this is much simpler.

The function now does this:

  • format behaves like printf: it takes a template string and a variadic list of arguments to pass into the template.
  • read-string prompts the user for a title in the minibuffer interactively
  • format-time-string produces an ISO-formatted string that complies to YAML’s date serialization
  • ido-completing-read is a variant of completing-read (which you could also use) to show a set of options, much like a dropdown or picker of author names

The template for this blog is similar but lacks the author picker, which I found most interesting.

I put the header template definition and the resulting function in the Org file that’s being executed during startup. It behaves like the usual .emacs.d/init.el file, but you can organize the file with Org outlines and embed Emacs Lisp code like the one shown above to be executed. This is called literate Emacs initialization. I prefer this over separating my initialization routines and macros into multiple init files because the Org outlines amaze me.

Some day I might add a tag picker, too, aggregating all tags I use on the blog from a local cache file or something. While I’d love that for the sake of technical prowess, it’s not important or even that useful.