Why UltiSnips?

When it comes to snippets, Vim has a somewhat large number of available plugins. Not necessarily the most popular, but definitely the most mentioned seems to be snipMate. Now, snipMate is a great plugin: it’s minimalistic, it has simple snippets syntax and it does its work really really well. But it doesn’t do much, and judging from the lack of activity in the official repository, it doesn’t look like it will learn to do more any time soon (although, to be fair, there is some activity on the many forks on github).

To be more specific, let’s take a look at snipMate’s own docs. “Disadvantages” section lists some of the features snipMate lacks: $0, nested placeholders, multiple-line placeholders and regex transformations of variables. These are great examples of functions that usually don’t get in the way, but may really come in handy when you need them.

UltiSnips is a Vim plugin that, while being as easy to use as snipMate, is much more feature rich and, along with many unique features, implements everything listed above.

Getting UltiSnips

You can download UltiSnips from the Vim website or the official bzr repository on launchpad. Installation is quite straightforward and is explained on the script page.

If you’re using pathogen and git submodules to manage your Vim plugins, there’s the vim-scripts github repository and official github mirror.

UltiSnips is written mostly in python and therefore requires Vim compiled with python support. This means that it may not work where snipMate did and is expected to be somewhat slower. In practice, I haven’t experienced any slowdowns and UltiSnips feels as fast and snappy as any snippet plugin I’ve used.

By default, UltiSnips loads .snippets files from UltiSnips directories in your runtimepath.

Features

UltiSnips documentation covers everything the plugin may do, but I’ll try to highlight the most interesting features here.

As I’ve mentioned, UltiSnips supports multi-line and nested placeholders, $0 (last tabstop) and placeholder text transformations. Most of the syntax for placeholders is the same as TextMate’s.

Other notable features include:

  • Ability to add filetype-independent snippets.
  • Support for Vim dotted filetype syntax.
  • Ability to “include” snippet files.
  • Command to show the list of available snippets.
  • Support for multi-word and regular expression triggers.

Apart from this UltiSnips has two extremely powerful tools: “options” and interpolation.

Options

Options, among other things, allow you to specify for each snippet whether it will be expanded only at the beginning of a line, in the middle of the word, at word boundary or not. This, combined with the option to overwrite all previously defined snippets with the same trigger, allows you to use the same trigger to expand different snippets in different conditions.

Interpolation

Interpolation allows you to execute arbitrary code and insert it’s output in your snippets. While snipMate allowed you to call a Vim function, UltiSnips supports shell code, VimScript and python code. There’s also a way to create standalone python functions for use inside python code blocks.

Usage examples

A list of features sounds really nice, but sometimes it’s hard to imagine how all these things may be useful. In this section I’ll try to demonstrate few small examples of some of the UltiSnips features. Apart from giving you some ideas about how each feature my be used, they also might serve as additional documentation of the snippet syntax.

Different beginning-of-line snippet

If you’ve ever used Django ORM (or any other ORM that requires you to describe model fields) you might be familiar with the following syntax for field definition:

name = models.CharField(max_length=255)

Now, if you want to write a snippet char for inserting this line, you have two options:

  1. Expand char trigger to ${1:FIELDNAME} = models.CharField(${2:...})
  2. Expand char trigger to models.CharField(${1:...})

The first one works great at the beginning of line, but is cumbersome if you’ve already typed the desired field name either by mistake or because you haven’t decided on the field type yet. The second definition saves a bit less time and won’t allow you to define a default field name in case you need one.

Now, the good news is, as you might have guessed from the header, it is possible to define both and make UltiSnips use the first one only on the beginning of line and the second one everywhere else. To do this, we start with defining the shorter snippet:

snippet char "char field"
models.CharField(${1:...})
endsnippet

Then, we define the beginning-of-line snippet:

snippet char "char field" !b
${1:FIELDNAME} = models.CharField(${2:...})
endsnippet

As you can see, the second snippet has two characters !b after the description. These are snippet options: b means that snippet should be expanded only at the beginning of a line and ! overwrites all previously defined snippets with the same trigger. Without ! there would be two possible expansions for char at the beginning of a line so UltiSnips would show a list of available snippets before expansion. And since beginning-of-line snippets can’t be used after you’ve typed a field name, the short snippet remains the only available option there and will be expanded without further questions. It is important that the beginning-of-line snippet is defined after the short snippet, since ! only overwrites previously defined snippets.

Inserting current date

This is a simple example of interpolation. Let’s define a snippet date that will insert the current date:

snippet date
`!v strftime("%Y-%m-%d")`
endsnippet

Backticks indicate interpolation and !v means that we’re using vimscript. strftime is the name of Vim function that returns date and time in the specified format. If you want this snippet to be available in files of any type, you can define it inside all.snippets file (analogous to _.snippets file in snipMate) in one of the directories in snippet search path.

Documenting function arguments

There is an advanced example of interpolation usage in python.snippets file bundled with UltiSnips. smart class and smart def snippets use python interpolation to insert a special line into function docstring for each function argument. I won’t explain the snippet here, but together with the box snippet from all.snippets it shows how much can be achieved with python interpolation in UltiSnips. I haven’t made much use of it yet, but it seems that such snippets may be a viable alternative for some types of Vim plugins.

More snippet examples can be found in snippets bundled with UltiSnips.

Porting snipMate snippets

UltiSnips comes with a nice collection of snippets, but if you’ve been using snipMate for a long time, there’s a chance that you’ve grown accustomed to snippets that aren’t readily available in UltiSnips. The good news is that, at the moment, pretty much every snippet can be converted from snipMate to UltiSnips format in few easy steps.

The main difference in snippet file syntax is that snipMate uses indentation to find the end of the snippet text, while UltiSnips requires and explicit endsnippet line at the end. Overcoming this difference is easy: remove leading indentation from snippet text and add endsnippets line at the end of the snippet, converting

snippet example
        example snippet text

to

snippet example
example snippet text
endsnippet

The second common difference is the snippet description text, which should be quoted in UltiSnips: snippet try Try/Except should be converted to snippet try "Try/Except".

At last, if you have snippets that call VimScript functions, you should either prepend them with !v to indicate VimScript interpolation (strftime("%Y-%m-%d") should be converted to !v strftime("%Y-%m-%d")) or rewrite them using one of the other UltiSnips interpolation types.

This should cover most of the differences and make the transition painless. There’s a chance that some non-trivial snippets may require some additional changes, but the above steps were enough for everything I’ve tried to convert. If you’re converting snippets from _.snippets file, don’t forget that you should rename it to all.snippets to make snippets available regardless of the filetype in UltiSnips.

UPDATE: It looks like the latest version of UltiSnips comes with a script for automatic snippet conversion from both snipMate and TextMate formats.

Conclusion

UltiSnips documentation contains a lot more information about what UltiSnips can and can’t do and you should probably consult it if you have any questions.

blog comments powered by Disqus