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
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.
The following code allows folding of functions, for loops, while loops, if...(else)...endif constructs, and augroups via foldmethod=syntax. Put the following text 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.
" The default Vim syntax file lacks the '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,vimString \ contains=NONE syn region VimFoldIfBeg \ start="\<if\>" \ end="\s*else\%[if]\>"ms=s-1,me=s-1 \ skip=/"[^"]\{-\}\("\|$\)/ \ fold transparent \ keepend \ contained containedin=VimFoldIfContainer \ nextgroup=VimFoldIfEnd \ contains=TOP syn region VimFoldIfEnd \ start="\s*else\%[if]\>" \ end="\<endif\>" \ skip=/"[^"]\{-\}\("\|$\)/ \ fold transparent \ keepend \ contained containedin=VimFoldIfContainer \ contains=TOP " fold try...catch...finally...endtry constructs syn region VimFoldTryContainer \ start="\<try\>" \ end="\<endtry\>" \ skip=/"[^"]\{-\}\("\|$\)/ \ transparent \ keepend extend \ containedin=TOP, \ vimComment,vimLineComment,vimCommentString,vimString,vimString \ contains=NONE syn region VimFoldTry \ start="\<try\>" \ end="\s*catch\>"ms=s-1,me=s-1 \ skip=/"[^"]\{-\}\("\|$\)/ \ fold transparent \ keepend \ contained containedin=VimFoldTryContainer \ nextgroup=VimFoldCatch,VimFoldFinally \ contains=TOP syn region VimFoldCatch \ start="\<catch\>" \ end="\s*\(finally\|catch\)\>"ms=s-1,me=s-1 \ skip=/"[^"]\{-\}\("\|$\)/ \ fold transparent \ keepend \ contained containedin=VimFoldTryContainer \ nextgroup=VimFoldFinally \ contains=TOP syn region VimFoldFinally \ start="\<finally\>" \ end="\s*endtry\>" \ skip=/"[^"]\{-\}\("\|$\)/ \ fold transparent \ keepend \ contained containedin=VimFoldTryContainer \ contains=TOP
These syntax groups set up regions between start and end patterns as long as they don't start within a comment, and attempt to skip over commented-out end patterns with the skip pattern.
The "if...else...endif" construct works as follows:
- VimFoldIfContainer matches the entire if...endif region
- VimFoldIfBeg is only contained in VimFoldIfContainer, and matches if...else, then backs up the end of the region to allow another match on the else.
- VimFoldIfEnd is also only contained in a VimFoldIfContainer, and will match else...endif.
- Note that VimFoldIfBeg does not have the "extend" argument, meaning that even if the "else" does not match to end VimFoldBeg, it will not extend beyond the confines of the VimFoldIfContainer (which ends at "endif").
These syntax folds are not quite perfect, and suffer from at least the following:
- Sometimes, especially when commenting out an "else", you will need to type :syn sync fromstart to update folds; you may want to map this command to a key.
- 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.
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.