UltiSnips is a really powerful vim plugin that boosts your productivity by saving you from typing redundant code patterns or repeated configurations. And UltiSnips provides an easy way to create and use snippets:

  1. Snippets are organized by file type: cpp.snippets for c++ and pandoc.snippets for pandoc.
  2. Any changes made to snippets will take effect after you save them. So you can create and edit snippets on the fly.

For example, writing the following c++ code can be really easy (and quick):

And You will notice a huge difference when you turn off this plugin once you get used to it. There are lots of awesome snippet plugins, such as neosnippet.

UltiSnips is more like a snippet engine while you actually need a separate plugin for your “snippet collection”. And offical document of UltiSnips uses vim-snippets as the “collection plugin”.

Installation of UltiSnips and vim-snippets

If you use vim-plug, all you need to do is to provide the repo name for UltiSnips and vim-snippets in vimrc like the following. Reload vim and run :PlugInstall.

1
2
3
4
5
6
7
" vim-plug
call plug#begin('~/.vim/plugged')
Plug 'SirVer/ultisnips'
Plug 'honza/vim-snippets'
call plug#end()"

And here is a screenshot of snippets provided by vim-snippets:

UltiSnips requirement and how it works

In order to use UltiSnips, vim needs python or python3 support, which can be checked by running

1
2
:echo has("python") " if you use python 2.7
:echo has("python3") " if you use python 3.3 or 3.4

or you could check you vim version and find out if it has python support by running

1
:version

If you saw either +python or +python3 in the output, you are good to go. My vim is compiled with python2 support, so I have +python and -python3 in the screenshot.

When vim is started, ultisnips will search all folders named UltiSnips (by default) under path $VIM and find the corresponding snippet file given the file type you have opened. And the snippets used in the example above are:

  • incc, which is expanded as include <iostream>
  • main, which is expanded as int main(...){ ... }
  • cout, which is expanded as cout << ... << endl;

UltiSnips will expand the keywords for you automatically or by a trigger key. Within each snippet, you can define a jump sequence to move cursor around and edit related content.

Expand a snippet

The default key to expand a snippet is <tab>. For example, after we enter main and hit <tab>, the word main will be replaced with a predefined snippet:

and you can use a different key to expand a snippet by changing g:UltiSnipsExpandTrigger in your vimrc.

1
let g:UltiSnipsExpandTrigger = [the key you like]

Create a simple snippet

A simple way to do this is to create a folder named UltiSnips under path $VIM to collect your customized snippets. Or you can put the folder anywhere you want and then create a link under $vim. Just for fun, let’s create a file named cpp.snippets and put the following snippet in it:

1
2
3
snippet std "use namespace std" b
using namespace std;
endsnippet

And UltiSnips will capture any changes you made when you save the snippet so there is no need to restart your vim.

A snippet is created with the following structure:

1
2
3
snippet keyword "description" option
content
endsnippet

In the snippet we just created, std is the keyword, and "use namespace std" is the description (which will be shown if you use autocomplete plugins like YouCompleteMe, neocomplete, or deoplete), and b is the option for the snippet:

  • b — snippet can only be expanded if it’s the beginning of the line
  • A — snippet will be expanded automatically
  • w — snippet can be expanded if it’s a “word” to vim (check :help iskeyword). For example, a word can be “a sequence of other non-blank characters” according to :help word.
  • i — snippet can be expanded in the middle of a word.

Other options such as r, s, t, m and their related description can be found by running:

1
:help UltiSnips

Advanced (Sort of) UltiSnips

Let’s write another c++ snippet in cpp.snippets for class definition. Save it and enter a c++ file.

1
2
3
4
5
6
7
8
9
10
11
12
snippet class "class" b
class ${1:Class}{
public:
// constructors, asssignment, destructor
$1();
$1(const $1&);
$1& operator=(const $1&);
~$1();
private:
};
endsnippet

After we expand this snippet, let’s type BinaryTree. And we can see that all fields marked as $1 are updated as we type.

In this snippet, ${1:Class} is a placeholder marked as $1 with default content Class. To define a jump sequence, we use dollar sign followed by numbers like $1, $2, $3

1
2
3
4
5
6
7
8
9
10
11
12
13
snippet class "class" b
class ${1:Class}{
public:
// constructors, asssignment, destructor
$1();
$1(const $1&);
$1& operator=(const $1&);
~$1();
$2
private:
$3
};
endsnippet

The default key to jump forward and backward is

1
2
<c-j> " jump forward
<c-k> " jump backward

After we update the snippet, expand class, and type BinaryTree, we can jump to the next two placeholders by pressing <c-j> (ctrl + j).

Visual Placeholder in UltiSnips

Visual placeholder is another powerful feature. It further reduces the amount of typing by providing you a way to reuse the existing context in the buffer. Suppose we use the following snippet to define a function in c++:

1
2
3
4
5
snippet fun "function definition" b
${2:void} ${1:name}($3){
$4
}
endsnippet

Suppose we have the following line in the buffer and want to use the following content as the function body:

1
cout << "Vim Rocks!" << endl;

This could be done in two steps:

  1. expand the keyword fun
  2. move the line and indent

Using the visual placeholder, this can be done in one step with a simple modification.

1
2
3
4
5
snippet fun "function def" b
${2:void} ${1:name}($3){
${4:${VISUAL}} // <--- visual placeholder added
}
endsnippet

Any selected content under the visual mode will be put into the visual placeholder, ${VISUAL} when the snippet is expanded. Thus, instead of moving the line and indent it, we could do

  1. select the line under visual mode and hit <TAB>
  2. expand the keyword fun

This feature is really helpful when you restructure you text a lot. For example, wrap a paragraph in environment such as enumerate, itemize when writing LaTex.

Python interpolation in UltiSnips

UltiSnips also allows users to leverage python to accomplish complex tasks. Suppose we want to insert all files in the current directory, we can write a simple python function to do this. We the following function in cpp.snippets. Here, !p means python.

1
2
3
4
5
6
7
8
global !p
def list_files():
files = []
for f in os.listdir('.'):
if f.endswith(('.cpp', '.h', '.cc')) and not f.startswith('.'):
files.append(f)
return ' '.join(files)
endglobal

Then, we could call this function when we write other snippets.

1
2
3
snippet ls "list source files" iw
`!p snip.rv = list_files()` // snip.rv means "snip return value"
endsnippet

Suppose we have two files in the folder: ultisnip.cpp and ultisnip.h.

And here is the link to the Chinese version of this post.

Great Tutorials on UltiSnips