The power of snippets
Version 1.1b17 is falling a little behind schedule, so just to signal that I’m still alive, here’s a teaser about how the snippet system has been enhanced in the upcoming beta.
2005-10-11: Added paragraph about regexp replacements performed on variables.
A snippet is a piece of text that you’d like to insert in your document, it can however include code to run at insertion time, variables (like selected text), tab stops/placeholders for missing information, and make simple transformations on the data you enter in the placeholders.
There are currently 3 ways to insert a snippet. Either select it from the Automation → Insert Snippet sub menu, press its assigned key equivalent, or type the tab trigger associated with the snippet, followed by tab.
Key equivalents and tab triggers can be restricted to a scope, and multiple snippets can share the same activation method, which results in a menu (unless the scope settles the tie). These aspects are the same for all bundle items and will not be discussed in this article.
To create a snippet, select Automation → Insert Snippet → Edit Snippets…, which will open the bundle editor, and here you can use the plus button in the lower left corner to add a new snippet to the selected bundle. It is recommended that you create a new bundle for your own customizations, e.g. called “Custom”.
Plain text
In the simplest case, you can use snippets to insert text that you don’t want to type again and again, either because you type it a lot, or because the actual text to insert is hard to remember.
For example I have snippets for the miscellaneous Apple modifier keys, so rather than go lookup the value of the cloverleaf glyph (⌘), I type command
and press tab (since that’s my tab trigger) and it expands to ⌘
. I have set the scope for this snippet to text.html
, so that it only expands in HTML documents (which includes Markdown).
If you use snippets to insert plain text, there’s only one thing you should be aware of: $
and ` are reserved characters. So if you want to insert one of these, prefix it with an escape (i.e. \$
). An escape not followed by one of these two characters will be inserted as a literal escape character.
Variables
You can insert the value of a variable by prefixing the name of the variable with $
. Variables are environment variables which are inherited from the program which launched TextMate. Additional variables can be set in Preferences → Advanced, and TextMate will also provide some variables for the current filename, selected text a.s.o., the full list is available in the Help Book. The most useful variable is probably TM_SELECTED_TEXT
. So if for example we want to create a snippet which wraps the selection in a LaTeX \textbf
command, we could do:
\textbf{$TM_SELECTED_TEXT}
If no text is selected, the variable will not be set, and nothing will be inserted. Though we can provide a default value by using this syntax: ${«variable»:«default value»}
. So the above would be:
\textbf{${TM_SELECTED_TEXT:no text was selected}}
The default value can itself contain variables or shell code. If you want the default text to contain a }
, you need to escape it. But all other characters are verbatim.
A variable also supports regular expression replacements using this syntax: ${«variable»/«regexp»/«format»/«options»}
. If the variable is not set, the replacement will be performed on the empty string. For example, to prepend a bullet to each non-empty line in the selection (and insert that) we can do:
${TM_SELECTED_TEXT/^.+$/• $0/g}
Shell code
You can use backticks to have shell code executed when the snippet is inserted. The result of running the code gets inserted into the snippet, though with the last newline in the result removed (if present). So for example to create a snippet that wraps the selection in an HTML link, where the URL of that link comes from the clipboard, we can do:
<a href="`pbpaste`">$TM_SELECTED_TEXT</a>
Since this is normal bash code, we can write a small program. E.g. we can let it verify that the clipboard contains only a single line of text like this:
<a href="`
if [[ $(pbpaste|wc -l) -eq 0 ]]
then pbpaste
else echo http://site.com/
fi
`">$TM_SELECTED_TEXT</a>
Inside shell code, the only character you need to escape is the backtick.
Tab stops
After insertion, the caret will be placed after the last character of the snippet. This is not always desirable, and we can change that by using $0
to mark where we want the caret to be. So if for example we make an HTML div-snippet, and want the caret to end between the opening and closing tags, we could make a snippet like this:
<div>
$0
</div>
Often though, we want to fill in text multiple places in the snippet, and we can provide multiple tab stops by inserting $1
-$n
. The caret will start at $1
, then when pressing tab, it will move to $2
, and $3
on next tab etc. until there are no more tab stops. If you do not explicitly set $0
, it will be at the end of the snippet.
So we could e.g. change the above to:
<div$1>
$0
</div>
This allows us to fill in an argument, and then tab on to $0
.
Placeholders
Like variables, tab stops can also have default values (and are generally referred to as placeholders, when they do). The syntax is the same: ${«tab stop»:«default value»}
. And the default value can contain both text, shell code, and other placeholders. So we can refine the above further by letting it be:
<div${1: id="${2:some_id}"}>
$0
</div>
Inserting this snippet will insert a div tag with the id argument selected, and we can then decide either to overtype the argument (e.g. delete it), and press tab again to reach $0
, or we can press tab immediately to get to the second tab stop (the value part of the argument), and edit that.
When you edit the placeholder text, any embedded tab stops will be removed.
Mirrors
There are times when you need to provide the same value several places in the inserted text, and in these situations you can re-use the tab stop to signal that you want it mirrored at that location. So for example to create a LaTeX environment with a snippet, we could do:
\begin{${1:enumerate}}
$0
\end{$1}
After inserting this snippet, enumerate
will be selected, and if we edit it, the changes will be reflected in the \end
part as well.
Transformations
There are situations where we do want our placeholder text mirrored, but it needs slight changes. For example in objective-c the setter/getter methods of the foo
instance variable looks something like this:
- (id)foo
{
return foo;
}
- (void)setFoo:(id)aValue
{
[foo autorelease];
foo = [aValue retain];
}
The problem here is that in one instance we title case foo
. We can solve this by doing a regular expression substitution on a tab stop (when mirroring it). The syntax for that is: ${«tab stop»/«regexp»/«format»/«options»}
. In the format string we can use \U
to switch to uppercase (until \E
), or \u
to make next character uppercase. So here’s how we can make a snippet for an objective-c getter/setter method:
- (${1:id})${2:foo}
{
return $2;
}
- (void)set${2/./\u$0/}:($1)aValue
{
[$2 autorelease];
$2 = [aValue retain];
}
Here $0
in the format string refers to the entire match, and has nothing to do with the tab stops.
Another feature of format strings are that they can do conditional insertions, based on a capture. The syntax for that is: (?«n»:«did match»:«didn't match»)
. If capture n did match, the left hand side is inserted, otherwise the (optional) right hand side is inserted.
We may want to use that, if we switch to a more verbose getter:
- (void)set${2/./\u$0/}:($1)aValue
{
$1 oldValue = $2;
$2 = [aValue retain];
[oldValue release];
}
The problem here is when we provide a pointer type, for example if $1 is NSString *
, the getter becomes:
- (void)setFoo:(NSString *)aValue
{
NSString * oldValue = foo;
foo = [aValue retain];
[oldValue release];
}
To avoid that extra space, we can check if the type ends with ` *, and only if it doesn't, insert a space in front of
oldValue`, here’s the result:
- (void)set${2/./\u$0/}:($1)aValue
{
${1/( \*)?$/(?1:$1: )/}oldValue = $2;
$2 = [aValue retain];
[oldValue release];
}
Back to HTML
A useful HTML snippet is “Wrap selection in tag”, it can look like this:
<${1:p}>$TM_SELECTED_TEXT</$1>
Selecting some text and then inserting this snippet, will wrap the selected text in a <p>
tag, with the p
selected, allowing us to specify another tag name (and press tab when done, to move past the entire thing).
However, if we also want to provide an attribute for the tag, that attribute will be mirrored in the end tag. Again we can turn to regexp substitution on the mirror, and change the snippet to:
<${1:p}>$TM_SELECTED_TEXT</${1/([^ ]+).*/$1/}>
What this does is, put everything up till the first space in capture register one, match the rest of the string, and substitute it all with capture register one. Effectively chopping off the attribute part of the start tag.
And back to C again
In longer functions/methods/sources I tend to insert comments of the following form:
/* ================================= */
/* = Clipboard history data source = */
/* ================================= */
So far I’ve been using a macro which turns the current line into such a comment box, but now that snippets can do transformations, I have created this neat snippet:
/* ==${1/./=/g}== */
/* = ${1:${TM_SELECTED_TEXT:Section name}} = */
/* ==${1/./=/g}== */
It inserts a comment box like the above, using the selection as the title (or “Section name” if there is no selection). After insertion, the title is selected, and if overwritten, the top and bottom lines will adapt in length to the new text.