yarik
Feb 16 2020 at 17:24 GMT
Can someone write an in-depth explanation of how to set up UltiSnips for vim and use it to create code snippets?
yarik
Feb 16 2020 at 17:50 GMT
UltiSnips is a vim plugin that makes it easy to create snippets in vim.
For example, we could define a snippet for a simple HTML link as
snippet link "Simple HTML link" b
<a href="$1">$0</a>
endsnippet
Then, when editing our HTML, we write link
followed by a tab, and this will expand to
<a href=""></a>
with the cursor set inside the href
string.
After entering the URL, we can press tab, and the cursor moves inside the tag where we can write the displayed text for the link.
After that, we exit "snippet mode".
Before seeing more complex snippets, let's see how to install UltiSnips.
We can install UltiSnips using vim pathogen as follows:
cd ~/.vim/bundle && git clone git://github.com/SirVer/ultisnips.git
To also install the default snippets, run:
cd ~/.vim/bundle && git clone git://github.com/honza/vim-snippets.git
After that, insert the following configuration in your .vimrc
file before the execute pathogen#infect()
line:
let g:UltiSnipsSnippetDirectories = ['~/.vim/UltiSnips', 'UltiSnips']
let g:UltiSnipsExpandTrigger="<tab>"
let g:UltiSnipsJumpForwardTrigger="<tab>"
let g:UltiSnipsJumpBackwardTrigger="<s-tab>"
let g:UltiSnipsEditSplit="vertical"
After closing vim and opening it again, you should be able to use snippets.
If you use a vim plugin manager other than pathogen, follow the instructions specific to that manager for installing a plugin. However, don't forget to add the above configuration in your .vimrc
file.
To add a snippet for the file type that is currently open (for example, a JavaScript snippet if the file open is a JavaScript file), run in vim
:UltiSnipsEdit
Let's define a snippet that we can use to define a function in JavaScript.
The snippet definition looks like this:
snippet func "Defines a function" b
function $1($2) {
$0
}
endsnippet
Begin by entering usnip
in INSERT
mode followed by a tab.
This should expand the snippet used for defining new snippets.
Enter the snippet name func
, then press tab, enter the description "Defines a function"
, press tab, leave the b
option without changing it, press tab, and finally enter the JavaScript snippet
function $1($2) {
$0
}
Then, save the file with :w
.
For the snippet to be available, you either need to restart vim or run
:call UltiSnips#RefreshSnippets()
Once the snippet is available, you can enter func
in a JavaScript file, followed by a tab, and this should expand to
function () {
}
with the cursor just before the opening parenthesis.
At this point, you can enter the function name, press tab, enter the parameters (if any), press tab, and finally enter the function body.
When you reach the $0
, you exit "snippet mode".
If you want to provide a default function name f
, you can do this by defining the snippet as
snippet func "Defines a function" b
function ${1:f}($2) {
$0
}
endsnippet
Let's say we want to define a snippet for a React component that is exported as the default export in a new file, we can do this as
snippet reactcomp "Starter for a new react component" b
/*
* $1.js
* ---------------
* Exports the $1 component.
*/
import React from "react";
const ${2:Component} = () => (
<${3:div}>
$0
</${3/(\w+).*/$1/}>
);
export default $2;
endsnippet
With this snippet definition, we can open a JavaScript file and type reactcomp
followed by a tab to create a new React component without much typing.
Note that the $1
appears multiple times in the snippet. This allows to keep all occurances of $1
in sync. Same goes for $2
and $3
.
Additionally, we have a substitution for $3
:
${3/(\w+).*/$1/}
This ensures that if we type h1 id="main-header"
in the first $3
, the second $3
only inserts the h1
part without the rest since in a closing tag we only need to enter the tag name.
Let's say that we have these 2 lines of JavaScript:
console.log(arguments);
return arguments[0];
and we want to make them the body of our new function.
To do so, we first need to change $0
in the func
snippet to ${0:${VISUAL}}
:
snippet func "Defines a function" b
function ${1:f}($2) {
${0:${VISUAL}}
}
endsnippet
Then, in VISUAL
mode, we can highlight the two lines that we want to be the body of our function and press tab, followed by func
, followed by another tab.
The result is that the two highlighted lines are the body of our function.
Using ${0:${VISUAL}}
still allows you to use the snippet as you would with $0
, with the additional benefit that you can insert the visually selected lines in place of the $0
.
In UltiSnips, you can create dynamic snippets by executing Python code.
Even if you don't know Python, you can still write simple Python statements in your snippets by looking at examples.
Let's say that we want to create a snippet with a comment that we insert at the beginning of every JavaScript file.
Let's call this snippet newfile
.
If we open a new example.js
file, we want to type newfile
followed by tab, and have it expand to
/*
* example.js
* ----------
* <Cursor here>
*/
with the cursor position at <Cursor here>
where we can write a comment specific to the file.
Notice how the file name in the comment got automatically filled in.
We can define the newfile
snippet as follows:
snippet newfile "Starting comment in new file" b
/*
* `!p snip.rv = snip.fn`
* `!p snip.rv = len(snip.fn) * "-"`
* ${0:${VISUAL}}
*/
endsnippet
The expressions `!p ...`
are Python interpolations.
The snip.rv
is the return value of that interpolation.
So, `!p snip.rv = snip.fn`
indicates that the return value is snip.fn
, which is the file name within which we have expanded the snippet.
In the example.js
file, snip.fn
would be example.js
.
Then, we want to have a separator made of dashes (--------
) that is as long as the file name.
We generate the separator by taking the length of snip.fn
using the len
function, and then multiply the length by the "-"
string.
Besiders rv
and fn
keys of the snip
object, there are more keys that are available in the snippet:
snip.basename
- The file name without the extension (e.g., example
for the example.js
file)snip.ft
- The file type (e.g., javascript
for a .js
file)Besides the snip
variable, there are some more useful variables that are available:
t
- Used to access the values of placeholders (e.g., t[2]
is the value of $2
)fn
- The name of the current filepath
- The absolute path to the current filematch
- The match object for the snippet regular expression (this is only available in snippets triggered by regular expressions)Let's say that we want to write a snippet that will produce 80 asterisks (***...
) when we type * 80
followed by a tab.
We can define such snippet like this:
snippet "(\S+) (\d+)" "Repeat token n times" r
`!p snip.rv = int(match.group(2)) * match.group(1)`
endsnippet
First of all, we use the r
option instead of the b
option. The r
option is used when we want the snippet to be triggered by a regular expression match.
The b
option, that we've been using so far, indicates that we simply want the snippet name to be on a line on its own with no extra text.
If we specify no option at all, then the snippet name can appear anywhere, and thus pressing tab will always expand it.
The above snippet is triggered by the (\S+) (\d+)
regular expression, which matches characters other than whitespace repeated one or more times, followed by a space, followed by digits repeated one or more times.
We use enclosing paretheses to create a capturing group, which gives us access to the specific matches.
Here, match.group(1)
corresponds to the first group in the regular expression ((\S+)
), and match.group(2)
corresponds to the second group ((\d+)
).
So, we just convert the digits from string to integer, and then multiply the integer by the token.
We can also use this to say "Hi!" 10 times. We just need to type Hi! 10
followed by a tab.
I hope that in this answer, I clearly explained how to install UltiSnips, define snippets, and use them.
However, I did not cover all the features of UltiSnips. If you want to learn more, read the official UltiSnips documentation.