a% v_a%
a% In Visual mode, select the matching group, as specified by
b:match_words, containing the cursor. Similar to v_a[.
A [count] is ignored, and only the first character of the closing
pattern is selected.
In Vim, as in plain vi, the percent key, %, jumps the cursor from a brace,
bracket, or paren to its match. This can be configured with the 'matchpairs'
option. The matchit plugin extends this in several ways:
You can match whole words, such as "if" and "endif", not just
single characters. You can also specify a regular-expression.
You can define groups with more than two words, such as "if",
"else", "endif". Banging on the "%" key will cycle from the "if" to
the first "else", the next "else", ..., the closing "endif", and back
to the opening "if". Nested structures are skipped. Using g% goes
in the reverse direction.
By default, words inside comments and strings are ignored, unless
the cursor is inside a comment or string when you type "%". If the
only thing you want to do is modify the behavior of "%" so that it
behaves this way, you do not have to define b:match_words, since the
script uses the 'matchpairs' option as well as this variable.
See matchit-details for details on what the script does, and b:match_words
for how to specify matching patterns.
LANGUAGES: matchit-languages
Currently, the following languages are supported: Ada, ASP with VBS, Csh,
DTD, Entity, Essbase, Fortran, HTML, JSP (same as HTML), LaTeX, Lua, Pascal,
SGML, Shell, Tcsh, Vim, XML. Other languages may already have support via
the default filetype-plugins in the standard vim distribution.
To support a new language, see matchit-newlang below.
matchit-choose
Next, the script looks for a word on the current line that matches the pattern
just constructed. It includes the patterns from the 'matchpairs' option.
The goal is to do what you expect, which turns out to be a little complicated.
The script follows these rules:
Insist on a match that ends on or after the cursor.
Prefer a match that includes the cursor position (that is, one that
starts on or before the cursor).
Prefer a match that starts as close to the cursor as possible.
If more than one pattern in b:match_words matches, choose the one
that is listed first.
Examples:
Suppose you
let b:match_words = '<:>,<tag>:</tag>'
and hit "%" with the cursor on or before the "<" in "a <tag> is born".
The pattern '<' comes first, so it is preferred over '<tag>', which
also matches. If the cursor is on the "t", however, then '<tag>' is
preferred, because this matches a bit of text containing the cursor.
If the two groups of patterns were reversed then '<' would never be
preferred.
Suppose you
let b:match_words = 'if:end if'
(Note the space!) and hit "%" with the cursor at the end of "end if".
Then "if" matches, which is probably not what you want, but if the
cursor starts on the "end " then "end if" is chosen. (You can avoid
this problem by using a more complicated pattern.)
If there is no match, the cursor does not move. (Before version 1.13 of the
script, it would fall back on the usual behavior of %). If debugging is
turned on, the matched bit of text is saved as b:match_match and the cursor
column of the start of the match is saved as b:match_col.
Next, the script looks through b:match_words (original and parsed versions)
for the group and pattern that match. If debugging is turned on, the group is
saved as b:match_ini (the first pattern) and b:match_tail (the rest). If
there are backrefs then, in addition, the matching pattern is saved as
b:match_word and a table of translations is saved as b:match_table. If
there are backrefs, these are determined from the matching pattern and
b:match_match and substituted into each pattern in the matching group.
The script decides whether to search forwards or backwards and chooses
arguments for the searchpair() function. Then, the cursor is moved to the
start of the match, and searchpair() is called. By default, matching
structures inside strings and comments are ignored. This can be changed by
setting b:match_skip.
b:match_skip
If b:match_skip is defined, it is passed as the skip argument to
searchpair(). This controls when matching structures are skipped, or
ignored. By default, they are ignored inside comments and strings, as
determined by the syntax mechanism. (If syntax highlighting is turned off,
nothing is skipped.) You can set b:match_skip to a string, which evaluates to
a non-zero, numerical value if the match is to be skipped or zero if the match
should not be skipped. In addition, the following special values are
supported by matchit.vim:
s:foo becomes (current syntax item) =~ foo
S:foo becomes (current syntax item) !~ foo
r:foo becomes (line before cursor) =~ foo
R:foo becomes (line before cursor) !~ foo
(The "s" is meant to suggest "syntax", and the "r" is meant to suggest
"regular expression".)
Examples:
You can get the default behavior with
let b:match_skip = 's:comment\|string'
If you want to skip matching structures unless they are at the start
of the line (ignoring whitespace) then you can
let b:match_skip = 'R:^\s*'
Do not do this if strings or comments can span several lines, since
the normal syntax checking will not be done if you set b:match_skip.
In LaTeX, since "%" is used as the comment character, you can
let b:match_skip = 'r:%'
Unfortunately, this will skip anything after "\%", an escaped "%". To
allow for this, and also "\\%" (an escaped backslash followed by the
comment character) you can
let b:match_skip = 'r:\(^\|[^\\]\)\(\\\\\)*%'
See the $VIMRUNTIME/ftplugin/vim.vim for an example that uses both
syntax and a regular expression.
In order for matchit.vim to support a new language, you must define a suitable
pattern for b:match_words. You may also want to set some of the
matchit-configure variables, as described above. If your language has a
complicated syntax, or many keywords, you will need to know something about
Vim's regular-expressions.
The format for b:match_words is similar to that of the 'matchpairs' option:
it is a comma (,)-separated list of groups; each group is a colon(:)-separated
list of patterns (regular expressions). Commas and backslashes that are part
of a pattern should be escaped with backslashes ('\:' and '\,'). It is OK to
have only one group; the effect is undefined if a group has only one pattern.
A simple example is
:let b:match_words = '\<if\>:\<endif\>,'
\ . '\<while\>:\<continue\>:\<break\>:\<endwhile\>'
(In Vim regular expressions, /\< and /\> denote word boundaries. Thus "if"
matches the end of "endif" but "\<if\>" does not.) Then banging on the "%"
key will bounce the cursor between "if" and the matching "endif"; and from
"while" to any matching "continue" or "break", then to the matching "endwhile"
and back to the "while". It is almost always easier to use literal-strings
(single quotes) as above: '\<if\>' rather than "\\<if\\>" and so on.
Exception: If the ":" character does not appear in b:match_words, then it is
treated as an expression to be evaluated. For example,
let b:match_words = 'GetMatchWords()'
allows you to define a function. This can return a different string depending
on the current syntax, for example.
Once you have defined the appropriate value of b:match_words, you will
probably want to have this set automatically each time you edit the
appropriate file type. The recommended way to do this is by adding the
definition to a filetype-plugin file.
Tips: Be careful that your initial pattern does not match your final pattern.
See the example above for the use of word-boundary expressions. It is usually
better to use ".\{-}" (as many as necessary) instead of ".*" (as many as
possible). See /\{-. For example, in the string "label", "<.*>"
matches the whole string whereas "<.>{-}>" and "<[^>]*>" match "<tag>" and
"</tag>".
matchit-backref matchit-\1
In any group, the expressions \1
, \2
, ..., \9
(see /\1) refer to parts of the
INITIAL pattern enclosed in escaped parentheses. These are referred to as
back references, or backrefs. For example,
let b:match_words = '\<b\(o\+\)\>:\(h\)\1\>'
means that "bo" pairs with "ho" and "boo" pairs with "hoo" and so on. Note
that "\1" does not refer to the "\(h\)" in this example. If you have
"\(nested \(parentheses\)\) then "\d" refers to the d-th "\(" and everything
up to and including the matching "\)": in "\(nested\(parentheses\)\)", "\1"
refers to everything and "\2" refers to "\(parentheses\)". If you use a
variable such as s:notend
or s:sol
in the previous paragraph then remember
to count any "\(" patterns in this variable. You do not have to count groups
defined by /\%(\).
It should be possible to resolve back references from any pattern in the
group. For example,
let b:match_words = '\(foo\)\(bar\):more\1:and\2:end\1\2'
would not work because "\2" cannot be determined from "morefoo" and "\1"
cannot be determined from "andbar". On the other hand,
let b:match_words = '\(\(foo\)\(bar\)\):\3\2:end\1'
should work (and have the same effect as "foobar:barfoo:endfoobar"), although
this has not been thoroughly tested.
You can use /zero-width patterns such as /\@<= and /\zs. (The latter has
not been thouroughly tested in matchit.vim.) For example, if the keyword "if"
must occur at the start of the line, with optional white space, you might use
the pattern "\(^\s*\)\@<=if" so that the cursor will end on the "i" instead of
at the start of the line. For another example, if HTML had only one tag then
one could
let b:match_words = '<:>,<\@<=tag>:<\@<=/tag>'
so that "%" can bounce between matching "<" and ">" pairs or (starting on
"tag" or "/tag") between matching tags. Without the /\@<=, the script would
bounce from "tag" to the "<" in "</tag>", and another "%" would not take you
back to where you started.