(adding support for multiple "catch" and "elseif" statements, adding section headers) |
(→How it works: VimFoldIfContainer bounds the other regions because it uses keepend) |
||
Line 27: | Line 27: | ||
#VimFoldElseIf is also only contained in the container, and will match any number of elseif...else(if) groups. This also backs up the endmarker match to allow another match on it to start the next region. |
#VimFoldElseIf is also only contained in the container, and will match any number of elseif...else(if) groups. This also backs up the endmarker match to allow another match on it to start the next region. |
||
#VimFoldElse is also only contained in the container, and will match the final possible group: else...endif. |
#VimFoldElse is also only contained in the container, and will match the final possible group: else...endif. |
||
− | #Note that the contained groups do not have the "extend" argument, meaning that even if the "else" does not match to end the group, the group will not extend beyond the confines of the VimFoldIfContainer (which ends at "endif"). |
+ | #Note that the contained groups do not have the "extend" argument, meaning that even if the "else" does not match to end the group, the group will not extend beyond the confines of the VimFoldIfContainer (which ends at "endif") because VimFoldIfContainer uses keepend. |
"try...catch" constructs work similarly to "if...else...endif" constructs (with try...catch...finally...endtry instead of if...elseif...else...endif). |
"try...catch" constructs work similarly to "if...else...endif" constructs (with try...catch...finally...endtry instead of if...elseif...else...endif). |
Revision as of 21:45, 11 December 2007
Tip: #1376 - Syntax folding of Vim scripts
Created: November 3, 2006 5:42 Complexity: intermediate Author: Ingo Karkat Version: 6.0 Karma: 18/6 Imported from: Tip#1376
Explanation
Many syntax files provide fold information. Unfortunately, the vimscript syntax file does not. You will need to define your own syntax folding, or resign yourself to inserting fold markers all over the place (which, incidentally, the vim.vim syntax file does). Here is a good set of syntax folding definitions that you can at least use as a starting point.
Usage
The code at the end of this tip allows folding of various Vim script constructs via foldmethod=syntax. Put it into 'after/syntax/vim.vim', located either in your system-wide or home Vim directory (see :help after-directory).
Use setlocal foldmethod=syntax to use these folds, in an autocommand or a mapping. If using an autocommand, the FileType and Syntax events are probably the best ones to use. Automatically calling zR when you do this may be a good idea as well.
How it works
These syntax groups set up regions between start and end patterns as long as they don't start within a comment or string, and attempt to skip over commented-out end patterns with the skip pattern.
Most of the syntax rules have "begin" and "end" keywords that set up a simple syntax region, using keepend to allow syntax highlighting of end markers and extend to allow nesting.
The "if...else...endif" construct is a little different and works as follows:
- VimFoldIfContainer is a simple transparent region with no folding that matches the entire if...endif region
- VimFoldIf is only contained in VimFoldIfContainer, and matches if...else or if...elseif, then backs up the endmarker match to allow another match on the else(if). This folds the first part of if...else...endif constructs.
- VimFoldElseIf is also only contained in the container, and will match any number of elseif...else(if) groups. This also backs up the endmarker match to allow another match on it to start the next region.
- VimFoldElse is also only contained in the container, and will match the final possible group: else...endif.
- Note that the contained groups do not have the "extend" argument, meaning that even if the "else" does not match to end the group, the group will not extend beyond the confines of the VimFoldIfContainer (which ends at "endif") because VimFoldIfContainer uses keepend.
"try...catch" constructs work similarly to "if...else...endif" constructs (with try...catch...finally...endtry instead of if...elseif...else...endif).
Problems
These syntax folds are not quite perfect, and suffer from at least the following:
- The syntax definitions are fairly complex. Sometimes, especially when deleting or commenting out an "else", "catch", or "finally", you will need to type :syn sync fromstart to update the fold information. You may want to map this command to a key if you need to do it often.
- Only limited support for comments and strings. The "skip" group could probably be improved; currently, it will skip anything between two " characters or anything after a single " character.
- Some keywords (such as "else" and "catch") must be preceded by nothing but whitespace to properly trigger fold behavior; this may or may not be an issue depending on your coding style.
- Because so many of these regions end with an "end<blah>", some shorthand endings (like "en" for "endif") will not work. \%[] groups are used when possible to allow as many shorthand keywords as possible, but you should specify at least as many characters to make the keyword unambiguous to a search pattern.
Syntax Definitions
As mentioned above, place the following in your after/syntax vim file:
" The default Vim syntax file lacks 'fold' definitions, so define them. " fold functions syn region VimFold \ start="\<fu\%[nction]!\=\s\+\(<[sS][iI][dD]>\|[Ss]:\|\u\)\i*\ze\s*(" \ end="\<endfu\%[nction]\>" \ skip=/"[^"]\{-\}\("\|$\)/ \ transparent fold \ keepend extend \ containedin=TOP,vimComment,vimLineComment,vimCommentString,vimString " fold while loops syn region VimFold \ start="\<wh\%[ile]\>" \ end="\<endw\%[hile]\>" \ skip=/"[^"]\{-\}\("\|$\)/ \ transparent fold \ keepend extend \ containedin=TOP,vimComment,vimLineComment,vimCommentString,vimString " fold for loops syn region VimFold \ start="\<for\>" \ end="\<endfo\%[r]\>" \ skip=/"[^"]\{-\}\("\|$\)/ \ transparent fold \ keepend extend \ containedin=TOP,vimComment,vimLineComment,vimCommentString,vimString " fold augroups syn region VimFold \ start="\<aug\%[roup]\ze\s\+\(END\>\)\@!" \ end="\<aug\%[roup]\s\+END\>" \ skip=/"[^"]\{-\}\("\|$\)/ \ transparent fold \ keepend extend \ containedin=TOP,vimComment,vimLineComment,vimCommentString,vimString " fold if...else...endif constructs syn region VimFoldIfContainer \ start="\<if\>" \ end="\<endif\>" \ skip=/"[^"]\{-\}\("\|$\)/ \ transparent \ keepend extend \ containedin=TOP, \ vimComment,vimLineComment,vimCommentString,vimString \ contains=NONE syn region VimFoldIf \ start="\<if\>" \ end="^\s*else\%[if]\>"ms=s-1,me=s-1 \ skip=/"[^"]\{-\}\("\|$\)/ \ fold transparent \ keepend \ contained containedin=VimFoldIfContainer \ nextgroup=VimFoldElseIf,VimFoldElse \ contains=TOP syn region VimFoldElseIf \ start="\<else\%[if]\>" \ end="^\s*else\%[if]\>"ms=s-1,me=s-1 \ skip=/"[^"]\{-\}\("\|$\)/ \ fold transparent \ keepend \ contained containedin=VimFoldIfContainer \ nextgroup=VimFoldElseIf,VimFoldElse \ contains=TOP syn region VimFoldElse \ start="\<else\>" \ end="\<endif\>" \ skip=/"[^"]\{-\}\("\|$\)/ \ fold transparent \ keepend \ contained containedin=VimFoldIfContainer \ contains=TOP " fold try...catch...finally...endtry constructs syn region VimFoldTryContainer \ start="\<try\>" \ end="\<endt\%[ry]\>" \ skip=/"[^"]\{-\}\("\|$\)/ \ transparent \ keepend extend \ containedin=TOP, \ vimComment,vimLineComment,vimCommentString,vimString \ contains=NONE syn region VimFoldTry \ start="\<try\>" \ end="^\s*\(fina\%[lly]\|cat\%[ch]\)\>"ms=s-1,me=s-1 \ skip=/"[^"]\{-\}\("\|$\)/ \ fold transparent \ keepend \ contained containedin=VimFoldTryContainer \ nextgroup=VimFoldCatch,VimFoldFinally \ contains=TOP syn region VimFoldCatch \ start="\<cat\%[ch]\>" \ end="^\s*\(cat\%[ch]\|fina\%[lly]\)\>"ms=s-1,me=s-1 \ skip=/"[^"]\{-\}\("\|$\)/ \ fold transparent \ keepend \ contained containedin=VimFoldTryContainer \ nextgroup=VimFoldCatch,VimFoldFinally \ contains=TOP syn region VimFoldFinally \ start="\<fina\%[lly]\>" \ end="\<endt\%[ry]\>" \ skip=/"[^"]\{-\}\("\|$\)/ \ fold transparent \ keepend \ contained containedin=VimFoldTryContainer \ contains=TOP
References
- :help mysyntaxfile-add tells how to add to a syntax file as we are doing
- :help :syn-region
- :help :syn-fold
- :help /\@! used to match only when a string DOESN'T match
- :help /\%[] used to match parts of a string
- :help :syn-pattern-offset used to "back up" on an if-else to allow else-endif to match
Comments
Todo: fix problems mentioned.