Advanced Projectionist Templates

This week I migrated some of the vim tooling I use for my blog from UltiSnips to projectionist. The result is a lighter weight and a more user friendly (for me) interface.

When I bloged about my vim plugins I mentioned that I use UltiSnips solely for writing new blog posts. It always felt weird to use this incredibly powerful snippit tool to generate the boilerplate for a single file by typing fmatter<tab> every time.

Projectionist, which I also blogged about in my vim plugins post, is generally useful for me in navigating well known parts of a project. One feature of Projectionist that I’d never used before was its templating. Basically with Projectionist you define a path and you can easily open files based on it.

For this blog I have a file like this:

{
   "content/posts/*.md": {
      "type": "post"
   }
}

I can run the command :Epost station and it will open the file at content/posts/station.md. It supports tab completion out of the box and other very handy features that make more sense when programming.

Back to templates; if you run the above command and the file does not exist, a template can fill in some boilerplate for you. You define the template in the JSON as follows:

{
   "content/posts/*.md": {
      "type": "post",
      "template": [
         "---",
         "title: ",
         "date: ~~CURDATE~~",
         "tags: ",
         "guid: ~~GUID~~",
         "---"
      ]
   }
}

It’s a little noisy, but the result is that when you first open a fresh post with :Epost some-post it will have the above filled in:

---
title: 
date: ~~CURDATE~~
tags: 
guid: ~~GUID~~
---

Of course there is one major setback. With UltiSnips you can trivially insert commands in the template. Their output is then seamlessly interpolated.

I proposed as much to Tim Pope and he didn’t seem to like the idea. After a few months of pondering on it I struck upon the idea of using an autocommand to postprocess the generated content. Here was my first version:

{
   "content/posts/*.md": {
      "type": "post",
      "template": [
         "TPLTPLTPL",
         "---",
         "title: ",
         "date: ~~CURDATE~~",
         "tags: ",
         "guid: ~~GUID~~",
         "---"
      ]
   }
}

Note especially the use of TPLTPLTPL as a signal that a template is to be processed.

Then I wrote the following vimscript:

function! ExpandTemplate()
   if getline(1) == 'TPLTPLTPL'
      %s/\~\~CURDATE\~\~/\=systemlist("date +%FT%T")[0]/ge
      %s/\~\~GUID\~\~/\=systemlist("uuidgen")[0]/ge
      1g/TPLTPLTPL/d
   endif
endfunction

au BufReadPost * call ExpandTemplate()

I figured out the autocommand to hook into by reading the source of Projectionist and the rest is just regular vimscript.

Finally, I decided I could make it slightly simpler with a patch to Projectionist. With the patch in place the code simply becomes:

function! ExpandTemplate()
   %s/\~\~CURDATE\~\~/\=systemlist("date +%FT%T")[0]/ge
   %s/\~\~GUID\~\~/\=systemlist("uuidgen")[0]/ge
endfunction

au User ProjectionistApplyTemplate call ExpandTemplate()

And the TPLTPLTPL line can go away.


I felt good to finally be able to scratch this itch. The user interface is simpler and the code is lighter and I have one fewer plugin that I rarely use.


If you’d like to learn more about vim, I can recommend a few excellent books. I first learned how to use vi from Learning the vi and Vim Editors. The new edition has a lot more information and spends more time on Vim specific features. It was helpful for me at the time, and the fundamental model of vi is still well supported in Vim and this book explores that well.

Second, if you really want to up your editing game, check out Practical Vim. It’s a very approachable book that unpacks some of the lesser used features in ways that will be clearly and immediately useful. I periodically review this book because it’s such a treasure trove of clear hints and tips.

Third and finally, if you want to really grok the guts of advanced vim, to write a plugin for example, you should really check out Learn Vimscript the Hard Way by Steve Losh. I expect to reread it two or three more times. I got the PDF version so I could read it while offline.

Posted Mon, Oct 16, 2017