Initial commit
commit
d383b7f11d
|
@ -0,0 +1,51 @@
|
||||||
|
" Maintainer: Lars H. Nielsen (dengmao@gmail.com)
|
||||||
|
" Last Change: January 22 2007
|
||||||
|
|
||||||
|
set background=dark
|
||||||
|
|
||||||
|
hi clear
|
||||||
|
|
||||||
|
if exists("syntax_on")
|
||||||
|
syntax reset
|
||||||
|
endif
|
||||||
|
|
||||||
|
let colors_name = "wombat"
|
||||||
|
|
||||||
|
|
||||||
|
" Vim >= 7.0 specific colors
|
||||||
|
if version >= 700
|
||||||
|
hi CursorLine guibg=#2d2d2d
|
||||||
|
hi CursorColumn guibg=#2d2d2d
|
||||||
|
hi MatchParen guifg=#f6f3e8 guibg=#857b6f gui=bold
|
||||||
|
hi Pmenu guifg=#f6f3e8 guibg=#444444
|
||||||
|
hi PmenuSel guifg=#000000 guibg=#cae682
|
||||||
|
endif
|
||||||
|
|
||||||
|
" General colors
|
||||||
|
hi Cursor guifg=NONE guibg=#656565 gui=none
|
||||||
|
hi Normal guifg=#f6f3e8 guibg=#242424 gui=none
|
||||||
|
hi NonText guifg=#808080 guibg=#303030 gui=none
|
||||||
|
hi LineNr guifg=#857b6f guibg=#000000 gui=none
|
||||||
|
hi StatusLine guifg=#f6f3e8 guibg=#444444 gui=italic
|
||||||
|
hi StatusLineNC guifg=#857b6f guibg=#444444 gui=none
|
||||||
|
hi VertSplit guifg=#444444 guibg=#444444 gui=none
|
||||||
|
hi Folded guibg=#384048 guifg=#a0a8b0 gui=none
|
||||||
|
hi Title guifg=#f6f3e8 guibg=NONE gui=bold
|
||||||
|
hi Visual guifg=#f6f3e8 guibg=#444444 gui=none
|
||||||
|
hi SpecialKey guifg=#808080 guibg=#343434 gui=none
|
||||||
|
|
||||||
|
" Syntax highlighting
|
||||||
|
hi Comment guifg=#99968b gui=italic
|
||||||
|
hi Todo guifg=#8f8f8f gui=italic
|
||||||
|
hi Constant guifg=#e5786d gui=none
|
||||||
|
hi String guifg=#95e454 gui=italic
|
||||||
|
hi Identifier guifg=#cae682 gui=none
|
||||||
|
hi Function guifg=#cae682 gui=none
|
||||||
|
hi Type guifg=#cae682 gui=none
|
||||||
|
hi Statement guifg=#8ac6f2 gui=none
|
||||||
|
hi Keyword guifg=#8ac6f2 gui=none
|
||||||
|
hi PreProc guifg=#e5786d gui=none
|
||||||
|
hi Number guifg=#e5786d gui=none
|
||||||
|
hi Special guifg=#e7f6da gui=none
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,406 @@
|
||||||
|
*matchit.txt* Extended "%" matching
|
||||||
|
|
||||||
|
For instructions on installing this file, type
|
||||||
|
:help matchit-install
|
||||||
|
inside Vim.
|
||||||
|
|
||||||
|
For Vim version 6.3. Last change: 2007 Aug 29
|
||||||
|
|
||||||
|
|
||||||
|
VIM REFERENCE MANUAL by Benji Fisher
|
||||||
|
|
||||||
|
*matchit* *matchit.vim*
|
||||||
|
|
||||||
|
1. Extended matching with "%" |matchit-intro|
|
||||||
|
2. Activation |matchit-activate|
|
||||||
|
3. Configuration |matchit-configure|
|
||||||
|
4. Supporting a New Language |matchit-newlang|
|
||||||
|
5. Known Bugs and Limitations |matchit-bugs|
|
||||||
|
|
||||||
|
The functionality mentioned here is a plugin, see |add-plugin|.
|
||||||
|
This plugin is only available if 'compatible' is not set.
|
||||||
|
You can avoid loading this plugin by setting the "loaded_matchit" variable
|
||||||
|
in your |vimrc| file: >
|
||||||
|
:let loaded_matchit = 1
|
||||||
|
|
||||||
|
{Vi does not have any of this}
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
1. Extended matching with "%" *matchit-intro*
|
||||||
|
|
||||||
|
*matchit-%*
|
||||||
|
% Cycle forward through matching groups, such as "if", "else", "endif",
|
||||||
|
as specified by |b:match_words|.
|
||||||
|
|
||||||
|
*g%* *v_g%* *o_g%*
|
||||||
|
g% Cycle backwards through matching groups, as specified by
|
||||||
|
|b:match_words|. For example, go from "if" to "endif" to "else".
|
||||||
|
|
||||||
|
*[%* *v_[%* *o_[%*
|
||||||
|
[% Go to [count] previous unmatched group, as specified by
|
||||||
|
|b:match_words|. Similar to |[{|.
|
||||||
|
|
||||||
|
*]%* *v_]%* *o_]%*
|
||||||
|
]% Go to [count] next unmatched group, as specified by
|
||||||
|
|b:match_words|. Similar to |]}|.
|
||||||
|
|
||||||
|
*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.
|
||||||
|
|
||||||
|
MODES: *matchit-modes* *matchit-v_%* *matchit-o_%*
|
||||||
|
|
||||||
|
Mostly, % and related motions (|g%| and |[%| and |]%|) work just like built-in
|
||||||
|
|motion| commands in |Operator-pending| and |Visual| modes. However, you
|
||||||
|
cannot make these motions |linewise| or |characterwise|, since the |:omap|s
|
||||||
|
that define them start with "v" in order to make the default behavior
|
||||||
|
inclusive. (See |o_v|.) In other words, "dV%" will not work. The
|
||||||
|
work-around is to go through Visual mode: "V%d" will work.
|
||||||
|
|
||||||
|
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-plugin|s in the standard vim distribution.
|
||||||
|
|
||||||
|
To support a new language, see |matchit-newlang| below.
|
||||||
|
|
||||||
|
DETAILS: *matchit-details* *matchit-parse*
|
||||||
|
|
||||||
|
Here is an outline of what matchit.vim does each time you hit the "%" key. If
|
||||||
|
there are |backref|s in |b:match_words| then the first step is to produce a
|
||||||
|
version in which these back references have been eliminated; if there are no
|
||||||
|
|backref|s then this step is skipped. This step is called parsing. For
|
||||||
|
example, "\(foo\|bar\):end\1" is parsed to yield
|
||||||
|
"\(foo\|bar\):end\(foo\|bar\)". This can get tricky, especially if there are
|
||||||
|
nested groups. If debugging is turned on, the parsed version is saved as
|
||||||
|
|b:match_pat|.
|
||||||
|
|
||||||
|
*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 |backref|s 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 |backref|s, 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|.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
2. Activation *matchit-activate*
|
||||||
|
|
||||||
|
You can use this script as a plugin, by copying it to your plugin directory.
|
||||||
|
See |add-global-plugin| for instructions. You can also add a line to your
|
||||||
|
|vimrc| file, such as >
|
||||||
|
:source $VIMRUNTIME/macros/matchit.vim
|
||||||
|
or >
|
||||||
|
:runtime macros/matchit.vim
|
||||||
|
Either way, the script should start working the next time you start up Vim.
|
||||||
|
|
||||||
|
(Earlier versions of the script did nothing unless a |buffer-variable| named
|
||||||
|
|b:match_words| was defined. Even earlier versions contained autocommands
|
||||||
|
that set this variable for various file types. Now, |b:match_words| is
|
||||||
|
defined in many of the default |filetype-plugin|s instead.)
|
||||||
|
|
||||||
|
For a new language, you can add autocommands to the script or to your vimrc
|
||||||
|
file, but the recommended method is to add a line such as >
|
||||||
|
let b:match_words = '\<foo\>:\<bar\>'
|
||||||
|
to the |filetype-plugin| for your language. See |b:match_words| below for how
|
||||||
|
this variable is interpreted.
|
||||||
|
|
||||||
|
TROUBLESHOOTING *matchit-troubleshoot*
|
||||||
|
|
||||||
|
The script should work in most installations of Vim. It may not work if Vim
|
||||||
|
was compiled with a minimal feature set, for example if the |+syntax| option
|
||||||
|
was not enabled. If your Vim has support for syntax compiled in, but you do
|
||||||
|
not have |syntax| highlighting turned on, matchit.vim should work, but it may
|
||||||
|
fail to skip matching groups in comments and strings. If the |filetype|
|
||||||
|
mechanism is turned off, the |b:match_words| variable will probably not be
|
||||||
|
defined automatically.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
3. Configuration *matchit-configure*
|
||||||
|
|
||||||
|
There are several variables that govern the behavior of matchit.vim. Note
|
||||||
|
that these are variables local to the buffer, not options, so use |:let| to
|
||||||
|
define them, not |:set|. Some of these variables have values that matter; for
|
||||||
|
others, it only matters whether the variable has been defined. All of these
|
||||||
|
can be defined in the |filetype-plugin| or autocommand that defines
|
||||||
|
|b:match_words| or "on the fly."
|
||||||
|
|
||||||
|
The main variable is |b:match_words|. It is described in the section below on
|
||||||
|
supporting a new language.
|
||||||
|
|
||||||
|
*MatchError* *matchit-hl* *matchit-highlight*
|
||||||
|
MatchError is the highlight group for error messages from the script. By
|
||||||
|
default, it is linked to WarningMsg. If you do not want to be bothered by
|
||||||
|
error messages, you can define this to be something invisible. For example,
|
||||||
|
if you use the GUI version of Vim and your command line is normally white, you
|
||||||
|
can do >
|
||||||
|
:hi MatchError guifg=white guibg=white
|
||||||
|
<
|
||||||
|
*b:match_ignorecase*
|
||||||
|
If you >
|
||||||
|
:let b:match_ignorecase = 1
|
||||||
|
then matchit.vim acts as if 'ignorecase' is set: for example, "end" and "END"
|
||||||
|
are equivalent. If you >
|
||||||
|
:let b:match_ignorecase = 0
|
||||||
|
then matchit.vim treats "end" and "END" differently. (There will be no
|
||||||
|
b:match_infercase option unless someone requests it.)
|
||||||
|
|
||||||
|
*b:match_debug*
|
||||||
|
Define b:match_debug if you want debugging information to be saved. See
|
||||||
|
|matchit-debug|, below.
|
||||||
|
|
||||||
|
*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 excaped 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.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
4. Supporting a New Language *matchit-newlang*
|
||||||
|
*b:match_words*
|
||||||
|
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-expression|s.
|
||||||
|
|
||||||
|
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-string|s
|
||||||
|
(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 "<tag>label</tag>", "<.*>"
|
||||||
|
matches the whole string whereas "<.\{-}>" and "<[^>]*>" match "<tag>" and
|
||||||
|
"</tag>".
|
||||||
|
|
||||||
|
*matchit-spaces* *matchit-s:notend*
|
||||||
|
If "if" is to be paired with "end if" (Note the space!) then word boundaries
|
||||||
|
are not enough. Instead, define a regular expression s:notend that will match
|
||||||
|
anything but "end" and use it as follows: >
|
||||||
|
:let s:notend = '\%(\<end\s\+\)\@<!'
|
||||||
|
:let b:match_words = s:notend . '\<if\>:\<end\s\+if\>'
|
||||||
|
< *matchit-s:sol*
|
||||||
|
This is a simplified version of what is done for Ada. The s:notend is a
|
||||||
|
|script-variable|. Similarly, you may want to define a start-of-line regular
|
||||||
|
expression >
|
||||||
|
:let s:sol = '\%(^\|;\)\s*'
|
||||||
|
if keywords are only recognized after the start of a line or after a
|
||||||
|
semicolon (;), with optional white space.
|
||||||
|
|
||||||
|
*matchit-backref* *matchit-\1*
|
||||||
|
In any group, the expressions |\1|, |\2|, ..., |\9| 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.
|
||||||
|
|
||||||
|
DEBUGGING *matchit-debug* *:MatchDebug*
|
||||||
|
|
||||||
|
If you are having trouble figuring out the appropriate definition of
|
||||||
|
|b:match_words| then you can take advantage of the same information I use when
|
||||||
|
debugging the script. This is especially true if you are not sure whether
|
||||||
|
your patterns or my script are at fault! To make this more convenient, I have
|
||||||
|
made the command :MatchDebug, which defines the variable |b:match_debug| and
|
||||||
|
creates a Matchit menu. This menu makes it convenient to check the values of
|
||||||
|
the variables described below. You will probably also want to read
|
||||||
|
|matchit-details| above.
|
||||||
|
|
||||||
|
Defining the variable |b:match_debug| causes the script to set the following
|
||||||
|
variables, each time you hit the "%" key. Several of these are only defined
|
||||||
|
if |b:match_words| includes |backref|s.
|
||||||
|
|
||||||
|
*b:match_pat*
|
||||||
|
The b:match_pat variable is set to |b:match_words| with |backref|s parsed.
|
||||||
|
*b:match_match*
|
||||||
|
The b:match_match variable is set to the bit of text that is recognized as a
|
||||||
|
match.
|
||||||
|
*b:match_col*
|
||||||
|
The b:match_col variable is set to the cursor column of the start of the
|
||||||
|
matching text.
|
||||||
|
*b:match_wholeBR*
|
||||||
|
The b:match_wholeBR variable is set to the comma-separated group of patterns
|
||||||
|
that matches, with |backref|s unparsed.
|
||||||
|
*b:match_iniBR*
|
||||||
|
The b:match_iniBR variable is set to the first pattern in |b:match_wholeBR|.
|
||||||
|
*b:match_ini*
|
||||||
|
The b:match_ini variable is set to the first pattern in |b:match_wholeBR|,
|
||||||
|
with |backref|s resolved from |b:match_match|.
|
||||||
|
*b:match_tail*
|
||||||
|
The b:match_tail variable is set to the remaining patterns in
|
||||||
|
|b:match_wholeBR|, with |backref|s resolved from |b:match_match|.
|
||||||
|
*b:match_word*
|
||||||
|
The b:match_word variable is set to the pattern from |b:match_wholeBR| that
|
||||||
|
matches |b:match_match|.
|
||||||
|
*b:match_table*
|
||||||
|
The back reference '\'.d refers to the same thing as '\'.b:match_table[d] in
|
||||||
|
|b:match_word|.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
5. Known Bugs and Limitations *matchit-bugs*
|
||||||
|
|
||||||
|
Just because I know about a bug does not mean that it is on my todo list. I
|
||||||
|
try to respond to reports of bugs that cause real problems. If it does not
|
||||||
|
cause serious problems, or if there is a work-around, a bug may sit there for
|
||||||
|
a while. Moral: if a bug (known or not) bothers you, let me know.
|
||||||
|
|
||||||
|
The various |:vmap|s defined in the script (%, |g%|, |[%|, |]%|, |a%|) may
|
||||||
|
have undesired effects in Select mode |Select-mode-mapping|. At least, if you
|
||||||
|
want to replace the selection with any character in "ag%[]" there will be a
|
||||||
|
pause of |'updatetime'| first.
|
||||||
|
|
||||||
|
It would be nice if "\0" were recognized as the entire pattern. That is, it
|
||||||
|
would be nice if "foo:\end\0" had the same effect as "\(foo\):\end\1". I may
|
||||||
|
try to implement this in a future version. (This is not so easy to arrange as
|
||||||
|
you might think!)
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
vim:tw=78:fo=tcq2:
|
|
@ -0,0 +1,50 @@
|
||||||
|
:MatchDebug matchit.txt /*:MatchDebug*
|
||||||
|
MatchError matchit.txt /*MatchError*
|
||||||
|
[% matchit.txt /*[%*
|
||||||
|
]% matchit.txt /*]%*
|
||||||
|
b:match_col matchit.txt /*b:match_col*
|
||||||
|
b:match_debug matchit.txt /*b:match_debug*
|
||||||
|
b:match_ignorecase matchit.txt /*b:match_ignorecase*
|
||||||
|
b:match_ini matchit.txt /*b:match_ini*
|
||||||
|
b:match_iniBR matchit.txt /*b:match_iniBR*
|
||||||
|
b:match_match matchit.txt /*b:match_match*
|
||||||
|
b:match_pat matchit.txt /*b:match_pat*
|
||||||
|
b:match_skip matchit.txt /*b:match_skip*
|
||||||
|
b:match_table matchit.txt /*b:match_table*
|
||||||
|
b:match_tail matchit.txt /*b:match_tail*
|
||||||
|
b:match_wholeBR matchit.txt /*b:match_wholeBR*
|
||||||
|
b:match_word matchit.txt /*b:match_word*
|
||||||
|
b:match_words matchit.txt /*b:match_words*
|
||||||
|
g% matchit.txt /*g%*
|
||||||
|
matchit matchit.txt /*matchit*
|
||||||
|
matchit-% matchit.txt /*matchit-%*
|
||||||
|
matchit-\1 matchit.txt /*matchit-\\1*
|
||||||
|
matchit-activate matchit.txt /*matchit-activate*
|
||||||
|
matchit-backref matchit.txt /*matchit-backref*
|
||||||
|
matchit-bugs matchit.txt /*matchit-bugs*
|
||||||
|
matchit-choose matchit.txt /*matchit-choose*
|
||||||
|
matchit-configure matchit.txt /*matchit-configure*
|
||||||
|
matchit-debug matchit.txt /*matchit-debug*
|
||||||
|
matchit-details matchit.txt /*matchit-details*
|
||||||
|
matchit-highlight matchit.txt /*matchit-highlight*
|
||||||
|
matchit-hl matchit.txt /*matchit-hl*
|
||||||
|
matchit-intro matchit.txt /*matchit-intro*
|
||||||
|
matchit-languages matchit.txt /*matchit-languages*
|
||||||
|
matchit-modes matchit.txt /*matchit-modes*
|
||||||
|
matchit-newlang matchit.txt /*matchit-newlang*
|
||||||
|
matchit-o_% matchit.txt /*matchit-o_%*
|
||||||
|
matchit-parse matchit.txt /*matchit-parse*
|
||||||
|
matchit-s:notend matchit.txt /*matchit-s:notend*
|
||||||
|
matchit-s:sol matchit.txt /*matchit-s:sol*
|
||||||
|
matchit-spaces matchit.txt /*matchit-spaces*
|
||||||
|
matchit-troubleshoot matchit.txt /*matchit-troubleshoot*
|
||||||
|
matchit-v_% matchit.txt /*matchit-v_%*
|
||||||
|
matchit.txt matchit.txt /*matchit.txt*
|
||||||
|
matchit.vim matchit.txt /*matchit.vim*
|
||||||
|
o_[% matchit.txt /*o_[%*
|
||||||
|
o_]% matchit.txt /*o_]%*
|
||||||
|
o_g% matchit.txt /*o_g%*
|
||||||
|
v_[% matchit.txt /*v_[%*
|
||||||
|
v_]% matchit.txt /*v_]%*
|
||||||
|
v_a% matchit.txt /*v_a%*
|
||||||
|
v_g% matchit.txt /*v_g%*
|
|
@ -0,0 +1,5 @@
|
||||||
|
setlocal tabstop=2
|
||||||
|
setlocal softtabstop=2
|
||||||
|
setlocal shiftwidth=2
|
||||||
|
setlocal expandtab
|
||||||
|
setlocal autoindent
|
|
@ -0,0 +1,72 @@
|
||||||
|
" File: HTML AutoCloseTag.vim
|
||||||
|
" Author: Michael Sanders (msanders42 [at] gmail [dot] com)
|
||||||
|
" Last Updated: April 7 2009
|
||||||
|
" Version: 0.3
|
||||||
|
" Description: Automatically closes HTML tag once you finish typing it with >
|
||||||
|
|
||||||
|
if exists('b:mapped_auto_closetag') || &cp | finish | endif
|
||||||
|
let b:mapped_auto_closetag = 1
|
||||||
|
|
||||||
|
ino <buffer> <silent> < <><left>
|
||||||
|
ino <buffer> <silent> > <c-r>=<SID>CloseTag()<cr>
|
||||||
|
ino <buffer> <expr> <cr> <SID>Return()
|
||||||
|
|
||||||
|
if exists('s:did_auto_closetag') | finish | endif
|
||||||
|
let s:did_auto_closetag = 1
|
||||||
|
|
||||||
|
" Gets the current HTML tag by the cursor.
|
||||||
|
fun s:GetCurrentTag()
|
||||||
|
return matchstr(matchstr(getline('.'),
|
||||||
|
\ '<\zs\(\w\|=\| \|''\|"\)*>\%'.col('.').'c'), '^\w*')
|
||||||
|
endf
|
||||||
|
|
||||||
|
" Cleanly return after autocompleting an html/xml tag.
|
||||||
|
fun s:Return()
|
||||||
|
let tag = s:GetCurrentTag()
|
||||||
|
return tag != '' && match(getline('.'), '</'.tag.'>') > -1 ?
|
||||||
|
\ "\<cr>\<cr>\<up>" : "\<cr>"
|
||||||
|
endf
|
||||||
|
|
||||||
|
fun s:InComment()
|
||||||
|
return stridx(synIDattr(synID(line('.'), col('.')-1, 0), 'name'), 'omment') != -1
|
||||||
|
endf
|
||||||
|
|
||||||
|
" Counts occurance of needle in page, when not in a comment.
|
||||||
|
fun s:CountInPage(needle)
|
||||||
|
let pos = [line('.'), col('.')]
|
||||||
|
call cursor(1, 1)
|
||||||
|
let counter = search(a:needle, 'Wc')
|
||||||
|
while search(a:needle, 'W')
|
||||||
|
if !s:InComment() | let counter += 1 | endif
|
||||||
|
endw
|
||||||
|
call cursor(pos)
|
||||||
|
return counter
|
||||||
|
endf
|
||||||
|
|
||||||
|
" Returns whether a closing tag has already been inserted.
|
||||||
|
fun s:ClosingTag(tag)
|
||||||
|
return s:CountInPage('\c<'.a:tag.'.\{-}>') <= s:CountInPage('\c</'.a:tag.'>')
|
||||||
|
endf
|
||||||
|
|
||||||
|
" Automatically inserts closing tag after starting tag is typed
|
||||||
|
fun s:CloseTag()
|
||||||
|
let line = getline('.')
|
||||||
|
let col = col('.')
|
||||||
|
if line[col-1] != '>' | return '>' | endif
|
||||||
|
let col += 1
|
||||||
|
call cursor(0, col)
|
||||||
|
" Don't autocomplete next to a word or another tag or if inside comment
|
||||||
|
if line[col] !~ '\w\|<\|>' && !s:InComment()
|
||||||
|
let tag = s:GetCurrentTag()
|
||||||
|
" Insert closing tag if tag is not self-closing and has not already
|
||||||
|
" been closed
|
||||||
|
if tag != '' && tag !~ '\vimg|input|link|meta|br|hr|area|base|param|dd|dt'
|
||||||
|
\ && !s:ClosingTag(tag)
|
||||||
|
let line = substitute(line, '\%'.col.'c', '</'.escape(tag, '/').'>', '')
|
||||||
|
call setline('.', line)
|
||||||
|
call cursor(0, col)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
return ''
|
||||||
|
endf
|
||||||
|
" vim:noet:sw=4:ts=4:ft=vim
|
|
@ -0,0 +1,5 @@
|
||||||
|
setlocal tabstop=4
|
||||||
|
setlocal softtabstop=4
|
||||||
|
setlocal shiftwidth=4
|
||||||
|
setlocal expandtab
|
||||||
|
setlocal autoindent
|
|
@ -0,0 +1,7 @@
|
||||||
|
" Nice window size for side-by-side editors
|
||||||
|
set lines=60 columns=169
|
||||||
|
" Cool, dark theme from
|
||||||
|
" http://dengmao.wordpress.com/2007/01/22/vim-color-scheme-wombat/
|
||||||
|
colorscheme wombat
|
||||||
|
" Nice font and size (escape spaces)
|
||||||
|
set guifont=Luxi\ Mono\ 10
|
|
@ -0,0 +1,22 @@
|
||||||
|
" Highlight all instances of word under cursor, when idle.
|
||||||
|
" Useful when studying strange source code.
|
||||||
|
" Type z/ to toggle highlighting on/off.
|
||||||
|
nnoremap z/ :if AutoHighlightToggle()<Bar>set hls<Bar>endif<CR>
|
||||||
|
function! AutoHighlightToggle()
|
||||||
|
let @/ = ''
|
||||||
|
if exists('#auto_highlight')
|
||||||
|
au! auto_highlight
|
||||||
|
augroup! auto_highlight
|
||||||
|
setl updatetime=4000
|
||||||
|
echo 'Highlight current word: off'
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
augroup auto_highlight
|
||||||
|
au!
|
||||||
|
au CursorHold * let @/ = '\V\<'.escape(expand('<cword>'), '\').'\>'
|
||||||
|
augroup end
|
||||||
|
setl updatetime=500
|
||||||
|
echo 'Highlight current word: ON'
|
||||||
|
return 1
|
||||||
|
endif
|
||||||
|
endfunction
|
|
@ -0,0 +1,812 @@
|
||||||
|
" matchit.vim: (global plugin) Extended "%" matching
|
||||||
|
" Last Change: Fri Jan 25 10:00 AM 2008 EST
|
||||||
|
" Maintainer: Benji Fisher PhD <benji@member.AMS.org>
|
||||||
|
" Version: 1.13.2, for Vim 6.3+
|
||||||
|
" URL: http://www.vim.org/script.php?script_id=39
|
||||||
|
|
||||||
|
" Documentation:
|
||||||
|
" The documentation is in a separate file, matchit.txt .
|
||||||
|
|
||||||
|
" Credits:
|
||||||
|
" Vim editor by Bram Moolenaar (Thanks, Bram!)
|
||||||
|
" Original script and design by Raul Segura Acevedo
|
||||||
|
" Support for comments by Douglas Potts
|
||||||
|
" Support for back references and other improvements by Benji Fisher
|
||||||
|
" Support for many languages by Johannes Zellner
|
||||||
|
" Suggestions for improvement, bug reports, and support for additional
|
||||||
|
" languages by Jordi-Albert Batalla, Neil Bird, Servatius Brandt, Mark
|
||||||
|
" Collett, Stephen Wall, Dany St-Amant, Yuheng Xie, and Johannes Zellner.
|
||||||
|
|
||||||
|
" Debugging:
|
||||||
|
" If you'd like to try the built-in debugging commands...
|
||||||
|
" :MatchDebug to activate debugging for the current buffer
|
||||||
|
" This saves the values of several key script variables as buffer-local
|
||||||
|
" variables. See the MatchDebug() function, below, for details.
|
||||||
|
|
||||||
|
" TODO: I should think about multi-line patterns for b:match_words.
|
||||||
|
" This would require an option: how many lines to scan (default 1).
|
||||||
|
" This would be useful for Python, maybe also for *ML.
|
||||||
|
" TODO: Maybe I should add a menu so that people will actually use some of
|
||||||
|
" the features that I have implemented.
|
||||||
|
" TODO: Eliminate the MultiMatch function. Add yet another argument to
|
||||||
|
" Match_wrapper() instead.
|
||||||
|
" TODO: Allow :let b:match_words = '\(\(foo\)\(bar\)\):\3\2:end\1'
|
||||||
|
" TODO: Make backrefs safer by using '\V' (very no-magic).
|
||||||
|
" TODO: Add a level of indirection, so that custom % scripts can use my
|
||||||
|
" work but extend it.
|
||||||
|
|
||||||
|
" allow user to prevent loading
|
||||||
|
" and prevent duplicate loading
|
||||||
|
if exists("loaded_matchit") || &cp
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
let loaded_matchit = 1
|
||||||
|
let s:last_mps = ""
|
||||||
|
let s:last_words = ":"
|
||||||
|
|
||||||
|
let s:save_cpo = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
nnoremap <silent> % :<C-U>call <SID>Match_wrapper('',1,'n') <CR>
|
||||||
|
nnoremap <silent> g% :<C-U>call <SID>Match_wrapper('',0,'n') <CR>
|
||||||
|
vnoremap <silent> % :<C-U>call <SID>Match_wrapper('',1,'v') <CR>m'gv``
|
||||||
|
vnoremap <silent> g% :<C-U>call <SID>Match_wrapper('',0,'v') <CR>m'gv``
|
||||||
|
onoremap <silent> % v:<C-U>call <SID>Match_wrapper('',1,'o') <CR>
|
||||||
|
onoremap <silent> g% v:<C-U>call <SID>Match_wrapper('',0,'o') <CR>
|
||||||
|
|
||||||
|
" Analogues of [{ and ]} using matching patterns:
|
||||||
|
nnoremap <silent> [% :<C-U>call <SID>MultiMatch("bW", "n") <CR>
|
||||||
|
nnoremap <silent> ]% :<C-U>call <SID>MultiMatch("W", "n") <CR>
|
||||||
|
vmap [% <Esc>[%m'gv``
|
||||||
|
vmap ]% <Esc>]%m'gv``
|
||||||
|
" vnoremap <silent> [% :<C-U>call <SID>MultiMatch("bW", "v") <CR>m'gv``
|
||||||
|
" vnoremap <silent> ]% :<C-U>call <SID>MultiMatch("W", "v") <CR>m'gv``
|
||||||
|
onoremap <silent> [% v:<C-U>call <SID>MultiMatch("bW", "o") <CR>
|
||||||
|
onoremap <silent> ]% v:<C-U>call <SID>MultiMatch("W", "o") <CR>
|
||||||
|
|
||||||
|
" text object:
|
||||||
|
vmap a% <Esc>[%v]%
|
||||||
|
|
||||||
|
" Auto-complete mappings: (not yet "ready for prime time")
|
||||||
|
" TODO Read :help write-plugin for the "right" way to let the user
|
||||||
|
" specify a key binding.
|
||||||
|
" let g:match_auto = '<C-]>'
|
||||||
|
" let g:match_autoCR = '<C-CR>'
|
||||||
|
" if exists("g:match_auto")
|
||||||
|
" execute "inoremap " . g:match_auto . ' x<Esc>"=<SID>Autocomplete()<CR>Pls'
|
||||||
|
" endif
|
||||||
|
" if exists("g:match_autoCR")
|
||||||
|
" execute "inoremap " . g:match_autoCR . ' <CR><C-R>=<SID>Autocomplete()<CR>'
|
||||||
|
" endif
|
||||||
|
" if exists("g:match_gthhoh")
|
||||||
|
" execute "inoremap " . g:match_gthhoh . ' <C-O>:call <SID>Gthhoh()<CR>'
|
||||||
|
" endif " gthhoh = "Get the heck out of here!"
|
||||||
|
|
||||||
|
let s:notslash = '\\\@<!\%(\\\\\)*'
|
||||||
|
|
||||||
|
function! s:Match_wrapper(word, forward, mode) range
|
||||||
|
" In s:CleanUp(), :execute "set" restore_options .
|
||||||
|
let restore_options = (&ic ? " " : " no") . "ignorecase"
|
||||||
|
if exists("b:match_ignorecase")
|
||||||
|
let &ignorecase = b:match_ignorecase
|
||||||
|
endif
|
||||||
|
let restore_options = " ve=" . &ve . restore_options
|
||||||
|
set ve=
|
||||||
|
" If this function was called from Visual mode, make sure that the cursor
|
||||||
|
" is at the correct end of the Visual range:
|
||||||
|
if a:mode == "v"
|
||||||
|
execute "normal! gv\<Esc>"
|
||||||
|
endif
|
||||||
|
" In s:CleanUp(), we may need to check whether the cursor moved forward.
|
||||||
|
let startline = line(".")
|
||||||
|
let startcol = col(".")
|
||||||
|
" Use default behavior if called with a count.
|
||||||
|
if v:count
|
||||||
|
exe "normal! " . v:count . "%"
|
||||||
|
return s:CleanUp(restore_options, a:mode, startline, startcol)
|
||||||
|
end
|
||||||
|
|
||||||
|
" First step: if not already done, set the script variables
|
||||||
|
" s:do_BR flag for whether there are backrefs
|
||||||
|
" s:pat parsed version of b:match_words
|
||||||
|
" s:all regexp based on s:pat and the default groups
|
||||||
|
"
|
||||||
|
if !exists("b:match_words") || b:match_words == ""
|
||||||
|
let match_words = ""
|
||||||
|
" Allow b:match_words = "GetVimMatchWords()" .
|
||||||
|
elseif b:match_words =~ ":"
|
||||||
|
let match_words = b:match_words
|
||||||
|
else
|
||||||
|
execute "let match_words =" b:match_words
|
||||||
|
endif
|
||||||
|
" Thanks to Preben "Peppe" Guldberg and Bram Moolenaar for this suggestion!
|
||||||
|
if (match_words != s:last_words) || (&mps != s:last_mps) ||
|
||||||
|
\ exists("b:match_debug")
|
||||||
|
let s:last_words = match_words
|
||||||
|
let s:last_mps = &mps
|
||||||
|
" The next several lines were here before
|
||||||
|
" BF started messing with this script.
|
||||||
|
" quote the special chars in 'matchpairs', replace [,:] with \| and then
|
||||||
|
" append the builtin pairs (/*, */, #if, #ifdef, #else, #elif, #endif)
|
||||||
|
" let default = substitute(escape(&mps, '[$^.*~\\/?]'), '[,:]\+',
|
||||||
|
" \ '\\|', 'g').'\|\/\*\|\*\/\|#if\>\|#ifdef\>\|#else\>\|#elif\>\|#endif\>'
|
||||||
|
let default = escape(&mps, '[$^.*~\\/?]') . (strlen(&mps) ? "," : "") .
|
||||||
|
\ '\/\*:\*\/,#if\%(def\)\=:#else\>:#elif\>:#endif\>'
|
||||||
|
" s:all = pattern with all the keywords
|
||||||
|
let match_words = match_words . (strlen(match_words) ? "," : "") . default
|
||||||
|
if match_words !~ s:notslash . '\\\d'
|
||||||
|
let s:do_BR = 0
|
||||||
|
let s:pat = match_words
|
||||||
|
else
|
||||||
|
let s:do_BR = 1
|
||||||
|
let s:pat = s:ParseWords(match_words)
|
||||||
|
endif
|
||||||
|
let s:all = substitute(s:pat, s:notslash . '\zs[,:]\+', '\\|', 'g')
|
||||||
|
let s:all = '\%(' . s:all . '\)'
|
||||||
|
" let s:all = '\%(' . substitute(s:all, '\\\ze[,:]', '', 'g') . '\)'
|
||||||
|
if exists("b:match_debug")
|
||||||
|
let b:match_pat = s:pat
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Second step: set the following local variables:
|
||||||
|
" matchline = line on which the cursor started
|
||||||
|
" curcol = number of characters before match
|
||||||
|
" prefix = regexp for start of line to start of match
|
||||||
|
" suffix = regexp for end of match to end of line
|
||||||
|
" Require match to end on or after the cursor and prefer it to
|
||||||
|
" start on or before the cursor.
|
||||||
|
let matchline = getline(startline)
|
||||||
|
if a:word != ''
|
||||||
|
" word given
|
||||||
|
if a:word !~ s:all
|
||||||
|
echohl WarningMsg|echo 'Missing rule for word:"'.a:word.'"'|echohl NONE
|
||||||
|
return s:CleanUp(restore_options, a:mode, startline, startcol)
|
||||||
|
endif
|
||||||
|
let matchline = a:word
|
||||||
|
let curcol = 0
|
||||||
|
let prefix = '^\%('
|
||||||
|
let suffix = '\)$'
|
||||||
|
" Now the case when "word" is not given
|
||||||
|
else " Find the match that ends on or after the cursor and set curcol.
|
||||||
|
let regexp = s:Wholematch(matchline, s:all, startcol-1)
|
||||||
|
let curcol = match(matchline, regexp)
|
||||||
|
" If there is no match, give up.
|
||||||
|
if curcol == -1
|
||||||
|
return s:CleanUp(restore_options, a:mode, startline, startcol)
|
||||||
|
endif
|
||||||
|
let endcol = matchend(matchline, regexp)
|
||||||
|
let suf = strlen(matchline) - endcol
|
||||||
|
let prefix = (curcol ? '^.*\%' . (curcol + 1) . 'c\%(' : '^\%(')
|
||||||
|
let suffix = (suf ? '\)\%' . (endcol + 1) . 'c.*$' : '\)$')
|
||||||
|
endif
|
||||||
|
if exists("b:match_debug")
|
||||||
|
let b:match_match = matchstr(matchline, regexp)
|
||||||
|
let b:match_col = curcol+1
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Third step: Find the group and single word that match, and the original
|
||||||
|
" (backref) versions of these. Then, resolve the backrefs.
|
||||||
|
" Set the following local variable:
|
||||||
|
" group = colon-separated list of patterns, one of which matches
|
||||||
|
" = ini:mid:fin or ini:fin
|
||||||
|
"
|
||||||
|
" Reconstruct the version with unresolved backrefs.
|
||||||
|
let patBR = substitute(match_words.',',
|
||||||
|
\ s:notslash.'\zs[,:]*,[,:]*', ',', 'g')
|
||||||
|
let patBR = substitute(patBR, s:notslash.'\zs:\{2,}', ':', 'g')
|
||||||
|
" Now, set group and groupBR to the matching group: 'if:endif' or
|
||||||
|
" 'while:endwhile' or whatever. A bit of a kluge: s:Choose() returns
|
||||||
|
" group . "," . groupBR, and we pick it apart.
|
||||||
|
let group = s:Choose(s:pat, matchline, ",", ":", prefix, suffix, patBR)
|
||||||
|
let i = matchend(group, s:notslash . ",")
|
||||||
|
let groupBR = strpart(group, i)
|
||||||
|
let group = strpart(group, 0, i-1)
|
||||||
|
" Now, matchline =~ prefix . substitute(group,':','\|','g') . suffix
|
||||||
|
if s:do_BR " Do the hard part: resolve those backrefs!
|
||||||
|
let group = s:InsertRefs(groupBR, prefix, group, suffix, matchline)
|
||||||
|
endif
|
||||||
|
if exists("b:match_debug")
|
||||||
|
let b:match_wholeBR = groupBR
|
||||||
|
let i = matchend(groupBR, s:notslash . ":")
|
||||||
|
let b:match_iniBR = strpart(groupBR, 0, i-1)
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Fourth step: Set the arguments for searchpair().
|
||||||
|
let i = matchend(group, s:notslash . ":")
|
||||||
|
let j = matchend(group, '.*' . s:notslash . ":")
|
||||||
|
let ini = strpart(group, 0, i-1)
|
||||||
|
let mid = substitute(strpart(group, i,j-i-1), s:notslash.'\zs:', '\\|', 'g')
|
||||||
|
let fin = strpart(group, j)
|
||||||
|
"Un-escape the remaining , and : characters.
|
||||||
|
let ini = substitute(ini, s:notslash . '\zs\\\(:\|,\)', '\1', 'g')
|
||||||
|
let mid = substitute(mid, s:notslash . '\zs\\\(:\|,\)', '\1', 'g')
|
||||||
|
let fin = substitute(fin, s:notslash . '\zs\\\(:\|,\)', '\1', 'g')
|
||||||
|
" searchpair() requires that these patterns avoid \(\) groups.
|
||||||
|
let ini = substitute(ini, s:notslash . '\zs\\(', '\\%(', 'g')
|
||||||
|
let mid = substitute(mid, s:notslash . '\zs\\(', '\\%(', 'g')
|
||||||
|
let fin = substitute(fin, s:notslash . '\zs\\(', '\\%(', 'g')
|
||||||
|
" Set mid. This is optimized for readability, not micro-efficiency!
|
||||||
|
if a:forward && matchline =~ prefix . fin . suffix
|
||||||
|
\ || !a:forward && matchline =~ prefix . ini . suffix
|
||||||
|
let mid = ""
|
||||||
|
endif
|
||||||
|
" Set flag. This is optimized for readability, not micro-efficiency!
|
||||||
|
if a:forward && matchline =~ prefix . fin . suffix
|
||||||
|
\ || !a:forward && matchline !~ prefix . ini . suffix
|
||||||
|
let flag = "bW"
|
||||||
|
else
|
||||||
|
let flag = "W"
|
||||||
|
endif
|
||||||
|
" Set skip.
|
||||||
|
if exists("b:match_skip")
|
||||||
|
let skip = b:match_skip
|
||||||
|
elseif exists("b:match_comment") " backwards compatibility and testing!
|
||||||
|
let skip = "r:" . b:match_comment
|
||||||
|
else
|
||||||
|
let skip = 's:comment\|string'
|
||||||
|
endif
|
||||||
|
let skip = s:ParseSkip(skip)
|
||||||
|
if exists("b:match_debug")
|
||||||
|
let b:match_ini = ini
|
||||||
|
let b:match_tail = (strlen(mid) ? mid.'\|' : '') . fin
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Fifth step: actually start moving the cursor and call searchpair().
|
||||||
|
" Later, :execute restore_cursor to get to the original screen.
|
||||||
|
let restore_cursor = virtcol(".") . "|"
|
||||||
|
normal! g0
|
||||||
|
let restore_cursor = line(".") . "G" . virtcol(".") . "|zs" . restore_cursor
|
||||||
|
normal! H
|
||||||
|
let restore_cursor = "normal!" . line(".") . "Gzt" . restore_cursor
|
||||||
|
execute restore_cursor
|
||||||
|
call cursor(0, curcol + 1)
|
||||||
|
" normal! 0
|
||||||
|
" if curcol
|
||||||
|
" execute "normal!" . curcol . "l"
|
||||||
|
" endif
|
||||||
|
if skip =~ 'synID' && !(has("syntax") && exists("g:syntax_on"))
|
||||||
|
let skip = "0"
|
||||||
|
else
|
||||||
|
execute "if " . skip . "| let skip = '0' | endif"
|
||||||
|
endif
|
||||||
|
let sp_return = searchpair(ini, mid, fin, flag, skip)
|
||||||
|
let final_position = "call cursor(" . line(".") . "," . col(".") . ")"
|
||||||
|
" Restore cursor position and original screen.
|
||||||
|
execute restore_cursor
|
||||||
|
normal! m'
|
||||||
|
if sp_return > 0
|
||||||
|
execute final_position
|
||||||
|
endif
|
||||||
|
return s:CleanUp(restore_options, a:mode, startline, startcol, mid.'\|'.fin)
|
||||||
|
endfun
|
||||||
|
|
||||||
|
" Restore options and do some special handling for Operator-pending mode.
|
||||||
|
" The optional argument is the tail of the matching group.
|
||||||
|
fun! s:CleanUp(options, mode, startline, startcol, ...)
|
||||||
|
execute "set" a:options
|
||||||
|
" Open folds, if appropriate.
|
||||||
|
if a:mode != "o"
|
||||||
|
if &foldopen =~ "percent"
|
||||||
|
normal! zv
|
||||||
|
endif
|
||||||
|
" In Operator-pending mode, we want to include the whole match
|
||||||
|
" (for example, d%).
|
||||||
|
" This is only a problem if we end up moving in the forward direction.
|
||||||
|
elseif (a:startline < line(".")) ||
|
||||||
|
\ (a:startline == line(".") && a:startcol < col("."))
|
||||||
|
if a:0
|
||||||
|
" Check whether the match is a single character. If not, move to the
|
||||||
|
" end of the match.
|
||||||
|
let matchline = getline(".")
|
||||||
|
let currcol = col(".")
|
||||||
|
let regexp = s:Wholematch(matchline, a:1, currcol-1)
|
||||||
|
let endcol = matchend(matchline, regexp)
|
||||||
|
if endcol > currcol " This is NOT off by one!
|
||||||
|
execute "normal!" . (endcol - currcol) . "l"
|
||||||
|
endif
|
||||||
|
endif " a:0
|
||||||
|
endif " a:mode != "o" && etc.
|
||||||
|
return 0
|
||||||
|
endfun
|
||||||
|
|
||||||
|
" Example (simplified HTML patterns): if
|
||||||
|
" a:groupBR = '<\(\k\+\)>:</\1>'
|
||||||
|
" a:prefix = '^.\{3}\('
|
||||||
|
" a:group = '<\(\k\+\)>:</\(\k\+\)>'
|
||||||
|
" a:suffix = '\).\{2}$'
|
||||||
|
" a:matchline = "123<tag>12" or "123</tag>12"
|
||||||
|
" then extract "tag" from a:matchline and return "<tag>:</tag>" .
|
||||||
|
fun! s:InsertRefs(groupBR, prefix, group, suffix, matchline)
|
||||||
|
if a:matchline !~ a:prefix .
|
||||||
|
\ substitute(a:group, s:notslash . '\zs:', '\\|', 'g') . a:suffix
|
||||||
|
return a:group
|
||||||
|
endif
|
||||||
|
let i = matchend(a:groupBR, s:notslash . ':')
|
||||||
|
let ini = strpart(a:groupBR, 0, i-1)
|
||||||
|
let tailBR = strpart(a:groupBR, i)
|
||||||
|
let word = s:Choose(a:group, a:matchline, ":", "", a:prefix, a:suffix,
|
||||||
|
\ a:groupBR)
|
||||||
|
let i = matchend(word, s:notslash . ":")
|
||||||
|
let wordBR = strpart(word, i)
|
||||||
|
let word = strpart(word, 0, i-1)
|
||||||
|
" Now, a:matchline =~ a:prefix . word . a:suffix
|
||||||
|
if wordBR != ini
|
||||||
|
let table = s:Resolve(ini, wordBR, "table")
|
||||||
|
else
|
||||||
|
" let table = "----------"
|
||||||
|
let table = ""
|
||||||
|
let d = 0
|
||||||
|
while d < 10
|
||||||
|
if tailBR =~ s:notslash . '\\' . d
|
||||||
|
" let table[d] = d
|
||||||
|
let table = table . d
|
||||||
|
else
|
||||||
|
let table = table . "-"
|
||||||
|
endif
|
||||||
|
let d = d + 1
|
||||||
|
endwhile
|
||||||
|
endif
|
||||||
|
let d = 9
|
||||||
|
while d
|
||||||
|
if table[d] != "-"
|
||||||
|
let backref = substitute(a:matchline, a:prefix.word.a:suffix,
|
||||||
|
\ '\'.table[d], "")
|
||||||
|
" Are there any other characters that should be escaped?
|
||||||
|
let backref = escape(backref, '*,:')
|
||||||
|
execute s:Ref(ini, d, "start", "len")
|
||||||
|
let ini = strpart(ini, 0, start) . backref . strpart(ini, start+len)
|
||||||
|
let tailBR = substitute(tailBR, s:notslash . '\zs\\' . d,
|
||||||
|
\ escape(backref, '\\'), 'g')
|
||||||
|
endif
|
||||||
|
let d = d-1
|
||||||
|
endwhile
|
||||||
|
if exists("b:match_debug")
|
||||||
|
if s:do_BR
|
||||||
|
let b:match_table = table
|
||||||
|
let b:match_word = word
|
||||||
|
else
|
||||||
|
let b:match_table = ""
|
||||||
|
let b:match_word = ""
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
return ini . ":" . tailBR
|
||||||
|
endfun
|
||||||
|
|
||||||
|
" Input a comma-separated list of groups with backrefs, such as
|
||||||
|
" a:groups = '\(foo\):end\1,\(bar\):end\1'
|
||||||
|
" and return a comma-separated list of groups with backrefs replaced:
|
||||||
|
" return '\(foo\):end\(foo\),\(bar\):end\(bar\)'
|
||||||
|
fun! s:ParseWords(groups)
|
||||||
|
let groups = substitute(a:groups.",", s:notslash.'\zs[,:]*,[,:]*', ',', 'g')
|
||||||
|
let groups = substitute(groups, s:notslash . '\zs:\{2,}', ':', 'g')
|
||||||
|
let parsed = ""
|
||||||
|
while groups =~ '[^,:]'
|
||||||
|
let i = matchend(groups, s:notslash . ':')
|
||||||
|
let j = matchend(groups, s:notslash . ',')
|
||||||
|
let ini = strpart(groups, 0, i-1)
|
||||||
|
let tail = strpart(groups, i, j-i-1) . ":"
|
||||||
|
let groups = strpart(groups, j)
|
||||||
|
let parsed = parsed . ini
|
||||||
|
let i = matchend(tail, s:notslash . ':')
|
||||||
|
while i != -1
|
||||||
|
" In 'if:else:endif', ini='if' and word='else' and then word='endif'.
|
||||||
|
let word = strpart(tail, 0, i-1)
|
||||||
|
let tail = strpart(tail, i)
|
||||||
|
let i = matchend(tail, s:notslash . ':')
|
||||||
|
let parsed = parsed . ":" . s:Resolve(ini, word, "word")
|
||||||
|
endwhile " Now, tail has been used up.
|
||||||
|
let parsed = parsed . ","
|
||||||
|
endwhile " groups =~ '[^,:]'
|
||||||
|
let parsed = substitute(parsed, ',$', '', '')
|
||||||
|
return parsed
|
||||||
|
endfun
|
||||||
|
|
||||||
|
" TODO I think this can be simplified and/or made more efficient.
|
||||||
|
" TODO What should I do if a:start is out of range?
|
||||||
|
" Return a regexp that matches all of a:string, such that
|
||||||
|
" matchstr(a:string, regexp) represents the match for a:pat that starts
|
||||||
|
" as close to a:start as possible, before being preferred to after, and
|
||||||
|
" ends after a:start .
|
||||||
|
" Usage:
|
||||||
|
" let regexp = s:Wholematch(getline("."), 'foo\|bar', col(".")-1)
|
||||||
|
" let i = match(getline("."), regexp)
|
||||||
|
" let j = matchend(getline("."), regexp)
|
||||||
|
" let match = matchstr(getline("."), regexp)
|
||||||
|
fun! s:Wholematch(string, pat, start)
|
||||||
|
let group = '\%(' . a:pat . '\)'
|
||||||
|
let prefix = (a:start ? '\(^.*\%<' . (a:start + 2) . 'c\)\zs' : '^')
|
||||||
|
let len = strlen(a:string)
|
||||||
|
let suffix = (a:start+1 < len ? '\(\%>'.(a:start+1).'c.*$\)\@=' : '$')
|
||||||
|
if a:string !~ prefix . group . suffix
|
||||||
|
let prefix = ''
|
||||||
|
endif
|
||||||
|
return prefix . group . suffix
|
||||||
|
endfun
|
||||||
|
|
||||||
|
" No extra arguments: s:Ref(string, d) will
|
||||||
|
" find the d'th occurrence of '\(' and return it, along with everything up
|
||||||
|
" to and including the matching '\)'.
|
||||||
|
" One argument: s:Ref(string, d, "start") returns the index of the start
|
||||||
|
" of the d'th '\(' and any other argument returns the length of the group.
|
||||||
|
" Two arguments: s:Ref(string, d, "foo", "bar") returns a string to be
|
||||||
|
" executed, having the effect of
|
||||||
|
" :let foo = s:Ref(string, d, "start")
|
||||||
|
" :let bar = s:Ref(string, d, "len")
|
||||||
|
fun! s:Ref(string, d, ...)
|
||||||
|
let len = strlen(a:string)
|
||||||
|
if a:d == 0
|
||||||
|
let start = 0
|
||||||
|
else
|
||||||
|
let cnt = a:d
|
||||||
|
let match = a:string
|
||||||
|
while cnt
|
||||||
|
let cnt = cnt - 1
|
||||||
|
let index = matchend(match, s:notslash . '\\(')
|
||||||
|
if index == -1
|
||||||
|
return ""
|
||||||
|
endif
|
||||||
|
let match = strpart(match, index)
|
||||||
|
endwhile
|
||||||
|
let start = len - strlen(match)
|
||||||
|
if a:0 == 1 && a:1 == "start"
|
||||||
|
return start - 2
|
||||||
|
endif
|
||||||
|
let cnt = 1
|
||||||
|
while cnt
|
||||||
|
let index = matchend(match, s:notslash . '\\(\|\\)') - 1
|
||||||
|
if index == -2
|
||||||
|
return ""
|
||||||
|
endif
|
||||||
|
" Increment if an open, decrement if a ')':
|
||||||
|
let cnt = cnt + (match[index]=="(" ? 1 : -1) " ')'
|
||||||
|
" let cnt = stridx('0(', match[index]) + cnt
|
||||||
|
let match = strpart(match, index+1)
|
||||||
|
endwhile
|
||||||
|
let start = start - 2
|
||||||
|
let len = len - start - strlen(match)
|
||||||
|
endif
|
||||||
|
if a:0 == 1
|
||||||
|
return len
|
||||||
|
elseif a:0 == 2
|
||||||
|
return "let " . a:1 . "=" . start . "| let " . a:2 . "=" . len
|
||||||
|
else
|
||||||
|
return strpart(a:string, start, len)
|
||||||
|
endif
|
||||||
|
endfun
|
||||||
|
|
||||||
|
" Count the number of disjoint copies of pattern in string.
|
||||||
|
" If the pattern is a literal string and contains no '0' or '1' characters
|
||||||
|
" then s:Count(string, pattern, '0', '1') should be faster than
|
||||||
|
" s:Count(string, pattern).
|
||||||
|
fun! s:Count(string, pattern, ...)
|
||||||
|
let pat = escape(a:pattern, '\\')
|
||||||
|
if a:0 > 1
|
||||||
|
let foo = substitute(a:string, '[^'.a:pattern.']', "a:1", "g")
|
||||||
|
let foo = substitute(a:string, pat, a:2, "g")
|
||||||
|
let foo = substitute(foo, '[^' . a:2 . ']', "", "g")
|
||||||
|
return strlen(foo)
|
||||||
|
endif
|
||||||
|
let result = 0
|
||||||
|
let foo = a:string
|
||||||
|
let index = matchend(foo, pat)
|
||||||
|
while index != -1
|
||||||
|
let result = result + 1
|
||||||
|
let foo = strpart(foo, index)
|
||||||
|
let index = matchend(foo, pat)
|
||||||
|
endwhile
|
||||||
|
return result
|
||||||
|
endfun
|
||||||
|
|
||||||
|
" s:Resolve('\(a\)\(b\)', '\(c\)\2\1\1\2') should return table.word, where
|
||||||
|
" word = '\(c\)\(b\)\(a\)\3\2' and table = '-32-------'. That is, the first
|
||||||
|
" '\1' in target is replaced by '\(a\)' in word, table[1] = 3, and this
|
||||||
|
" indicates that all other instances of '\1' in target are to be replaced
|
||||||
|
" by '\3'. The hard part is dealing with nesting...
|
||||||
|
" Note that ":" is an illegal character for source and target,
|
||||||
|
" unless it is preceded by "\".
|
||||||
|
fun! s:Resolve(source, target, output)
|
||||||
|
let word = a:target
|
||||||
|
let i = matchend(word, s:notslash . '\\\d') - 1
|
||||||
|
let table = "----------"
|
||||||
|
while i != -2 " There are back references to be replaced.
|
||||||
|
let d = word[i]
|
||||||
|
let backref = s:Ref(a:source, d)
|
||||||
|
" The idea is to replace '\d' with backref. Before we do this,
|
||||||
|
" replace any \(\) groups in backref with :1, :2, ... if they
|
||||||
|
" correspond to the first, second, ... group already inserted
|
||||||
|
" into backref. Later, replace :1 with \1 and so on. The group
|
||||||
|
" number w+b within backref corresponds to the group number
|
||||||
|
" s within a:source.
|
||||||
|
" w = number of '\(' in word before the current one
|
||||||
|
let w = s:Count(
|
||||||
|
\ substitute(strpart(word, 0, i-1), '\\\\', '', 'g'), '\(', '1')
|
||||||
|
let b = 1 " number of the current '\(' in backref
|
||||||
|
let s = d " number of the current '\(' in a:source
|
||||||
|
while b <= s:Count(substitute(backref, '\\\\', '', 'g'), '\(', '1')
|
||||||
|
\ && s < 10
|
||||||
|
if table[s] == "-"
|
||||||
|
if w + b < 10
|
||||||
|
" let table[s] = w + b
|
||||||
|
let table = strpart(table, 0, s) . (w+b) . strpart(table, s+1)
|
||||||
|
endif
|
||||||
|
let b = b + 1
|
||||||
|
let s = s + 1
|
||||||
|
else
|
||||||
|
execute s:Ref(backref, b, "start", "len")
|
||||||
|
let ref = strpart(backref, start, len)
|
||||||
|
let backref = strpart(backref, 0, start) . ":". table[s]
|
||||||
|
\ . strpart(backref, start+len)
|
||||||
|
let s = s + s:Count(substitute(ref, '\\\\', '', 'g'), '\(', '1')
|
||||||
|
endif
|
||||||
|
endwhile
|
||||||
|
let word = strpart(word, 0, i-1) . backref . strpart(word, i+1)
|
||||||
|
let i = matchend(word, s:notslash . '\\\d') - 1
|
||||||
|
endwhile
|
||||||
|
let word = substitute(word, s:notslash . '\zs:', '\\', 'g')
|
||||||
|
if a:output == "table"
|
||||||
|
return table
|
||||||
|
elseif a:output == "word"
|
||||||
|
return word
|
||||||
|
else
|
||||||
|
return table . word
|
||||||
|
endif
|
||||||
|
endfun
|
||||||
|
|
||||||
|
" Assume a:comma = ",". Then the format for a:patterns and a:1 is
|
||||||
|
" a:patterns = "<pat1>,<pat2>,..."
|
||||||
|
" a:1 = "<alt1>,<alt2>,..."
|
||||||
|
" If <patn> is the first pattern that matches a:string then return <patn>
|
||||||
|
" if no optional arguments are given; return <patn>,<altn> if a:1 is given.
|
||||||
|
fun! s:Choose(patterns, string, comma, branch, prefix, suffix, ...)
|
||||||
|
let tail = (a:patterns =~ a:comma."$" ? a:patterns : a:patterns . a:comma)
|
||||||
|
let i = matchend(tail, s:notslash . a:comma)
|
||||||
|
if a:0
|
||||||
|
let alttail = (a:1 =~ a:comma."$" ? a:1 : a:1 . a:comma)
|
||||||
|
let j = matchend(alttail, s:notslash . a:comma)
|
||||||
|
endif
|
||||||
|
let current = strpart(tail, 0, i-1)
|
||||||
|
if a:branch == ""
|
||||||
|
let currpat = current
|
||||||
|
else
|
||||||
|
let currpat = substitute(current, s:notslash . a:branch, '\\|', 'g')
|
||||||
|
endif
|
||||||
|
while a:string !~ a:prefix . currpat . a:suffix
|
||||||
|
let tail = strpart(tail, i)
|
||||||
|
let i = matchend(tail, s:notslash . a:comma)
|
||||||
|
if i == -1
|
||||||
|
return -1
|
||||||
|
endif
|
||||||
|
let current = strpart(tail, 0, i-1)
|
||||||
|
if a:branch == ""
|
||||||
|
let currpat = current
|
||||||
|
else
|
||||||
|
let currpat = substitute(current, s:notslash . a:branch, '\\|', 'g')
|
||||||
|
endif
|
||||||
|
if a:0
|
||||||
|
let alttail = strpart(alttail, j)
|
||||||
|
let j = matchend(alttail, s:notslash . a:comma)
|
||||||
|
endif
|
||||||
|
endwhile
|
||||||
|
if a:0
|
||||||
|
let current = current . a:comma . strpart(alttail, 0, j-1)
|
||||||
|
endif
|
||||||
|
return current
|
||||||
|
endfun
|
||||||
|
|
||||||
|
" Call this function to turn on debugging information. Every time the main
|
||||||
|
" script is run, buffer variables will be saved. These can be used directly
|
||||||
|
" or viewed using the menu items below.
|
||||||
|
if !exists(":MatchDebug")
|
||||||
|
command! -nargs=0 MatchDebug call s:Match_debug()
|
||||||
|
endif
|
||||||
|
|
||||||
|
fun! s:Match_debug()
|
||||||
|
let b:match_debug = 1 " Save debugging information.
|
||||||
|
" pat = all of b:match_words with backrefs parsed
|
||||||
|
amenu &Matchit.&pat :echo b:match_pat<CR>
|
||||||
|
" match = bit of text that is recognized as a match
|
||||||
|
amenu &Matchit.&match :echo b:match_match<CR>
|
||||||
|
" curcol = cursor column of the start of the matching text
|
||||||
|
amenu &Matchit.&curcol :echo b:match_col<CR>
|
||||||
|
" wholeBR = matching group, original version
|
||||||
|
amenu &Matchit.wh&oleBR :echo b:match_wholeBR<CR>
|
||||||
|
" iniBR = 'if' piece, original version
|
||||||
|
amenu &Matchit.ini&BR :echo b:match_iniBR<CR>
|
||||||
|
" ini = 'if' piece, with all backrefs resolved from match
|
||||||
|
amenu &Matchit.&ini :echo b:match_ini<CR>
|
||||||
|
" tail = 'else\|endif' piece, with all backrefs resolved from match
|
||||||
|
amenu &Matchit.&tail :echo b:match_tail<CR>
|
||||||
|
" fin = 'endif' piece, with all backrefs resolved from match
|
||||||
|
amenu &Matchit.&word :echo b:match_word<CR>
|
||||||
|
" '\'.d in ini refers to the same thing as '\'.table[d] in word.
|
||||||
|
amenu &Matchit.t&able :echo '0:' . b:match_table . ':9'<CR>
|
||||||
|
endfun
|
||||||
|
|
||||||
|
" Jump to the nearest unmatched "(" or "if" or "<tag>" if a:spflag == "bW"
|
||||||
|
" or the nearest unmatched "</tag>" or "endif" or ")" if a:spflag == "W".
|
||||||
|
" Return a "mark" for the original position, so that
|
||||||
|
" let m = MultiMatch("bW", "n") ... execute m
|
||||||
|
" will return to the original position. If there is a problem, do not
|
||||||
|
" move the cursor and return "", unless a count is given, in which case
|
||||||
|
" go up or down as many levels as possible and again return "".
|
||||||
|
" TODO This relies on the same patterns as % matching. It might be a good
|
||||||
|
" idea to give it its own matching patterns.
|
||||||
|
fun! s:MultiMatch(spflag, mode)
|
||||||
|
if !exists("b:match_words") || b:match_words == ""
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
let restore_options = (&ic ? "" : "no") . "ignorecase"
|
||||||
|
if exists("b:match_ignorecase")
|
||||||
|
let &ignorecase = b:match_ignorecase
|
||||||
|
endif
|
||||||
|
let startline = line(".")
|
||||||
|
let startcol = col(".")
|
||||||
|
|
||||||
|
" First step: if not already done, set the script variables
|
||||||
|
" s:do_BR flag for whether there are backrefs
|
||||||
|
" s:pat parsed version of b:match_words
|
||||||
|
" s:all regexp based on s:pat and the default groups
|
||||||
|
" This part is copied and slightly modified from s:Match_wrapper().
|
||||||
|
let default = escape(&mps, '[$^.*~\\/?]') . (strlen(&mps) ? "," : "") .
|
||||||
|
\ '\/\*:\*\/,#if\%(def\)\=:#else\>:#elif\>:#endif\>'
|
||||||
|
" Allow b:match_words = "GetVimMatchWords()" .
|
||||||
|
if b:match_words =~ ":"
|
||||||
|
let match_words = b:match_words
|
||||||
|
else
|
||||||
|
execute "let match_words =" b:match_words
|
||||||
|
endif
|
||||||
|
if (match_words != s:last_words) || (&mps != s:last_mps) ||
|
||||||
|
\ exists("b:match_debug")
|
||||||
|
let s:last_words = match_words
|
||||||
|
let s:last_mps = &mps
|
||||||
|
if match_words !~ s:notslash . '\\\d'
|
||||||
|
let s:do_BR = 0
|
||||||
|
let s:pat = match_words
|
||||||
|
else
|
||||||
|
let s:do_BR = 1
|
||||||
|
let s:pat = s:ParseWords(match_words)
|
||||||
|
endif
|
||||||
|
let s:all = '\%(' . substitute(s:pat . (strlen(s:pat)?",":"") . default,
|
||||||
|
\ '[,:]\+','\\|','g') . '\)'
|
||||||
|
if exists("b:match_debug")
|
||||||
|
let b:match_pat = s:pat
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Second step: figure out the patterns for searchpair()
|
||||||
|
" and save the screen, cursor position, and 'ignorecase'.
|
||||||
|
" - TODO: A lot of this is copied from s:Match_wrapper().
|
||||||
|
" - maybe even more functionality should be split off
|
||||||
|
" - into separate functions!
|
||||||
|
let cdefault = (s:pat =~ '[^,]$' ? "," : "") . default
|
||||||
|
let open = substitute(s:pat . cdefault,
|
||||||
|
\ s:notslash . '\zs:.\{-}' . s:notslash . ',', '\\),\\(', 'g')
|
||||||
|
let open = '\(' . substitute(open, s:notslash . '\zs:.*$', '\\)', '')
|
||||||
|
let close = substitute(s:pat . cdefault,
|
||||||
|
\ s:notslash . '\zs,.\{-}' . s:notslash . ':', '\\),\\(', 'g')
|
||||||
|
let close = substitute(close, '^.\{-}' . s:notslash . ':', '\\(', '') . '\)'
|
||||||
|
if exists("b:match_skip")
|
||||||
|
let skip = b:match_skip
|
||||||
|
elseif exists("b:match_comment") " backwards compatibility and testing!
|
||||||
|
let skip = "r:" . b:match_comment
|
||||||
|
else
|
||||||
|
let skip = 's:comment\|string'
|
||||||
|
endif
|
||||||
|
let skip = s:ParseSkip(skip)
|
||||||
|
" let restore_cursor = line(".") . "G" . virtcol(".") . "|"
|
||||||
|
" normal! H
|
||||||
|
" let restore_cursor = "normal!" . line(".") . "Gzt" . restore_cursor
|
||||||
|
let restore_cursor = virtcol(".") . "|"
|
||||||
|
normal! g0
|
||||||
|
let restore_cursor = line(".") . "G" . virtcol(".") . "|zs" . restore_cursor
|
||||||
|
normal! H
|
||||||
|
let restore_cursor = "normal!" . line(".") . "Gzt" . restore_cursor
|
||||||
|
execute restore_cursor
|
||||||
|
|
||||||
|
" Third step: call searchpair().
|
||||||
|
" Replace '\('--but not '\\('--with '\%(' and ',' with '\|'.
|
||||||
|
let openpat = substitute(open, '\(\\\@<!\(\\\\\)*\)\@<=\\(', '\\%(', 'g')
|
||||||
|
let openpat = substitute(openpat, ',', '\\|', 'g')
|
||||||
|
let closepat = substitute(close, '\(\\\@<!\(\\\\\)*\)\@<=\\(', '\\%(', 'g')
|
||||||
|
let closepat = substitute(closepat, ',', '\\|', 'g')
|
||||||
|
if skip =~ 'synID' && !(has("syntax") && exists("g:syntax_on"))
|
||||||
|
let skip = '0'
|
||||||
|
else
|
||||||
|
execute "if " . skip . "| let skip = '0' | endif"
|
||||||
|
endif
|
||||||
|
mark '
|
||||||
|
let level = v:count1
|
||||||
|
while level
|
||||||
|
if searchpair(openpat, '', closepat, a:spflag, skip) < 1
|
||||||
|
call s:CleanUp(restore_options, a:mode, startline, startcol)
|
||||||
|
return ""
|
||||||
|
endif
|
||||||
|
let level = level - 1
|
||||||
|
endwhile
|
||||||
|
|
||||||
|
" Restore options and return a string to restore the original position.
|
||||||
|
call s:CleanUp(restore_options, a:mode, startline, startcol)
|
||||||
|
return restore_cursor
|
||||||
|
endfun
|
||||||
|
|
||||||
|
" Search backwards for "if" or "while" or "<tag>" or ...
|
||||||
|
" and return "endif" or "endwhile" or "</tag>" or ... .
|
||||||
|
" For now, this uses b:match_words and the same script variables
|
||||||
|
" as s:Match_wrapper() . Later, it may get its own patterns,
|
||||||
|
" either from a buffer variable or passed as arguments.
|
||||||
|
" fun! s:Autocomplete()
|
||||||
|
" echo "autocomplete not yet implemented :-("
|
||||||
|
" if !exists("b:match_words") || b:match_words == ""
|
||||||
|
" return ""
|
||||||
|
" end
|
||||||
|
" let startpos = s:MultiMatch("bW")
|
||||||
|
"
|
||||||
|
" if startpos == ""
|
||||||
|
" return ""
|
||||||
|
" endif
|
||||||
|
" " - TODO: figure out whether 'if' or '<tag>' matched, and construct
|
||||||
|
" " - the appropriate closing.
|
||||||
|
" let matchline = getline(".")
|
||||||
|
" let curcol = col(".") - 1
|
||||||
|
" " - TODO: Change the s:all argument if there is a new set of match pats.
|
||||||
|
" let regexp = s:Wholematch(matchline, s:all, curcol)
|
||||||
|
" let suf = strlen(matchline) - matchend(matchline, regexp)
|
||||||
|
" let prefix = (curcol ? '^.\{' . curcol . '}\%(' : '^\%(')
|
||||||
|
" let suffix = (suf ? '\).\{' . suf . '}$' : '\)$')
|
||||||
|
" " Reconstruct the version with unresolved backrefs.
|
||||||
|
" let patBR = substitute(b:match_words.',', '[,:]*,[,:]*', ',', 'g')
|
||||||
|
" let patBR = substitute(patBR, ':\{2,}', ':', "g")
|
||||||
|
" " Now, set group and groupBR to the matching group: 'if:endif' or
|
||||||
|
" " 'while:endwhile' or whatever.
|
||||||
|
" let group = s:Choose(s:pat, matchline, ",", ":", prefix, suffix, patBR)
|
||||||
|
" let i = matchend(group, s:notslash . ",")
|
||||||
|
" let groupBR = strpart(group, i)
|
||||||
|
" let group = strpart(group, 0, i-1)
|
||||||
|
" " Now, matchline =~ prefix . substitute(group,':','\|','g') . suffix
|
||||||
|
" if s:do_BR
|
||||||
|
" let group = s:InsertRefs(groupBR, prefix, group, suffix, matchline)
|
||||||
|
" endif
|
||||||
|
" " let g:group = group
|
||||||
|
"
|
||||||
|
" " - TODO: Construct the closing from group.
|
||||||
|
" let fake = "end" . expand("<cword>")
|
||||||
|
" execute startpos
|
||||||
|
" return fake
|
||||||
|
" endfun
|
||||||
|
|
||||||
|
" Close all open structures. "Get the heck out of here!"
|
||||||
|
" fun! s:Gthhoh()
|
||||||
|
" let close = s:Autocomplete()
|
||||||
|
" while strlen(close)
|
||||||
|
" put=close
|
||||||
|
" let close = s:Autocomplete()
|
||||||
|
" endwhile
|
||||||
|
" endfun
|
||||||
|
|
||||||
|
" Parse special strings as typical skip arguments for searchpair():
|
||||||
|
" 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
|
||||||
|
fun! s:ParseSkip(str)
|
||||||
|
let skip = a:str
|
||||||
|
if skip[1] == ":"
|
||||||
|
if skip[0] == "s"
|
||||||
|
let skip = "synIDattr(synID(line('.'),col('.'),1),'name') =~? '" .
|
||||||
|
\ strpart(skip,2) . "'"
|
||||||
|
elseif skip[0] == "S"
|
||||||
|
let skip = "synIDattr(synID(line('.'),col('.'),1),'name') !~? '" .
|
||||||
|
\ strpart(skip,2) . "'"
|
||||||
|
elseif skip[0] == "r"
|
||||||
|
let skip = "strpart(getline('.'),0,col('.'))=~'" . strpart(skip,2). "'"
|
||||||
|
elseif skip[0] == "R"
|
||||||
|
let skip = "strpart(getline('.'),0,col('.'))!~'" . strpart(skip,2). "'"
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
return skip
|
||||||
|
endfun
|
||||||
|
|
||||||
|
let &cpo = s:save_cpo
|
||||||
|
|
||||||
|
" vim:sts=2:sw=2:
|
|
@ -0,0 +1,10 @@
|
||||||
|
if did_filetype()
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
for i in range(1, 5)
|
||||||
|
if getline(i) =~ '^version 12'
|
||||||
|
setfiletype ciscoconfig
|
||||||
|
elseif getline(i) =~ '^ASA Version'
|
||||||
|
setfiletype ciscoconfig
|
||||||
|
endif
|
||||||
|
endfor
|
|
@ -0,0 +1,80 @@
|
||||||
|
" Vim syntax file
|
||||||
|
" Language: Cisco IOS config file
|
||||||
|
" Last Change: 2008-07-16
|
||||||
|
|
||||||
|
if version < 600
|
||||||
|
syntax clear
|
||||||
|
elseif exists("b:current_syntax")
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
|
||||||
|
setlocal iskeyword+=-
|
||||||
|
|
||||||
|
syn match ciscoComment "^\s*!.*$"
|
||||||
|
hi def link ciscoComment Comment
|
||||||
|
|
||||||
|
syn match ciscoIpAddr /\<\(25[0-5]\|2[0-4][0-9]\|[01]\?[0-9][0-9]\?\)\.\(25[0-5]\|2[0-4][0-9]\|[01]\?[0-9][0-9]\?\)\.\(25[0-5]\|2[0-4][0-9]\|[01]\?[0-9][0-9]\?\)\.\(25[0-5]\|2[0-4][0-9]\|[01]\?[0-9][0-9]\?\)\>/
|
||||||
|
hi def link ciscoIpAddr Number
|
||||||
|
|
||||||
|
syn match ciscoIfName /\<\(Loopback\|Tunnel\|Dialer\)[0-9][0-9]*\>/
|
||||||
|
syn match ciscoIfName +\<\(Ethernet\|FastEthernet\|GigabitEthernet\)[0-9][0-9]*/[0-9][0-9]*\(/[0-9][0-9]*\)\?\(\.[0-9][0-9]*\)\?\>+
|
||||||
|
syn match ciscoIfName +\<ATM[0-9][0-9]*\(/[0-9][0-9]*\)*\(\.[0-9][0-9]*\)\?\>+
|
||||||
|
hi def link ciscoIfName Identifier
|
||||||
|
|
||||||
|
syn match ciscoWord contained +[a-zA-Z0-9-_]*+
|
||||||
|
hi def link ciscoWord String
|
||||||
|
|
||||||
|
|
||||||
|
syn region ciscoUsernames start=+^username\s+ skip=+^username\s+ end=+^\S+me=s-1 fold
|
||||||
|
syn region ciscoIpHosts start=+^ip host\s+ skip=+^ip host\s+ end=+^\S+me=s-1 fold
|
||||||
|
|
||||||
|
|
||||||
|
syn region ciscoInterfaces start=+^interface\s+ skip=+^\(!\n\)\?interface\s+ end=+^\S+me=s-1 fold contains=ciscoInterfaceRegion
|
||||||
|
syn region ciscoInterfaceRegion contained start=+^interface\s+ end=+^\S+me=s-1 fold contains=ciscoIpAddr,ciscoIfName,ciscoComment
|
||||||
|
|
||||||
|
|
||||||
|
syn region ciscoRouters start=+^router\s+ skip=+^\(!\n\)\?router\s+ end=+^\S+me=s-1 fold contains=ciscoRouterRegion
|
||||||
|
syn region ciscoRouterRegion start=+^router\s+ end=+^\S+me=s-1 contained fold contains=ciscoIpAddr,ciscoIfName,ciscoComment
|
||||||
|
|
||||||
|
|
||||||
|
syn region ciscoIpRoutes start=+^ip route\s+ end=+^\(ip route\)\@!+me=s-1 fold contains=ciscoIpRoute
|
||||||
|
syn match ciscoIpRoute +^ip route.*$+ contained skipwhite contains=ciscoIpAddr,ciscoNumber,ciscoIfName
|
||||||
|
|
||||||
|
|
||||||
|
syn region ciscoIpAccessLists start=+^ip access-list\s+ skip=+^\(!\n\)\?ip access-list\s+ end=+^\S+me=s-1 fold contains=ciscoIpAccessList
|
||||||
|
syn region ciscoIpAccessList contained start=+^ip access-list\s+ end=+^\S+me=s-1 fold contains=ciscoIpAccessListNamed,ciscoIpAddr,ciscoIfName,ciscoComment,ciscoAclKeywords,ciscoAclOperator
|
||||||
|
syn match ciscoIpAccessListNamed +^ip access-list \(standard\|extended\) + contained nextgroup=ciscoWord skipwhite
|
||||||
|
syn keyword ciscoAclKeywords contained skipwhite host any
|
||||||
|
syn keyword ciscoAclOperator contained skipwhite eq ne
|
||||||
|
hi def link ciscoAclKeywords Keyword
|
||||||
|
hi def link ciscoAclOperator Special
|
||||||
|
|
||||||
|
syn region ciscoAccessLists start=+^access-list\s+ skip=+^access-list\s+ end=+^\S+me=s-1 fold contains=ciscoAccessList
|
||||||
|
syn region ciscoAccessList start=+^access-list \z(\d\+\)\ + skip=+^access-list \z1 + end=+^\S+me=s-1 contained fold contains=ciscoIpAddr,ciscoIfName
|
||||||
|
|
||||||
|
|
||||||
|
syn region ciscoRouteMaps start=+^route-map\s+ skip=+^\(!\n\)\?route-map\s+ end=+^\S+me=s-1 fold contains=ciscoRouteMap
|
||||||
|
syn region ciscoRouteMap contained start=+^route-map\s+ end=+^\S+me=s-1 fold contains=ciscoIpAddr,ciscoIfName,ciscoComment
|
||||||
|
|
||||||
|
|
||||||
|
syn region ciscoCryptoIsakmp start=+^crypto isakmp\s+ end=+^\S+me=s-1 fold
|
||||||
|
|
||||||
|
syn region ciscoCryptoIsakmpKeys start=+^crypto isakmp key\s+ skip=+^crypto isakmp key\s+ end=+^\S+me=s-1 fold
|
||||||
|
|
||||||
|
syn region ciscoCryptoIpsecTses start=+^crypto ipsec transform-set\s+ skip=+^crypto ipsec transform-set\s+ end=+^\S+me=s-1 fold contains=ciscoCryptoIpsecTs
|
||||||
|
syn match ciscoCryptoIpsecTs contained +^crypto ipsec transform-set + nextgroup=ciscoWord skipwhite
|
||||||
|
|
||||||
|
syn region ciscoCryptoMaps start=+^crypto map\s+ skip=+^crypto map\s+ end=+^\S+me=s-1 fold contains=ciscoCryptoMap
|
||||||
|
syn region ciscoCryptoMap start=+^crypto map \z(\S\+\)\ + skip=+^crypto map \z1 + end=+^\S+me=s-1 contained fold contains=ciscoCryptoMapEntry
|
||||||
|
syn region ciscoCryptoMapEntry contained start=+^crypto map\s+ end=+^\S+me=s-1 fold contains=ciscoCryptoMapName,ciscoIpAddr
|
||||||
|
syn match ciscoCryptoMapName contained +^crypto map + nextgroup=ciscoWord skipwhite
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
set foldmethod=syntax
|
||||||
|
|
||||||
|
let b:current_syntax = "ciscoconfig"
|
||||||
|
|
||||||
|
" vim: set ts=4
|
|
@ -0,0 +1,18 @@
|
||||||
|
" enble modelines (i.e. `# vim: set ft=rst`, etc.)
|
||||||
|
set modeline
|
||||||
|
" Need line numbers, of course
|
||||||
|
set number
|
||||||
|
" Incremental search (i.e. find-as-you-type)
|
||||||
|
set incsearch
|
||||||
|
" Big fat red bar telling you your lines are too long
|
||||||
|
set colorcolumn=80
|
||||||
|
" Open windows with `:vsplit` on the right instead of the left
|
||||||
|
set splitright
|
||||||
|
|
||||||
|
" Use a dark theme for console editing
|
||||||
|
colorscheme torte
|
||||||
|
|
||||||
|
" The ESC key is way out of the way. Use jj to switch to normal mode
|
||||||
|
inoremap jj <esc><right>
|
||||||
|
" Shift+Tab to un-indent
|
||||||
|
inoremap <S-TAB> <C-D>
|
Reference in New Issue