2. An UltiSnips guide for LaTeX workflows
You might be interested in this article for two reasons:
- As a standalone guide to writing snippets with the UltiSnips plugin (this article is LaTeX-heavy, but it is applicable to any language).
- As part two in a seven-part series explaining how to use the Vim or Neovim text editors to efficiently write LaTeX documents.
There is also a LuaSnip version of this article.
Contents of this article
- What snippets do
- Getting started with UltiSnips
- Watch the screencasts!
- Writing Snippets
- (Subjective) practical tips for fast editing
- Tip: A snippet for writing snippets
What snippets do
Snippets are templates of commonly used code (for example the boilerplate code for typical LaTeX environments and commands) inserted into text dynamically using short (e.g. two- or three-character) triggers. Without wishing to overstate the case, good use of snippets is the single most important step in the process of writing LaTeX efficiently and painlessly. Here is a simple example:
Getting started with UltiSnips
This tutorial will use the UltiSnips plugin, which is the most mature out of the menagerie of Vim snippet plugins. If you use Neovim, note that UltiSnips’s support of Neovim is “best-effort only”. Don’t let this discourage you—although I have since switched to LuaSnip, both I and many other Neovim users have daily driven Ultisnips and Neovim without any issues, and things will probably be fine for you, too. If you use regular Vim, you should be fine in any case.
Neovim users: there is also a LuaSnip version of this article if you prefer.
Installation
Install UltiSnips like any other Vim plugin using your plugin installation method of choice. Because the UltiSnips plugin uses Python…
- you need a working installation of Python 3 on your system (see
:help UltiSnips-requirements
) - your Vim must be compiled with the
python3
feature enabled—you can test this with:echo has("python3")
, which will return1
ifpython3
is enabled and0
otherwise. Note that Neovim comes withpython3
enabled by default.
UltiSnips is a snippet engine only and intentionally ships without snippets—you have to write your own or use an existing snippet database.
The canonical source of existing snippets is GitHub user honza
’s vim-snippets
repository.
Whether you download someone else’s snippets, write your own, or use a mixture of both, you should know:
- where the text files holding your snippets are stored on your local file system, and
- how to write, edit, and otherwise tweak snippets to suit your particular needs, so you are not stuck using someone else’s without the possibility of customization.
Both questions are answered in this article.
First steps: snippet trigger and tabstop navigation keys
After installing UltiSnips you should configure…
- the key you use to trigger (expand) snippets, which is set using the global variable
g:UltiSnipsExpandTrigger
, - the key you use to move forward through a snippet’s tabstops, which is set using
g:UltiSnipsJumpForwardTrigger
, and - the key you use to move backward through a snippet’s tabstops, which is set with
g:UltiSnipsJumpBackwardTrigger
.
For orientation, here is an example configuration, which you would place the code in your vimrc
or init.vim
:
" This code should go in your vimrc or init.vim
let g:UltiSnipsExpandTrigger = '<Tab>' " use Tab to expand snippets
let g:UltiSnipsJumpForwardTrigger = '<Tab>' " use Tab to move forward through tabstops
let g:UltiSnipsJumpBackwardTrigger = '<S-Tab>' " use Shift-Tab to move backward through tabstops
Explanation: this code would make the <Tab>
key trigger snippets and navigate forward through snippet tabstops (yes, UltiSnips lets you use the same key for both expansion and tabstop navigation), and make the key combination <Shift>
+<Tab>
navigate backward through tabstops.
In the above GIF, I am actually using jk
as the g:UltiSnipsJumpForwardTrigger
key.
I find this home-row combination more efficient than <Tab>
, but it takes some getting used to;
scroll down to the (Subjective) practical tips for fast editing at the bottom of this article for more on using jk
as a jump-forward key and similar tips.
See :help UltiSnips-trigger-key-mappings
for official documentation of trigger keys.
For fine-grained control one can also work directly with functions controlling expand and jump behavior; for more information on this see :help UltiSnips-trigger-functions
.
For most users just setting the three global trigger key variables, as in the example above, should suffice.
A home for your snippets
You store snippets in text files with the .snippets
extension.
The file’s base name determines which Vim filetype
the snippets apply to.
For example, snippets inside the file tex.snippets
would apply to files with filetype=tex
.
If you want certain snippets to apply globally to all file types, place these global snippets in the file all.snippets
, which is documented towards the bottom of :help UltiSnips-how-snippets-are-loaded
.
By default, UltiSnips expects your .snippet
files to live in directories called UltiSnips
, which, if you wanted, you could place anywhere in your Vim runtimepath
.
You can use folder names other than the default UltiSnips
, too—the snippet directory name is controlled with the global variable g:UltiSnipsSnippetDirectories
.
From :help UltiSnips-how-snippets-are-loaded
,
UltiSnips will search each
runtimepath
directory for the subdirectory names defined ing:UltiSnipsSnippetDirectories
in the order they are defined.
For example, to use MySnippets
as a snippet directory, you would place the following Vimscript in your vimrc
or init.vim
:
" Use both `UltiSnips` and `MySnippets` as snippet directories
let g:UltiSnipsSnippetDirectories=["UltiSnips", "MySnippets"]
UltiSnips would then load *.snippet
files from all UltiSnips
and MySnippets
directories in your Vim runtimepath
.
Possible optimization: if, like me, you use only a single predefined snippet directory and don’t need UltiSnips to scan your entire runtimepath
each time you open Vim (which can slow down Vim’s start-up time), set g:UltiSnipsSnippetDirectories
to use a single, absolute path to your snippets directory, for example
let g:UltiSnipsSnippetDirectories=[$HOME.'/.vim/UltiSnips'] " using Vim
let g:UltiSnipsSnippetDirectories=[$HOME.'/.config/nvim/UltiSnips'] " using Neovim
This behavior is documented in :help UltiSnips-how-snippets-are-loaded
.
(The .
joining $HOME
and '/.vim/UltiSnips'
is the Vimscript string concatenation operator.)
Snippet folders
You might prefer to further organize filetype
-specific snippets into multiple files of their own.
To do so, make a folder named with the target filetype
inside your snippets directory.
UltiSnips will then load all .snippet
files inside this folder, regardless of their basename.
Again, this behavior is documented in :help UltiSnips-how-snippets-are-loaded
.
As a concrete example, a selection of my UltiSnips directory looks like this:
${HOME}/.vim/UltiSnips/ # Vim
${HOME}/.config/nvim/UltiSnips/ # Neovim
├── all.snippets
├── markdown.snippets
├── python.snippets
└── tex
├── delimiters.snippets
├── environments.snippets
├── fonts.snippets
└── math.snippets
Explanation: I have a lot of tex
snippets, so I prefer to further organize them in a dedicated directory, while a single file suffices for all
, markdown
, and python
.
Watch the screencasts!
Quite a few years ago now, Holger Rapp, the author of UltiSnips, created four screencasts demonstrating the plugin’s features:
- Episode 1: What are snippets and do I need them?
- Episode 2: Creating Basic Snippets
- Episode 3: What’s new in version 2.0
- Episode 4: Python Interpolation
They’re old but gold, and pack an impressively thorough demonstration of UltiSnips’s capabilities into about 20 minutes of video. I strongly suggest your watch them—you will find many of the features described in this article covered from a different perspective in the screencasts.
Writing Snippets
TLDR: create a {filetype}.snippets
file in your UltiSnips
directory (e.g. tex.snippets
) and write your snippets inside this file using the syntax described in :help UltiSnips-basic-syntax
.
Anatomy of an UltiSnips snippet
The general form of an UltiSnips snippet is:
snippet {trigger} ["description" [options]]
{snippet body}
endsnippet
The trigger
and snippet body
are mandatory, while "description"
(which should be enclosed in quotes) and options
are optional; options
can be included only if a "description"
is also provided.
The keywords snippet
and endsnippet
define the beginning and end of the snippet.
See :help UltiSnips-authoring-snippets
for the relevant documentation.
An apology about syntax highlighting
Please excuse the sub-optimal syntax highlighting of UltiSnips snippet code blocks throughout this article.
This website is written with Jekyll and GitHub Pages, which use the rogue
Ruby Gem for syntax highlighting.
At the time of writing, rogue
does not support the UltiSnips snippet language
(see here for the current list of rogue
lexers),
and so the snippet code looks meh.
For lack of a better option, I shuffle between plain text and generic shell-script highlighting
(which at least highlights comments),
neither of which are particularly satisfactory.
I might or might not get around to fixing this by just writing and contributing an UltiSnips lexer for rouge
;
for the time being, we’ll have to put up less-than-perfect snippet highlighting.
Options
You’ll need to use a few options to get the full UltiSnips experience.
All options are clearly documented at :help UltiSnips-snippet-options
, and I’ll summarize here only what is necessary for understanding the snippets that appear later in this document.
Based on my (subjective) experience, with a focus on LaTeX files, here are some good options to know:
-
A
enables automatic expansion, i.e. a snippet with theA
option will expand immediately aftertrigger
is typed, without you having to press theg:UltiSnipsExpandTrigger
key. If you’re aiming for real-time LaTeX, using well thought-out automatic snippet expansion will dramatically increase your efficiency—more on this in (subjective) practical tips for fast editing. -
r
allows the use of regular expansions in the snippet’s trigger. More on this in the section on regex snippet triggers. -
b
expands snippets only iftrigger
is typed at the beginning of a line—this is a useful option when writing snippets for LaTeX environments, which are usually defined at the beginning of a new line. -
i
(for “in-word” expansion) expands snippets regardless of wheretrigger
is typed. (By default snippets expand only iftrigger
begins a new line or is preceded by whitespace.)
Assorted snippet syntax rules
-
UltiSnips supports comments, which start with
#
and can be used to document snippets (see:help UltiSnips-basic-syntax
for reference). -
According to
:help UltiSnips-character-escaping
, the characters'
,{
,$
, and\
need to be escaped by prepending a backslash\
. The actual rules of what needs to be escaped when are a little more subtle (see UltiSnips Issue #1437 for context) and more lenient in practice, so you can often get away with using'
,{
, and\
in snippet bodies without escaping them. - Including the line
extends filetype
anywhere in a
*.snippets
file will load all snippets fromfiletype.snippets
into the snippets file containingextends filetype
. As an example use case from:help UltiSnips-basic-syntax
, you might useextends c
inside acpp.snippets
file, since C++ could use many snippets from C. - The line
priority {N}
, whereN
is an integer number (e.g.priority 5
), placed anywhere in.snippets
file on its own line will set the priority of all snippets below that line toN
. When multiple snippets have the sametrigger
, only the highest-priority snippet is expanded. Usingpriority
can be useful to override global snippets defined inall.snipets
. Ifpriority
is not specified anywhere in a file, the implicit value ispriority 0
. You can read more about thepriority
keyword in:help UltiSnips-basic-syntax
.
Tabstops
Tabstops are predefined positions within a snippet body to which you can move by pressing the key mapped to g:UltiSnipsJumpForwardTrigger
.
Tabstops allow you to efficiently navigate through a snippet’s variable content while skipping the positions of static content.
You navigate through tabstops by pressing, in insert mode, the keys mapped to g:UltiSnipsJumpForwardTrigger
and g:UltiSnipsJumpBackwardTrigger
.
Since that might sound vague, here is an example of jumping through the tabstops for figure path, caption, and label in a LaTeX figure
environment:
Paraphrasing from :help UltiSnips-tabstops
:
-
You create a tabstop with a dollar sign followed by a number, e.g.
$1
or$2
. -
Tabstops should start at
$1
and proceed in sequential order, i.e.$2
,$3
, and so on. -
The
$0
tabstop is special—it is always the last tabstop in the snippet no matter how many tabstops are defined. If$0
is not explicitly defined, the$0
tabstop is implicitly placed at the end of the snippet.
As far as I’m aware, this is a similar tabstop syntax to that used in the popular IDE Visual Studio Code.
Some example LaTeX snippets
For orientation, here are two examples: one maps tt
to the \texttt
command and the other maps ff
to the \frac
command.
Note that (at least for me) the snippet expands correctly without escaping the \
, {
, and }
characters as suggested in :help UltiSnips-character-escaping
(see the second bullet in Assorted snippet syntax rules).
snippet tt "The \texttt{} command for typewriter-style font"
\texttt{$1}$0
endsnippet
snippet ff "The LaTeX \frac{}{} command"
\frac{$1}{$2}$0
endsnippet
Here are the above \texttt{}
and \frac{}{}
snippets in action:
Useful: tabstop placeholders
Placeholders are used to enrich a tabstop with a description or default text.
The syntax for defining placeholder text is ${1:placeholder}
.
Placeholders are documented at :help UltiSnips-placeholders
.
Here is a real-world example I used to remind myself the correct order for the URL and display text in the hyperref
package’s href
command:
snippet hr "The hyperref package's \href{}{} command (for url links)"
\href{${1:url}}{${2:display name}}$0
endsnippet
Here is what this snippet looks like in practice:
Useful: mirrored tabstops
Mirrors allow you to reuse a tabstop’s content in multiple locations throughout the snippet body.
In practice, you might use mirrored tabstops for the \begin
and \end
fields of a LaTeX environment.
Here is an example:
The syntax for mirrored tabstops is what you might intuitively expect: just repeat the tabstop you wish to mirror.
For example, following is the code for the snippet shown in the above GIF; note how the $1
tabstop containing the environment name is mirrored in both the \begin
and \end
commands:
snippet env "New LaTeX environment" b
\begin{$1}
$2
\end{$1}
$0
endsnippet
The b
options ensures the snippet only expands at the start of line; see the Options section for review of common UltiSnips options.
Mirrored tabstops are documented at :help UltiSnips-mirrors
.
Useful: the visual placeholder
The visual placeholder lets you use text selected in Vim’s visual mode inside the content of a snippet body. Here is the how to use it:
- Create and save an UltiSnips snippet that includes the
${VISUAL}
keyword (explained below). - Use Vim to open a file in which you want to trigger the snippet.
- Use Vim’s visual mode to select some text.
- Press the Tab key (or more generally the key stored in the
g:UltiSnipsExpandTrigger
variable, which is Tab by default). The selected text is deleted, stored by UltiSnips in memory, and you are placed into Vim’s insert mode. - Type the trigger to expand the previously-written snippet that included the
${VISUAL}
keyword. The snippet expands, and the text you had selected in visual mode appears in place of the${VISUAL}
keyword in the snippet body.
As an example, here is a snippet for the LaTeX \textit
command that uses a visual placeholder to make it easer to surround text in italics:
snippet tii "The \textit{} command for italic font"
\textit{${1:${VISUAL:}}}$0
endsnippet
And here is what this snippet looks like in action:
Indeed (as far as I know) the most common use case for the visual placeholder is to quickly surround existing text with a snippet (e.g. to place a sentence inside a LaTeX italics command, to surround a word with quotation marks, surround a paragraph in a LaTeX environment, etc.).
You can have one visual placeholder per snippet, and you specify it with the ${VISUAL}
keyword—this keyword is usually (but does not have to be) integrated into tabstops.
Of course, you can still use any snippet that includes the ${VISUAL}
keyword without going through the select-and-Tab procedure described above—you just type the snippet trigger and use it like any other snippet.
The visual placeholder is documented at :help UltiSnips-visual-placeholder
and explained on video in the UltiSnips screencast Episode 3: What’s new in version 2.0; I encourage you to watch the video for orientation, if needed.
Dynamically-evaluated code inside snippets
It is possible to add dynamically-evaluated code to snippet bodies (UltiSnips calls this “code interpolation”).
Shell script, Vimscript, and Python are all supported.
Interpolation is covered in :help UltiSnips-interpolation
and in the UltiSnips screencast Episode 4: Python Interpolation.
I will only cover two examples I subjectively find to be most useful for LaTeX:
-
making certain snippets expand only when the trigger is typed in LaTeX math environments, which is called custom context expansion, and
-
accessing characters captured by a regular expression trigger’s capture group.
Custom context expansion and VimTeX’s syntax detection
UltiSnips’s custom context features (see :help UltiSnips-custom-context-snippets
) give you essentially arbitrary control over when snippets expand, and one very useful LaTeX application is expanding a snippet only if its trigger is typed in a LaTeX math context.
As an example of why this might be useful:
- Problem: good snippet triggers tend to interfere with words typed in regular text.
For example,
ff
is a great choice for a\frac{}{}
snippet, but you wouldn’t wantff
to expand to\frac{}{}
in the middle of the word “offer”, for example. - Solution: make
ff
expand to\frac{}{}
only in math context, where it won’t conflict with regular text. (Note that thefrac
expansion problem can also be solved with a regex snippet trigger, which is covered in the next section.)
You will need GitHub user lervag
’s VimTeX plugin for math-context expansion.
(I cover VimTeX in much more detail in the fourth article in this series.)
The VimTeX plugin, among many other things, provides the user with the function vimtex#syntax#in_mathzone()
, which returns 1
if the cursor is inside a LaTeX math zone (e.g. between $ $
for inline math, inside an equation
environment, etc…) and 0
otherwise.
This function isn’t explicitly mentioned in the VimTeX documentation, but you can find it in the VimTeX source code at vimtex/autoload/vimtex/syntax.vim
.
You can integrate VimTeX’s math zone detection with UltiSnips’s context
feature as follows:
# include this code block at the top of a *.snippets file...
# ----------------------------- #
global !p
def math():
return vim.eval('vimtex#syntax#in_mathzone()') == '1'
endglobal
# ----------------------------- #
# ...then place 'context "math()"' above any snippets you want to expand only in math mode
context "math()"
snippet ff "This \frac{}{} snippet expands only a LaTeX math context"
\frac{$1}{$2}$0
endsnippet
My original source for the implementation of math-context expansion: https://castel.dev/post/lecture-notes-1/#context.
Regex snippet triggers
For our purposes, if you aren’t familiar with them, regular expressions let you (among many other things) implement conditional pattern matching in snippet triggers.
You could use a regular expression trigger, for example, to do something like “make _
expand to a subscript snippet like _{$1}$0
, but only if the _
trigger immediately follows an alphanumeric character”.
A formal explanation of regular expressions falls beyond the scope of this article, and I offer the examples below in a “cookbook” style in the hope that you can adapt the ideas to your own use cases. Regex tutorials abound on the internet; if you need a place to start, I recommend Corey Schafer’s tutorial on YouTube.
-
This class of triggers suppresses expansion following alphanumeric text and permits expansion after blank space, punctuation marks, braces and other delimiters, etc…
snippet "([^a-zA-Z])trigger" "Expands if 'trigger' is typed after characters other than a-z or A-Z" r `!p snip.rv = match.group(1)`snippet body endsnippet snippet "(^|[^a-zA-Z])trigger" "Expands on a new line or after characters other than a-z or A-Z" r `!p snip.rv = match.group(1)`snippet body endsnippet # This trigger suppresses numbers, too snippet "(^|[\W])trigger" "Expands on a new line or after characters other than 0-9, a-z, or A-Z" r `!p snip.rv = match.group(1)`snippet body endsnippet
This is by far my most-used class of regex triggers. Here are some example use cases:
- Make
mm
expand to$ $
(inline math), including on new lines, but not in words like “communication”, “command”, etc…snippet "(^|[^a-zA-Z])mm" "Inline LaTeX math" rA `!p snip.rv = match.group(1)`\$ ${1:${VISUAL:}} \$$0 endsnippet
Note that the dollar signs used for the inline math must be escaped (i.e. written
\$
instead of just$
) to avoid conflict with UltiSnips tabstops, as described in:help UltiSnips-character-escaping
. - Make
ee
expand toe^{}
(Euler’s number raised to a power) after spaces,(
,{
, and other delimiters, but not in words like “see”, “feel”, etc…snippet "([^a-zA-Z])ee" "e^{} supercript" rA `!p snip.rv = match.group(1)`e^{${1:${VISUAL:}}}$0 endsnippet
- Make
ff
expand tofrac{}{}
but not in words like “off”, “offer”, etc…snippet "(^|[^a-zA-Z])ff" "\frac{}{}" rA `!p snip.rv = match.group(1)`\frac{${1:${VISUAL:}}}{$2}$0 endsnippet
The line
`!p snip.rv = match.group(1)`
inserts the regex group captured by the trigger parentheses back into the original text. Since that might sound vague, try omitting`!p snip.rv = match.group(1)`
from any of the above snippets and seeing what happens—the first character in the snippet trigger disappears after the snippet expands.
- Make
-
This class of triggers expands only after alphanumerical characters (
\w
) or the characters}
,)
,]
, and|
.snippet "([\w])trigger" "Expands if 'trigger' is typed after 0-9, a-z, and A-Z" r `!p snip.rv = match.group(1)`snippet body endsnippet # Of course, modify the }, ), ], and | characters as you wish snippet "([\w]|[\}\)\]\|])trigger" "Expands after 0-9, a-z, A-Z and }, ), ], and |" r `!p snip.rv = match.group(1)`snippet body endsnippet # This trigger suppresses expansion after numbers snippet "([a-zA-Z]|[\}\)\]\|])trigger" "Expands after a-z, A-Z and }, ), ], and |" r `!p snip.rv = match.group(1)`snippet body endsnippet
I don’t use this one often, but here is one example I really like. It makes
00
expand to the_{0}
subscript after letters and closing delimiters, but not in numbers like100
:snippet "([a-zA-Z]|[\}\)\]\|'])00" "Automatic 0 subscript" rA `!p snip.rv = match.group(1)`_{0} endsnippet
Here is the above snippet in action:
Combined with math-context expansion, these two classes of regex triggers cover the majority of my use cases and should give you enough to get started writing your own.
Note that you can do much fancier stuff than this.
See the UltiSnips documentation or look through the snippets in vim-snippets
for inspiration.
Tip: Refreshing snippets
The function UltiSnips#RefreshSnippets
refreshes the snippets in the current Vim instance to reflect the contents of your snippets directory.
Here’s an example use case:
-
Problem: you’re editing
myfile.tex
in one Vim instance, make some changes totex.snippets
in a separate Vim instance, and want the updates to be immediately available inmyfile.tex
without having to restart Vim. -
Solution: call
UltiSnips#RefreshSnippets
using:call UltiSnips#RefreshSnippets()
.
This workflow comes up regularly if you use snippets often, and I suggest writing a key mapping in your vimrc
to call the UltiSnips#RefreshSnippets()
function, for example
" Use <leader>u in normal mode to refresh UltiSnips snippets
nnoremap <leader>u <Cmd>call UltiSnips#RefreshSnippets()<CR>
In case it looks unfamiliar, the above code snippet is a Vim key mapping, a standard Vim configuration tool described in much more detail in the series’s final article, 7. A Vimscript Primer for Filetype-Specific Workflows.
(Subjective) practical tips for fast editing
I’m writing this with math-heavy LaTeX in real-time university lectures in mind, where speed is crucial; these tips might be overkill for more relaxed use cases. In no particular order, here are some useful tips based on my personal experience:
-
Use automatic completion whenever possible. This technically makes UltiSnips use more computing resources—see the warning in
:help UltiSnips-autotrigger
—but I am yet to notice a perceptible slow-down on modern hardware. For example, I regularly use 150+ auto-trigger snippets on a 2.5 GHz, dual-core, third-gen i5 processor and 8 gigabytes of RAM (typical, even modest specs by today’s standards) without any problems. -
Use short snippet triggers. Like one-, two-, or and maybe three-character triggers.
-
Repeated-character triggers offer a good balance between efficiency and good semantics. For example, I use
ff
(fraction),mm
(inline math), andnn
(new equation environment). Althoughfrac
,$
, andeqn
would be even clearer,ff
,mm
, andnn
still get the message across and are also much faster to type.Use math-context expansion and regular expressions to free up short, convenient triggers that would otherwise conflict with common words.
-
Use ergonomic triggers on or near the home row. Depending on your capacity to develop muscle memory, you can dramatically improve efficiency if you sacrifice meaningful trigger names for convenient trigger locations. I’m talking weird combinations of home row keys like
j
,k
,l
,s
,d
, andf
that smoothly roll off your fingers. For example,sd
,df
,jk
, andkl
, if you can get used to them, are very convenient to type and also don’t conflict with many words in English or Romance languages.Here are two examples I use all the time:
-
I first define the LaTeX command
\newcommand{\diff}{\ensuremath{\operatorname{d}\!}}
in a system-wide preamble file, then access it with the following snippet:snippet "([^a-zA-Z0-9])df" "\diff (A personal command I universally use for differentials)" rA `!p snip.rv = match.group(1)`\diff endsnippet
This
df
snippet makes typing differentials a breeze, with correct spacing, upright font, and all that. Happily, in this case usingdf
for a differential also makes semantic sense.You can see the
\diff
snippet playing a minor supporting role as the differential in this variation of the fundamental theorem of calculus:As a side note, using a
\diff
command also makes redefinition of the differential symbol very easy—for example to adapt an article for submission to a journal that uses italic instead of upright differentials, one could just replace\operatorname{d}\!
with\,d
in the command definition instead of rummaging through LaTeX source code changing individual differentials. -
I use the following snippet for upright text in subscripts—the trigger makes no semantic sense, but I got used to it and love it.
snippet "([\w]|[\}\)\]\|])sd" "Subscript with upright text" rA `!p snip.rv = match.group(1)`_{\mathrm{${1:${VISUAL:}}}}$0 endsnippet
This snippet triggers after alphanumeric characters and closing delimiters, and includes a visual placeholder.
Please keep in mind: I’m not suggesting you should stop what you’re doing, fire up your Vim config, and start using
sd
to trigger upright-text subscripts just like me. The point here is just to get you thinking about using the home-row keys as efficient snippet triggers. Try experimenting for yourself—you might significantly speed up your editing. Or maybe this tip doesn’t work for you, and that’s fine, too.
-
-
Try using
jk
as yourg:UltiSnipsJumpForwardTrigger
key, i.e. for moving forward through tabstops. The other obvious choice is the Tab key, but I found the resulting pinky reach away from the home row to be a hindrance in real-time LaTeX editing.Of course
jk
is two key presses instead of one, but it rolls of the fingers so quickly that I don’t notice a slowdown. (And you don’t havejk
reserved for exiting Vim’s insert mode because you’ve remapped Caps Lock to Escape on a system-wide level and use that to exit insert mode, right?)
Tip: A snippet for writing snippets
The following snippet makes it easier to write more snippets.
To use it, create the file ~/.vim/UltiSnips/snippets.snippets
, and inside it paste the following code:
snippet snip "A snippet for writing Ultisnips snippets" b
`!p snip.rv = "snippet"` ${1:trigger} "${2:Description}" ${3:options}
$4
`!p snip.rv = "endsnippet"`
$0
endsnippet
This will insert a snippet template when you type snip
, followed by the snippet trigger key stored in g:UltiSnipsExpandTrigger
, at the beginning of a line in a *.snippets
file in insert mode.
Here’s what this looks like in practice:
The use of `!p snip.rv = "snippet"`
needs some explanation—this uses the UltiSnips Python interpolation feature, described in the section on dynamically-evaluated code inside snippets—to insert the literal string snippet
in place of `!p snip.rv = "snippet"`
.
The naive implementation would be to write
# THIS SNIPPET WON'T WORK---IT'S JUST FOR EXPLANATION!
snippet snip "A snippet for writing Ultisnips snippets" b
snippet ${1:trigger} "${2:Description}" ${3:options}
$4
endsnippet
$0
endsnippet
but this would make the UltiSnips parser think that the line snippet ${1:trigger}...
starts a new snippet definition, when the goal is to insert the literal string snippet ${1:trigger}...
into another file.
In any case, this problem is specific to using the string snippet
inside a snippet, and most snippets are much easier to write than this.
The original writing, images, and animations in this series are licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.