m (→New built-in folding: code for folding augroups and functions has been uncommented by default) |
(BF: autoload functions can start with lowercase letter, added support for curly-braces-names, global/buffer/window objects in function definitions.) |
||
Line 175: | Line 175: | ||
" fold functions |
" fold functions |
||
syn region vimFold |
syn region vimFold |
||
− | \ start="\<fu\%[nction]!\=\s\+\(<[sS][iI][dD]>\|[ |
+ | \ start="\<fu\%[nction]!\=\s\+\%(<[sS][iI][dD]>\|[sSgGbBwW]:\)\?\%(\i\|[#{}.]\)*\ze\s*(" |
\ end="\<endfu\%[nction]\>" |
\ end="\<endfu\%[nction]\>" |
||
\ skip=/"[^"]\{-\}\("\|$\)/ |
\ skip=/"[^"]\{-\}\("\|$\)/ |
Revision as of 11:22, 31 July 2008
created November 3, 2006 · complexity advanced · author Ingo Karkat, fritzophrenic · version 6.0
Explanation
Many syntax files provide fold information. Unfortunately, the officially distributed vimscript syntax file did not until version 7.1-76, and even now only provides limited support. 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).
To use these folds, put setlocal foldmethod=syntax in a ftplugin file, 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 certain syntax groups such as comments, strings, or the lhs of a mapping, and attempt to skip over commented-out end patterns with the skip pattern.
A syntax cluster (:help :syn-cluster) called vimNoFold is defined to easily exclude certain syntax items from containing a fold. The containedin option is set to @vimNoFold to make sure the fold definitions do not match in areas such as comments, syntax definitions, embedded scripts, and the like. The syntax items contained in @vimNoFold were determined through (a) finding an error and (b) looking at vim.vim to determine which ones are triggering the syntax folding that shouldn't be. It is useful to display the highlight group under the cursor while debugging in this way.
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.
- 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 many regions end with "endxxx", some shorthand endings (like "en" for "endif") will not work. Where feasible, \%[] groups are used to allow as many shorthand keywords as possible, but in order to use this folding you will need to write code with at least the number of characters that make the keyword unambiguous to a search pattern.
- If you use reserved words like "while" or "if" for purposes other than Vim language constructs, these fold definitions may incorrectly match them to start a fold. The definitions have an extensive vimNoFold group in an attempt to prevent this, but it can still be fooled. Try to avoid such keywords if you can, especially for variable names. Note: because of the "extend" part of the fold definitions, an incorrect match may cause incorrect syntax highlighting as well as incorrect folding.
New built-in folding
Version 7.1-76 of the default vim.vim syntax file (released January 24, 2008) includes folding for the following as a configurable option:
- augroups
- functions
- embedded scripts in scheme, perl, python, ruby, and tcl
See :help g:vimsyn_folding for details. Since this is a new feature the link may not work...try it within Vim after downloading the latest version.
Get the latest distribution of Vim from whichever source you prefer and put the following in either your vimrc or in vim.vim in your ftplugin directory:
let g:vimsyn_folding='af'
You can then delete the code for folding augroups and functions from the syntax definitions.
Syntax definitions
As mentioned above, place the following in your after/syntax Vim file:
" The default Vim syntax file has limited 'fold' definitions, so define more. " VIM's command window ('q:') and the :options window also set filetype=vim; we " do not want folding in these though. if bufname('') =~# '^\%(command-line\|option-window\)$' finish endif " define groups that cannot contain the start of a fold syn cluster vimNoFold contains=vimComment,vimLineComment,vimCommentString,vimString,vimSynKeyRegion,vimSynRegPat,vimMapLhs,vimOperParen,@EmbeddedScript syn cluster vimEmbeddedScript contains=vimMzSchemeRegion,vimTclRegion,vimPythonRegion,vimRubyRegion,vimPerlRegion " fold while loops syn region vimFoldWhile \ start="\<wh\%[ile]\>" \ end="\<endw\%[hile]\>" \ skip=/"[^"]\{-\}\("\|$\)/ \ transparent fold \ keepend extend \ containedin=ALLBUT,@vimNoFold " fold for loops syn region vimFoldFor \ start="\v<for>%(\s*\n\s*\\)?\s*\k+%(\s*\n\s*\\\s*)?\s*<in>" \ end="\<endfo\%[r]\>" \ skip=/"[^"]\{-\}\("\|$\)/ \ transparent fold \ keepend extend \ containedin=ALLBUT,@vimNoFold " fold if...else...endif constructs syn region vimFoldIfContainer \ start="\<if\>" \ end="\<endif\>" \ skip=/"[^"]\{-\}\("\|$\)/ \ transparent \ keepend extend \ containedin=ALLBUT,@vimNoFold \ contains=NONE syn region vimFoldIf \ start="\<if\>" \ end="^\s*\\\?\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*\\\?\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=ALLBUT,@vimNoFold \ contains=NONE syn region vimFoldTry \ start="\<try\>" \ end="^\s*\\\?\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*\\\?\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 " The following are now supported by default with vim.vim version 7.1-76 " if g:vimsyn_folding contains 'a' and 'f', so set this variable if you want it. " " Since we cannot check for that particular version of the runtime file (runtime " files aren't patched), we assume VIM 7.1 hasn't this yet, and define it here. if v:version <= 701 if exists('g:vimsyn_folding') && g:vimsyn_folding =~# 'f' " fold functions syn region vimFold \ start="\<fu\%[nction]!\=\s\+\%(<[sS][iI][dD]>\|[sSgGbBwW]:\)\?\%(\i\|[#{}.]\)*\ze\s*(" \ end="\<endfu\%[nction]\>" \ skip=/"[^"]\{-\}\("\|$\)/ \ transparent fold \ keepend extend \ containedin=ALLBUT,@vimNoFold endif " fold augroups if exists('g:vimsyn_folding') && g:vimsyn_folding =~# 'a' syn region vimFold \ start="\<aug\%[roup]\ze\s\+\(END\>\)\@!" \ end="\<aug\%[roup]\s\+END\>" \ skip=/"[^"]\{-\}\("\|$\)/ \ transparent fold \ keepend extend \ containedin=ALLBUT,@vimNoFold endif endif
References
- :help mysyntaxfile-add tells how to add to a syntax file as done in this tip
- :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
TO DO
- Fix problems mentioned in tip.
- Try to rework so that fold groups contain top-level language constructs instead of being contained within them (makes more sense conceptually, and probably more robust/less intrusive, but will need to be very dependent on the names defined in the vim.vim syntax file).