2017-07-02 Using yasnippet programmatically

Some time ago I was writing an Emacs function to insert a certain template into a buffer. The template had a few places where user-defined strings should be put.

My first approach was to write a template-interpolation facility for Elisp, and I will most probably blog about it some day – I learned a bit from it (spoiler: don’t use strings for heavy string manipulations, buffers are faster!). But after I revisited the issue after some time, it occured to me that it doesn’t make too much sense to first ask the user for some strings (using read-string or even completing-read) only to put it in the template with my functions when YASnippet is available!

Of course, the usual way to use YASnippet is to turn yasnippet-mode on and use TAB to expand snippets. But it turns out that it is quite easy to use it programmatically. Here’s an (artificial) example:

(yas-expand-snippet "\\begin{$1}\n$0\n\\end{$1}")

One can even use the more advanced features of YASnippet. For instance, I have a snippet much like this:

\documentclass{article}

\title{$1}
\author{$2}
%% \date{${3:$$(comment-line-if-modified)}}

\begin{document}
$0
\end{document}

(remember to escape the backslashes if you want to put that into a string!) and a function comment-line-if-modified:

(defun comment-line-if-modified ()
  "Run `comment-line' on exiting yas when the placeholder was modified."
  (when (and yas-modified-p
		 yas-moving-away-p)
	(comment-line 1)))

With this, if the user actually changed the date, the line with the date is automatically uncommented. (And you could delete-matching-lines beginning with %% after finishing with the snippet!) As you can see, you can do all sorts of stuff here. I’m planning to use it (as you might have guessed) for creating skeletons of LaTeX files, where after creating the file the program e.g. sets up a Mercurial repository and does a bunch of other things, but I’d imagine that there can be more uses for this technique.

One thing you have to remember is that after yas-expand-snippet, you have to make sure Emacs enters the command loop so that the user has a chance to actually edit anything. But this is another story; for now, let me mention that one possibility (albeit not uncontroversial) is to use recursive-edit. (Its main advantage is that it is simple. Its disadvantage is that it is potentially confusing for the user. See the relevant section of the Emacs and Elisp manuals for more info and an alternative for recursive edit.)

CategoryEnglish, CategoryBlog, CategoryEmacs