I think you can get the same result as in the video with this code:
${1/(".*)|.*/(?1:comment => )/}${1:"some comment"}${1/(".*)|.*/(?1:,
)/}
It can be broken down into 3 parts:
┌ mirror of tabstop n°1
│ ┌ substitution
┌─┤┌─────────────────────────┤
(A) ${1/(".*)|.*/(?1:comment => )/}
┌ tabstop n° 1
│ ┌ placeholder text,
│ │ which fills the tabstop as long as you don't replace it
┌─┤┌─────────────┤
(B) ${1:"some comment"}
┌ mirror of tabstop n°1
│ ┌ beginning of substitution
┌─┤┌─────────────┤
(C) ${1/(".*)|.*/(?1:,
)/}
└─┤
└ end of substitution
(B)
is the real tabstop. The one in which you can insert an arbitrary comment. (A)
and (C)
are just mirrors of (B)
. But, contrary to a simple mirror, which would simply re-insert the original tabstop at the desired position (and which here you would write as $1
), these mirrors perform a substitution on the original tabstop.
To avoid any confusion later, here are 3 kinds of ${some_number}
token which are going to be used later:
┌───────────────────────────┬──────────────────────────────────────────────┐
│ $1 │ a tabstop, if it's the first in the snippet │
│ │ a mirror of the 1st tabstop otherwise │
├───────────────────────────┼──────────────────────────────────────────────┤
│ ${1:hello} │ a tabstop, whose placeholder text is `hello` │
├───────────────────────────┼──────────────────────────────────────────────┤
│ ${1/pattern/replacement/} │ a mirror of the 1st tabstop │
│ │ in which you substitute the text │
│ │ matched by `pattern` with `replacement` │
└───────────────────────────┴──────────────────────────────────────────────┘
Here's how (A)
works.
Purpose:
if you insert some text beginning with a double quote in the tabstop, the mirror should prepend it with comment =>
. Otherwise, it shouldn't insert anything.
Suppose that when you jump to the tabstop (B)
, you write "hello world"
, and suppose you wrote (A)
as a simple mirror $1
. You would get:
snippet test
$1${1:"some comment"}
endsnippet
→
"some comment""some comment"
└────────────┤└────────────┤
│ └ tabstop
└ mirror
But that's not how (A)
was written earlier:
${1/(".*)|.*/(?1:comment => )/}
Here, you perform a substitution which replaces the text matched by the pattern (".*)|.*
with the replacement (?1:comment => )
.
The pattern consists of 2 branches, the first one being captured by the parentheses so that you can check whether it was matched in the replacement part of the substitution via ?1
.
The first branch describes some text beginning with a double quote: ".*
,
while the 2nd one describes any text: .*
The replacement part checks whether the 1st capturing group is not empty (i.e. “Does the text you wrote in the 1st tabstop begin with a double quote?”). If it's not, it's replaced with comment =>
, otherwise nothing is inserted. Nothing is inserted because there's no 2nd :
. IOW, these 2 replacements are equivalent:
(?1:comment => )
(?1:comment => :)
└┤ └─────────┤└┤
│ │ └ if no, don't insert anything
│ └ if yes, replace it with `comment => `
└ is the tabstop matched by the 1st capturing group?
Note that the 2nd branch in the pattern (|.*
) is important. It asks UltiSnips to remove the whole mirror if the original tabstop doesn't begin with a double quote.
And note that the parentheses in the replacement are also important. They tell UltiSnips that it's not a literal string, but a conditional expression which must be evaluated. They are similar to the \=
token which you may use in the replacement part of a substitution command in Vim (see :h :s\=
).
Here's how (C)
works.
Purpose:
if you insert some text beginning with a double quote in the tabstop, the mirror should append it with a comma, a newline, and some indentation on the next line. Otherwise, it shouldn't insert anything.
Functionally, (C)
is identical to (A)
: a mirror whose replacement part checks the contents of the original tabstop to decide what it should insert. The only difference is in the contents of the replacement part. The latter contains here a comma, a newline, and some indentation.
Since it contains a newline, the code of the mirror is written on 2 lines:
newline inside replacement
v
${1/(".*)|.*/(?1:,
)/}
You may find the code a little unreadable, and be tempted to move the newline outside the replacement:
newline outside replacement
v
${1/(".*)|.*/(?1:,)/}
…
But if you do, when you'll remove the tabstop, the whole line won't be removed: the newline and the indentation will remain. You have to include it in the replacement, so that its presence depends on the first capturing group being non-empty, and so that it's removed when you decide that the tabstop is useless.
Here's a full snippet which should implement the behavior you observed in the video:
snippet user "demo" bm
group { "${1:username}":
ensure => ${2:present},
uid => ${3:123},
gid => ${4:123},
${5/(".*)|.*/(?1:comment => )/}${5:"some comment"}${5/(".*)|.*/(?1:,
)/}home => ${6:"/home/$1"},
managehome => ${7:false},
require => ${8/./\u$0/}["${8:group}"],
}
endsnippet
At least it does seem to behave like in the video on my machine:
For more information, see :h Ultisnips-replacement-string
.