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
(eval `(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"))))))

I use eval to inline the template string into the function definition. I cannot define the function (defun) directly and reference the template string directly. The template is stored in the variable template according to the block definition (:var template=...). I am no expert in Emacs Lisp and Babel, but I think this means: the variable is not part of the Lisp function when it is executed. This BEGIN_SRC block is run once, and it behaves much like a template language itself, only with Lisp syntax. The template variable will be available when the source block “template” is executed; when you execute the function that’s produces by running the block, though, the variable isn’t visible there anymore. That’s why I use eval to literally put the YAML header inside the function body.

  • 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.

Receive new .