Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 1 | *matchit.txt* Extended "%" matching |
| 2 | |
| 3 | For instructions on installing this file, type |
| 4 | :help matchit-install |
| 5 | inside Vim. |
| 6 | |
Bram Moolenaar | 446cb83 | 2008-06-24 21:56:24 +0000 | [diff] [blame] | 7 | For Vim version 6.3. Last change: 2007 Aug 29 |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 8 | |
| 9 | |
| 10 | VIM REFERENCE MANUAL by Benji Fisher |
| 11 | |
| 12 | *matchit* *matchit.vim* |
| 13 | |
| 14 | 1. Extended matching with "%" |matchit-intro| |
| 15 | 2. Activation |matchit-activate| |
| 16 | 3. Configuration |matchit-configure| |
| 17 | 4. Supporting a New Language |matchit-newlang| |
| 18 | 5. Known Bugs and Limitations |matchit-bugs| |
| 19 | |
| 20 | The functionality mentioned here is a plugin, see |add-plugin|. |
| 21 | This plugin is only available if 'compatible' is not set. |
| 22 | You can avoid loading this plugin by setting the "loaded_matchit" variable |
| 23 | in your |vimrc| file: > |
| 24 | :let loaded_matchit = 1 |
| 25 | |
| 26 | {Vi does not have any of this} |
| 27 | |
| 28 | ============================================================================== |
| 29 | 1. Extended matching with "%" *matchit-intro* |
| 30 | |
| 31 | *matchit-%* |
| 32 | % Cycle forward through matching groups, such as "if", "else", "endif", |
| 33 | as specified by |b:match_words|. |
| 34 | |
| 35 | *g%* *v_g%* *o_g%* |
| 36 | g% Cycle backwards through matching groups, as specified by |
Bram Moolenaar | 446cb83 | 2008-06-24 21:56:24 +0000 | [diff] [blame] | 37 | |b:match_words|. For example, go from "if" to "endif" to "else". |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 38 | |
| 39 | *[%* *v_[%* *o_[%* |
| 40 | [% Go to [count] previous unmatched group, as specified by |
| 41 | |b:match_words|. Similar to |[{|. |
| 42 | |
| 43 | *]%* *v_]%* *o_]%* |
| 44 | ]% Go to [count] next unmatched group, as specified by |
| 45 | |b:match_words|. Similar to |]}|. |
| 46 | |
| 47 | *v_a%* |
| 48 | a% In Visual mode, select the matching group, as specified by |
| 49 | |b:match_words|, containing the cursor. Similar to |v_a[|. |
| 50 | A [count] is ignored, and only the first character of the closing |
| 51 | pattern is selected. |
| 52 | |
| 53 | In Vim, as in plain vi, the percent key, |%|, jumps the cursor from a brace, |
| 54 | bracket, or paren to its match. This can be configured with the 'matchpairs' |
| 55 | option. The matchit plugin extends this in several ways: |
| 56 | |
| 57 | You can match whole words, such as "if" and "endif", not just |
| 58 | single characters. You can also specify a |regular-expression|. |
| 59 | You can define groups with more than two words, such as "if", |
| 60 | "else", "endif". Banging on the "%" key will cycle from the "if" to |
| 61 | the first "else", the next "else", ..., the closing "endif", and back |
| 62 | to the opening "if". Nested structures are skipped. Using |g%| goes |
| 63 | in the reverse direction. |
| 64 | By default, words inside comments and strings are ignored, unless |
| 65 | the cursor is inside a comment or string when you type "%". If the |
| 66 | only thing you want to do is modify the behavior of "%" so that it |
Bram Moolenaar | 446cb83 | 2008-06-24 21:56:24 +0000 | [diff] [blame] | 67 | behaves this way, you do not have to define |b:match_words|, since the |
| 68 | script uses the 'matchpairs' option as well as this variable. |
| 69 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 70 | See |matchit-details| for details on what the script does, and |b:match_words| |
| 71 | for how to specify matching patterns. |
| 72 | |
| 73 | MODES: *matchit-modes* *matchit-v_%* *matchit-o_%* |
| 74 | |
| 75 | Mostly, % and related motions (|g%| and |[%| and |]%|) work just like built-in |
| 76 | |motion| commands in |Operator-pending| and |Visual| modes. However, you |
| 77 | cannot make these motions |linewise| or |characterwise|, since the |:omap|s |
| 78 | that define them start with "v" in order to make the default behavior |
| 79 | inclusive. (See |o_v|.) In other words, "dV%" will not work. The |
| 80 | work-around is to go through Visual mode: "V%d" will work. |
| 81 | |
| 82 | LANGUAGES: *matchit-languages* |
| 83 | |
| 84 | Currently, the following languages are supported: Ada, ASP with VBS, Csh, |
| 85 | DTD, Entity, Essbase, Fortran, HTML, JSP (same as HTML), LaTeX, Lua, Pascal, |
| 86 | SGML, Shell, Tcsh, Vim, XML. Other languages may already have support via |
Bram Moolenaar | 446cb83 | 2008-06-24 21:56:24 +0000 | [diff] [blame] | 87 | the default |filetype-plugin|s in the standard vim distribution. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 88 | |
| 89 | To support a new language, see |matchit-newlang| below. |
| 90 | |
| 91 | DETAILS: *matchit-details* *matchit-parse* |
| 92 | |
| 93 | Here is an outline of what matchit.vim does each time you hit the "%" key. If |
| 94 | there are |backref|s in |b:match_words| then the first step is to produce a |
| 95 | version in which these back references have been eliminated; if there are no |
| 96 | |backref|s then this step is skipped. This step is called parsing. For |
| 97 | example, "\(foo\|bar\):end\1" is parsed to yield |
| 98 | "\(foo\|bar\):end\(foo\|bar\)". This can get tricky, especially if there are |
| 99 | nested groups. If debugging is turned on, the parsed version is saved as |
| 100 | |b:match_pat|. |
| 101 | |
| 102 | *matchit-choose* |
| 103 | Next, the script looks for a word on the current line that matches the pattern |
| 104 | just constructed. It includes the patterns from the 'matchpairs' option. |
| 105 | The goal is to do what you expect, which turns out to be a little complicated. |
| 106 | The script follows these rules: |
| 107 | |
| 108 | Insist on a match that ends on or after the cursor. |
| 109 | Prefer a match that includes the cursor position (that is, one that |
| 110 | starts on or before the cursor). |
| 111 | Prefer a match that starts as close to the cursor as possible. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 112 | If more than one pattern in |b:match_words| matches, choose the one |
| 113 | that is listed first. |
| 114 | |
| 115 | Examples: |
| 116 | |
| 117 | Suppose you > |
| 118 | :let b:match_words = '<:>,<tag>:</tag>' |
| 119 | < and hit "%" with the cursor on or before the "<" in "a <tag> is born". |
| 120 | The pattern '<' comes first, so it is preferred over '<tag>', which |
| 121 | also matches. If the cursor is on the "t", however, then '<tag>' is |
| 122 | preferred, because this matches a bit of text containing the cursor. |
| 123 | If the two groups of patterns were reversed then '<' would never be |
| 124 | preferred. |
| 125 | |
| 126 | Suppose you > |
| 127 | :let b:match_words = 'if:end if' |
| 128 | < (Note the space!) and hit "%" with the cursor at the end of "end if". |
| 129 | Then "if" matches, which is probably not what you want, but if the |
| 130 | cursor starts on the "end " then "end if" is chosen. (You can avoid |
| 131 | this problem by using a more complicated pattern.) |
| 132 | |
Bram Moolenaar | 446cb83 | 2008-06-24 21:56:24 +0000 | [diff] [blame] | 133 | If there is no match, the cursor does not move. (Before version 1.13 of the |
| 134 | script, it would fall back on the usual behavior of |%|). If debugging is |
| 135 | turned on, the matched bit of text is saved as |b:match_match| and the cursor |
| 136 | column of the start of the match is saved as |b:match_col|. |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 137 | |
| 138 | Next, the script looks through |b:match_words| (original and parsed versions) |
| 139 | for the group and pattern that match. If debugging is turned on, the group is |
| 140 | saved as |b:match_ini| (the first pattern) and |b:match_tail| (the rest). If |
| 141 | there are |backref|s then, in addition, the matching pattern is saved as |
| 142 | |b:match_word| and a table of translations is saved as |b:match_table|. If |
| 143 | there are |backref|s, these are determined from the matching pattern and |
| 144 | |b:match_match| and substituted into each pattern in the matching group. |
| 145 | |
| 146 | The script decides whether to search forwards or backwards and chooses |
| 147 | arguments for the |searchpair()| function. Then, the cursor is moved to the |
| 148 | start of the match, and |searchpair()| is called. By default, matching |
| 149 | structures inside strings and comments are ignored. This can be changed by |
| 150 | setting |b:match_skip|. |
| 151 | |
| 152 | ============================================================================== |
| 153 | 2. Activation *matchit-activate* |
| 154 | |
| 155 | You can use this script as a plugin, by copying it to your plugin directory. |
| 156 | See |add-global-plugin| for instructions. You can also add a line to your |
| 157 | |vimrc| file, such as > |
| 158 | :source $VIMRUNTIME/macros/matchit.vim |
| 159 | or > |
| 160 | :runtime macros/matchit.vim |
| 161 | Either way, the script should start working the next time you start up Vim. |
| 162 | |
Bram Moolenaar | 446cb83 | 2008-06-24 21:56:24 +0000 | [diff] [blame] | 163 | (Earlier versions of the script did nothing unless a |buffer-variable| named |
| 164 | |b:match_words| was defined. Even earlier versions contained autocommands |
| 165 | that set this variable for various file types. Now, |b:match_words| is |
| 166 | defined in many of the default |filetype-plugin|s instead.) |
| 167 | |
| 168 | For a new language, you can add autocommands to the script or to your vimrc |
| 169 | file, but the recommended method is to add a line such as > |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 170 | let b:match_words = '\<foo\>:\<bar\>' |
| 171 | to the |filetype-plugin| for your language. See |b:match_words| below for how |
| 172 | this variable is interpreted. |
| 173 | |
| 174 | TROUBLESHOOTING *matchit-troubleshoot* |
| 175 | |
| 176 | The script should work in most installations of Vim. It may not work if Vim |
| 177 | was compiled with a minimal feature set, for example if the |+syntax| option |
| 178 | was not enabled. If your Vim has support for syntax compiled in, but you do |
| 179 | not have |syntax| highlighting turned on, matchit.vim should work, but it may |
| 180 | fail to skip matching groups in comments and strings. If the |filetype| |
| 181 | mechanism is turned off, the |b:match_words| variable will probably not be |
| 182 | defined automatically. |
| 183 | |
| 184 | ============================================================================== |
| 185 | 3. Configuration *matchit-configure* |
| 186 | |
| 187 | There are several variables that govern the behavior of matchit.vim. Note |
| 188 | that these are variables local to the buffer, not options, so use |:let| to |
| 189 | define them, not |:set|. Some of these variables have values that matter; for |
| 190 | others, it only matters whether the variable has been defined. All of these |
| 191 | can be defined in the |filetype-plugin| or autocommand that defines |
| 192 | |b:match_words| or "on the fly." |
| 193 | |
| 194 | The main variable is |b:match_words|. It is described in the section below on |
| 195 | supporting a new language. |
| 196 | |
| 197 | *MatchError* *matchit-hl* *matchit-highlight* |
| 198 | MatchError is the highlight group for error messages from the script. By |
| 199 | default, it is linked to WarningMsg. If you do not want to be bothered by |
| 200 | error messages, you can define this to be something invisible. For example, |
| 201 | if you use the GUI version of Vim and your command line is normally white, you |
| 202 | can do > |
| 203 | :hi MatchError guifg=white guibg=white |
| 204 | < |
| 205 | *b:match_ignorecase* |
| 206 | If you > |
| 207 | :let b:match_ignorecase = 1 |
| 208 | then matchit.vim acts as if 'ignorecase' is set: for example, "end" and "END" |
| 209 | are equivalent. If you > |
| 210 | :let b:match_ignorecase = 0 |
| 211 | then matchit.vim treats "end" and "END" differently. (There will be no |
| 212 | b:match_infercase option unless someone requests it.) |
| 213 | |
| 214 | *b:match_debug* |
| 215 | Define b:match_debug if you want debugging information to be saved. See |
| 216 | |matchit-debug|, below. |
| 217 | |
| 218 | *b:match_skip* |
| 219 | If b:match_skip is defined, it is passed as the skip argument to |
| 220 | |searchpair()|. This controls when matching structures are skipped, or |
| 221 | ignored. By default, they are ignored inside comments and strings, as |
| 222 | determined by the |syntax| mechanism. (If syntax highlighting is turned off, |
| 223 | nothing is skipped.) You can set b:match_skip to a string, which evaluates to |
| 224 | a non-zero, numerical value if the match is to be skipped or zero if the match |
| 225 | should not be skipped. In addition, the following special values are |
| 226 | supported by matchit.vim: |
| 227 | s:foo becomes (current syntax item) =~ foo |
| 228 | S:foo becomes (current syntax item) !~ foo |
| 229 | r:foo becomes (line before cursor) =~ foo |
| 230 | R:foo becomes (line before cursor) !~ foo |
| 231 | (The "s" is meant to suggest "syntax", and the "r" is meant to suggest |
| 232 | "regular expression".) |
| 233 | |
| 234 | Examples: |
| 235 | |
| 236 | You can get the default behavior with > |
| 237 | :let b:match_skip = 's:comment\|string' |
| 238 | < |
| 239 | If you want to skip matching structures unless they are at the start |
| 240 | of the line (ignoring whitespace) then you can > |
| 241 | :let b:match_skip = 'R:^\s*' |
| 242 | < Do not do this if strings or comments can span several lines, since |
| 243 | the normal syntax checking will not be done if you set b:match_skip. |
| 244 | |
| 245 | In LaTeX, since "%" is used as the comment character, you can > |
| 246 | :let b:match_skip = 'r:%' |
| 247 | < Unfortunately, this will skip anything after "\%", an escaped "%". To |
| 248 | allow for this, and also "\\%" (an excaped backslash followed by the |
| 249 | comment character) you can > |
| 250 | :let b:match_skip = 'r:\(^\|[^\\]\)\(\\\\\)*%' |
| 251 | < |
Bram Moolenaar | c01140a | 2006-03-24 22:21:52 +0000 | [diff] [blame] | 252 | See the $VIMRUNTIME/ftplugin/vim.vim for an example that uses both |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 253 | syntax and a regular expression. |
| 254 | |
| 255 | ============================================================================== |
| 256 | 4. Supporting a New Language *matchit-newlang* |
| 257 | *b:match_words* |
| 258 | In order for matchit.vim to support a new language, you must define a suitable |
| 259 | pattern for |b:match_words|. You may also want to set some of the |
| 260 | |matchit-configure| variables, as described above. If your language has a |
| 261 | complicated syntax, or many keywords, you will need to know something about |
| 262 | Vim's |regular-expression|s. |
| 263 | |
| 264 | The format for |b:match_words| is similar to that of the 'matchpairs' option: |
| 265 | it is a comma (,)-separated list of groups; each group is a colon(:)-separated |
Bram Moolenaar | c01140a | 2006-03-24 22:21:52 +0000 | [diff] [blame] | 266 | list of patterns (regular expressions). Commas and backslashes that are part |
| 267 | of a pattern should be escaped with backslashes ('\:' and '\,'). It is OK to |
| 268 | have only one group; the effect is undefined if a group has only one pattern. |
| 269 | A simple example is > |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 270 | :let b:match_words = '\<if\>:\<endif\>,' |
| 271 | \ . '\<while\>:\<continue\>:\<break\>:\<endwhile\>' |
| 272 | (In Vim regular expressions, |\<| and |\>| denote word boundaries. Thus "if" |
| 273 | matches the end of "endif" but "\<if\>" does not.) Then banging on the "%" |
| 274 | key will bounce the cursor between "if" and the matching "endif"; and from |
| 275 | "while" to any matching "continue" or "break", then to the matching "endwhile" |
| 276 | and back to the "while". It is almost always easier to use |literal-string|s |
| 277 | (single quotes) as above: '\<if\>' rather than "\\<if\\>" and so on. |
| 278 | |
| 279 | Exception: If the ":" character does not appear in b:match_words, then it is |
| 280 | treated as an expression to be evaluated. For example, > |
| 281 | :let b:match_words = 'GetMatchWords()' |
| 282 | allows you to define a function. This can return a different string depending |
| 283 | on the current syntax, for example. |
| 284 | |
| 285 | Once you have defined the appropriate value of |b:match_words|, you will |
| 286 | probably want to have this set automatically each time you edit the |
| 287 | appropriate file type. The recommended way to do this is by adding the |
| 288 | definition to a |filetype-plugin| file. |
| 289 | |
| 290 | Tips: Be careful that your initial pattern does not match your final pattern. |
| 291 | See the example above for the use of word-boundary expressions. It is usually |
| 292 | better to use ".\{-}" (as many as necessary) instead of ".*" (as many as |
| 293 | possible). See |\{-|. For example, in the string "<tag>label</tag>", "<.*>" |
| 294 | matches the whole string whereas "<.\{-}>" and "<[^>]*>" match "<tag>" and |
| 295 | "</tag>". |
| 296 | |
| 297 | *matchit-spaces* *matchit-s:notend* |
| 298 | If "if" is to be paired with "end if" (Note the space!) then word boundaries |
| 299 | are not enough. Instead, define a regular expression s:notend that will match |
| 300 | anything but "end" and use it as follows: > |
| 301 | :let s:notend = '\%(\<end\s\+\)\@<!' |
| 302 | :let b:match_words = s:notend . '\<if\>:\<end\s\+if\>' |
| 303 | < *matchit-s:sol* |
| 304 | This is a simplified version of what is done for Ada. The s:notend is a |
| 305 | |script-variable|. Similarly, you may want to define a start-of-line regular |
| 306 | expression > |
| 307 | :let s:sol = '\%(^\|;\)\s*' |
| 308 | if keywords are only recognized after the start of a line or after a |
| 309 | semicolon (;), with optional white space. |
| 310 | |
| 311 | *matchit-backref* *matchit-\1* |
| 312 | In any group, the expressions |\1|, |\2|, ..., |\9| refer to parts of the |
| 313 | INITIAL pattern enclosed in |\(|escaped parentheses|\)|. These are referred |
| 314 | to as back references, or backrefs. For example, > |
| 315 | :let b:match_words = '\<b\(o\+\)\>:\(h\)\1\>' |
| 316 | means that "bo" pairs with "ho" and "boo" pairs with "hoo" and so on. Note |
| 317 | that "\1" does not refer to the "\(h\)" in this example. If you have |
| 318 | "\(nested \(parentheses\)\) then "\d" refers to the d-th "\(" and everything |
| 319 | up to and including the matching "\)": in "\(nested\(parentheses\)\)", "\1" |
| 320 | refers to everything and "\2" refers to "\(parentheses\)". If you use a |
| 321 | variable such as |s:notend| or |s:sol| in the previous paragraph then remember |
| 322 | to count any "\(" patterns in this variable. You do not have to count groups |
| 323 | defined by |\%(\)|. |
| 324 | |
| 325 | It should be possible to resolve back references from any pattern in the |
| 326 | group. For example, > |
| 327 | :let b:match_words = '\(foo\)\(bar\):more\1:and\2:end\1\2' |
| 328 | would not work because "\2" cannot be determined from "morefoo" and "\1" |
| 329 | cannot be determined from "andbar". On the other hand, > |
| 330 | :let b:match_words = '\(\(foo\)\(bar\)\):\3\2:end\1' |
| 331 | should work (and have the same effect as "foobar:barfoo:endfoobar"), although |
| 332 | this has not been thoroughly tested. |
| 333 | |
| 334 | You can use |zero-width| patterns such as |\@<=| and |\zs|. (The latter has |
| 335 | not been thouroughly tested in matchit.vim.) For example, if the keyword "if" |
| 336 | must occur at the start of the line, with optional white space, you might use |
| 337 | the pattern "\(^\s*\)\@<=if" so that the cursor will end on the "i" instead of |
| 338 | at the start of the line. For another example, if HTML had only one tag then |
| 339 | one could > |
| 340 | :let b:match_words = '<:>,<\@<=tag>:<\@<=/tag>' |
| 341 | so that "%" can bounce between matching "<" and ">" pairs or (starting on |
| 342 | "tag" or "/tag") between matching tags. Without the |\@<=|, the script would |
| 343 | bounce from "tag" to the "<" in "</tag>", and another "%" would not take you |
| 344 | back to where you started. |
| 345 | |
| 346 | DEBUGGING *matchit-debug* *:MatchDebug* |
| 347 | |
| 348 | If you are having trouble figuring out the appropriate definition of |
| 349 | |b:match_words| then you can take advantage of the same information I use when |
| 350 | debugging the script. This is especially true if you are not sure whether |
| 351 | your patterns or my script are at fault! To make this more convenient, I have |
| 352 | made the command :MatchDebug, which defines the variable |b:match_debug| and |
| 353 | creates a Matchit menu. This menu makes it convenient to check the values of |
| 354 | the variables described below. You will probably also want to read |
| 355 | |matchit-details| above. |
| 356 | |
| 357 | Defining the variable |b:match_debug| causes the script to set the following |
| 358 | variables, each time you hit the "%" key. Several of these are only defined |
| 359 | if |b:match_words| includes |backref|s. |
| 360 | |
| 361 | *b:match_pat* |
| 362 | The b:match_pat variable is set to |b:match_words| with |backref|s parsed. |
| 363 | *b:match_match* |
| 364 | The b:match_match variable is set to the bit of text that is recognized as a |
| 365 | match. |
| 366 | *b:match_col* |
| 367 | The b:match_col variable is set to the cursor column of the start of the |
| 368 | matching text. |
| 369 | *b:match_wholeBR* |
| 370 | The b:match_wholeBR variable is set to the comma-separated group of patterns |
| 371 | that matches, with |backref|s unparsed. |
| 372 | *b:match_iniBR* |
| 373 | The b:match_iniBR variable is set to the first pattern in |b:match_wholeBR|. |
| 374 | *b:match_ini* |
| 375 | The b:match_ini variable is set to the first pattern in |b:match_wholeBR|, |
| 376 | with |backref|s resolved from |b:match_match|. |
| 377 | *b:match_tail* |
| 378 | The b:match_tail variable is set to the remaining patterns in |
| 379 | |b:match_wholeBR|, with |backref|s resolved from |b:match_match|. |
| 380 | *b:match_word* |
| 381 | The b:match_word variable is set to the pattern from |b:match_wholeBR| that |
| 382 | matches |b:match_match|. |
| 383 | *b:match_table* |
| 384 | The back reference '\'.d refers to the same thing as '\'.b:match_table[d] in |
| 385 | |b:match_word|. |
| 386 | |
| 387 | ============================================================================== |
| 388 | 5. Known Bugs and Limitations *matchit-bugs* |
| 389 | |
| 390 | Just because I know about a bug does not mean that it is on my todo list. I |
| 391 | try to respond to reports of bugs that cause real problems. If it does not |
| 392 | cause serious problems, or if there is a work-around, a bug may sit there for |
| 393 | a while. Moral: if a bug (known or not) bothers you, let me know. |
| 394 | |
| 395 | The various |:vmap|s defined in the script (%, |g%|, |[%|, |]%|, |a%|) may |
| 396 | have undesired effects in Select mode |Select-mode-mapping|. At least, if you |
| 397 | want to replace the selection with any character in "ag%[]" there will be a |
| 398 | pause of |'updatetime'| first. |
| 399 | |
| 400 | It would be nice if "\0" were recognized as the entire pattern. That is, it |
| 401 | would be nice if "foo:\end\0" had the same effect as "\(foo\):\end\1". I may |
| 402 | try to implement this in a future version. (This is not so easy to arrange as |
| 403 | you might think!) |
| 404 | |
| 405 | ============================================================================== |
| 406 | vim:tw=78:fo=tcq2: |