Tagging blog posts from Emacs
First, I apologise in advance (to all 3 of you subscribed) if you got an avalanche of old new posts in your RSS feed. I’ve just gone through and tagged a lot of posts.
What’s most interesting, at least for me, was that I did it en-masse via Emacs. This blog is a set of static files generated by Jekyll Bootstrap, as I have written about previously. A post is simply a file with a bit of YAML meta-data up the top, optionally including tags applying to that post:
---
layout: post
title: "Sample yaml front-matter"
tags: [SomeTag,AnotherTag]
---
Body of the post...
To simplify the repetitive work involved I wrote some elisp to take a bunch of files, query for the relevant tags, and add them to the front matter:
(defun mh/tag-posts (tags)
"Apply tags (specified interactively) to the files marked in
dired, merging with existing tags. Assumes the use of
jekyll-bootstrap, ie with the `---' delimited yaml front-matter
at the beginning."
(interactive (list (read-from-minibuffer "Tag(s), comma-separated: ")))
(let ((tags (split-string tags "[, ]+")))
(labels
((do-tags (f)
(with-current-buffer (find-file-noselect f)
(save-excursion
(goto-char 0)
(if (re-search-forward "^tags: \\[\\([[:alnum:], ]*\\)\\]" nil t)
(let* ((existing-tags (split-string (match-string 1) "[, ]+"))
(combined (union existing-tags tags))
(new-tags (mapconcat 'identity combined ",")))
;; replace-match wasn't working for some reason:
(goto-char (match-beginning 1))
(delete-region (match-beginning 1) (match-end 1))
(insert new-tags))
(progn
(goto-char 0)
(forward-line 1)
(re-search-forward "^---" nil t) ; find second occurence, assuming first is on line 1
(goto-char (match-beginning 0))
(insert (format "tags: [%s]\n" (mapconcat 'identity tags ","))))))
(save-buffer))))
(mapcar #'do-tags (dired-get-marked-files)))))
The guts are in the last line: I mark files using
dired
in Emacs, then invoke mh/tag-posts
, which applies a function to
every marked file. This function then just has to take care of a few
corner cases such as the tag already being included (by using union
from the cl
common lisp emulation library to avoid repetition), and
when there’s no tags: []
entry at all, in which case it must add
one.