XFN authoring in Emacs, part 1
In which I teach Emacs how to tab complete Atom and HTML link relations. If you like, you can follow along at home!
I’ve been a bad microformateer lately. I’ve been very lax in XFN-ifying links to people. For instance, consider my wife Erin. I should link to her like this:
<a rel="friend met co-resident spouse muse" href="http://erin.oconnor.cx/">Erin</a>
But usually I just write
It’s just much less effort.
Maybe some Emacs hackery is in order, to improve the likelihood
of me acually authoring with XFN. Let’s try
for XFN authoring enlightenment by making Emacs
rel values when in
(the major mode I use for authoring XML and HTML).
Tab completion of link relations
Posts in my blog backend are
represented as Atom Entry
Documents, so I write both
@rel pretty frequently.
I’d like to be able to complete for any of the relations
allowed in either place.
(It’ll be my responsibility at authoring time to remember
which is allowed where.)
The first thing to do is to collect a complete list of Atom and HTML link relations. Here’s what I’ve come up with. Note that several relations are defined in multiple places. I’ve defined these variables to hold them:
Aside: I use
ted- as a
prefix for Emacs Lisp code I write for personal use. Emacs Lisp
lacks namespaces, and it’s considered good
practice to use such a prefix. If I ever wrap up this code
into its own
xfn.el, I’ll replace
ted-link-relations to contain
all of these relations, like so (You’ll have to
'cl) to get
(defvar ted-link-relations (remove-duplicates (sort (append ted-html4-link-relations ted-html5-link-relations ted-atom-link-relations ted-xfn-link-relations ted-uf-link-relations) 'string<)) "List of Atom and HTML link relations.")
To prompt the user for a link relation with completion, you can
(defun ted-read-link-relation () "Read a link relation from the user, with completion." (interactive) (completing-read "Link relation: " ted-link-relations))
OK, that’s nice, but what I’d really like is to
prompt the user for as many relations as they’d like to
type, not just one (remember the case of Erin above).
Here’s a function which uses
ted-read-link-relation to do this:
(defun ted-read-link-relations () "Read link relations from the user until they hit RET." (interactive) (let ((relations '()) (relation (ted-read-link-relation))) (while (not (equal relation "")) (push relation relations) (setq relation (ted-read-link-relation))) (mapconcat (lambda (x) x) (sort relations 'string<) " ")))
One optimization to make at this point is to make
ted-read-link-relations stop right away if I enter
me relation. (The odds are
really good that I don’t want to enter any more
@rel values at that point.) Let’s do this by
while loop in a conditional:
(defun ted-read-link-relations () "Read link relations from the user until they hit RET." (interactive) (let ((relations '()) (relation (ted-read-link-relation))) (if (equal relation "me") relation (while (not (equal relation "")) (push relation relations) (setq relation (ted-read-link-relation))) (mapconcat (lambda (x) x) (sort relations 'string<) " "))))
We now have the low-level bits that’ll let us augment
nxml-mode to prompt for link relations
automatically. Let’s do that next.
Automatically asking for
@rel values while
Emacs has extensive facilities for the automatic insertion of code templates while editing. The way this is usually done is to define a skeleton and bind it to an abbrev. A skeleton is a special function which does the template formatting and insertion. You can read all about skeletons in the Autotype manual, or on the EmacsWiki.
Once we have such a skeleton, we’ll bind it to an abbrev. An abbrev is a short sequence of characters that, when typed, are automatically replaced by substitution text. As with skeletons, you can read all about abbrevs in the Emacs Manual or on the EmacsWiki.
Here’s a skeleton that, when invoked, will insert
rel="whatever relations the user chose"
into the current buffer:
(define-skeleton ted-rel-expand "Expand @rel." nil "rel=\"" (ted-read-link-relations) "\"")
OK, let’s make an abbrev for our new skeleton. If you
haven’t already enabled
should do so now. Here’s how I turn it on in my
(setq-default abbrev-mode t) (when (file-exists-p abbrev-file-name) (quietly-read-abbrev-file))
Here’s the actual abbrev definition:
(define-abbrev nxml-mode-abbrev-table "rel" t 'ted-rel-expand)
There’s a catch, though:
doesn’t define an abbrev table, so we’ll have to do
that ourselves. Here’s how to do that (N.B.: this
code needs to be evaluated before the call to
(unless (boundp 'nxml-mode-abbrev-table) (setq nxml-mode-abbrev-table (make-abbrev-table)))
Right, so that pretty much wraps things up. We can now tab complete link relations when authoring links in Atom and HTML. This is a piece of the XFN-authoring-zen puzzle, but it’s just a piece. Next time, I’ll teach Emacs to read XFN blogrolls. Then all I'll have to do is say "link erin" and it’ll DTRT.
Would someone please DTRT and ack?
— RMS, on numerous occasions