index edit log

text snippets, skeletons, templates

When editing any kind of text often you have to type the same over and over again. An example is a footer in an email or a conditional statement in a programming language. The most simple way to automate this is using the

  :abbrev command.

The next simple thing is create a mapping such as

imap \insert Hello<space>World
# more advanced example:
imap \eemail_header <c-r>="Dear ".input("name: ")."\n"<cr>

This is all managable, however for longer texts it becomse tiresome. For that reason special "snippet" or "template" plugins have been written. There are many different snippet solutions. So let's compare them:

Features:

XPTemplate, Sander's snipmate, snipmate, UltiSnips, UltiSnipsF, neo snippet, vim-template, lh-suite comparison:
featureXPTemplateSander's snipmatesnipmateUltiSnipsUltiSnipsFneo snippetvim-templatelh-suitefeature description
manual-reloadyesyesYou have to reload snippet files
automatic-reloadyesyesyesyesyesIts enough to write a snippet file, it'll be reloaded automatically
runtimepath-supportyesyesyesPick snippets by looking at &runtimepath
space-abstractionyesyesThere is a way to configure whether arguments eg in for(a; b; c) get prefixed/suffixed by spaces
can-read-snippets-filesyesyesyesyesyessnipmate is very popular. Comprehensive snippet collection can be found at vim-snippets
requires-pythonyesyes
nested placeholdersyesyesyes
info-missingyes
completion-menuyesyesyesyesyesWhen typing only part of a snippet trigger Vim shows a completion menu with all triggers matching. For some snippets this may not make sense, eg if the trigger itself contains input to for the snippet. Eg UltiSnips can use regular expression matching on triggers
regular-expression-as-triggeryesyesyes
generated-snippetsyesyesCan the snippet engine add snippets on the fly in some way?
snippet-headeryesHas a nice (extendable) header which can be used to attach additional (future) information to snippets.
dynamic-snippetsyesWhen expanded, snippets can react to settings (e.g. naming policy for getters), context (name of the current class when defining a new constructor), user choices ("What is the semantics of this new class (value, entity, ...) ?")
include-snippetsyesSnippets can include other snippets to avoid duplicating code (e.g. a `C-swith` snippet can include the `C-case` snippet), or to introduce variation points: e.g. a C header-file template can include a first template that defines file-headers for all files in the current project (copyright statements meant to be overridden in each project), and a second template that generates anti-reinclusion header gates.

Additional notes

XPTemplate: mature engine, advanced features. Creating snippets takes some effort

Sander's snipmate: original snipmate version.

snipmate: adds some features, therefore depends on additional libraries. generated-snippets implementation: You can add a new function as source returning snippets depending on anything (cursor pos, current line, whatever you want)

UltiSnipsF: forked version of UltiSnips. Its goal is to merge communities of snipmate and UltiSnips by providing most important features to both communities. However the test suite is not up to date and maybe will never be updated, because everything Marc Weber cares about works

UltiSnips will soon have a new release containing most of these changes

Update: SirVer has updated Ultisnips so that it can also read snipmate files.

neo snippet: It uses marker to implement snippet features instead of snipMate and Ultisnips. It integrates with neocomplcache/neocomplete for completion. The snippet files are compatible with snipMate. And can read snipMate snippet files.

vim-template: I don't know much about it - help fill in more information. This feature list is incomplete

lh-suite: lh-suite/mu-template is a forked version of mu-template. It has a few unlisted features:

snippet engine features by examples

This section exists to illustrate the different features you may want to have which some engines implement, others do not. Thus add future ideas, too.

Let's start with a typical intermediate snippet taken from snipmate:

snippet for
	for (${2:i} = 0; $2 < ${1:count}; $2${3:++}) {
		${4:/* code */}
	}${5}

When typing for then expanding the snippet[ ${1:count} will be selected as "count" only so that you can change the stop condition of the loop.

Then when hitting the "jump to next placeholder" key the ${2:i} will be hit defaulting to value "i". If you edit it the $2 will change in multiple places and so on.

run arbitrary code:

snippet git_version
	`system('git rev-parse HEAD')`

The `viml code` will be run, and its result will be inserted into the snippet. Different engines have slightly different syntax for this eventually.

nested placeholders (UltiSnips feature):

snippet input
<input type="${1:text}" value="${2}" name="${3}"${4: id="${5:$3}}/>${7}
endsnippet

This snippet basically behaves like the first example, however pay attention to ${4: id="${5:$3}} . When the 4th placeholder gets selected you can remove it by hitting backspace, therefore the 5th placeholder is gone, too. This way you can omit blocks of code.

regular expression trigger (UltiSnips feature):


modifying snippets on the fly. Snippet / UltiSnips allows to register your own sources of snippets. Therefore you can modify or add snippets before snipmate chooses the matching one. One use case is adding folding markers. Example taken from snipmate documentation:

  let g:commentChar = { \ 'vim': '"', \ 'c': '//', \ 'cpp': '//', \ 'sh': '#', \ 'python': '#' \}
  fun! AddFolding(text)
          return substitute(a:text,'\n'," ".g:commentChar[&ft]." \n&quot;,1).&quot;\n&quot;.g:commentChar[&amp;ft].&quot; "
  endf

  fun! SnippetsWithFolding(scopes, trigger, result)
    " hacky: temporarely remove this function to prevent infinite recursion:
    call remove(g:snipMateSources, 'with_folding')
    " get list of snippets:
    let result = snipMate#GetSnippets(a:scopes, substitute(a:trigger,'_\(\*\)\?$','\1',''))
    let g:snipMateSources['with_folding'] = funcref#Function('SnippetsWithFolding')

    " add folding:
    for k in keys(result)
      let a:result[k.'_'] = map(result[k],'AddFolding(v:val)')
    endfor
  endf

  " force setting default:
  runtime plugin/snipMate.vim
  " add our own source
  let g:snipMateSources['with_folding'] = funcref#Function('SnippetsWithFolding')

The SnippetsWithFolding is of interest: It runs the default source to get a list of snippets, then adds folding markers and returns them with '_' suffixed to the trigger. This feature can be "abused" to create snippets for functions defined in the current file. Then snippets serve a similar purpose as completion does - which is why neosnippets integrates well with neocomplcache.

spaces: should empty lines be prefixed by indenting spaces?

tags: Idea. You don't want the snippet engine ask you "found this snippet twice, which one" - if there is a way to prefer one always. Idea to solve this:

" snippets file
snippet n
tag: for_tag
	for="$1"
" UltiSnips file
snipept n
tag: for_tag
for="$1"
endsnippet

Now you should be able to say

prio: ["UltiSnips", "snipmate"]
only-first-snippet-for-each-tag?

Then a "foreach" snippet would still be shown.