Vim Tips Wiki
Advertisement

Previous TipNext Tip

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:

  1. VimFoldIfContainer matches the entire if...endif region
  2. 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.
  3. VimFoldIfEnd is also only contained in a VimFoldIfContainer, and will match else...endif.
  4. 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


Comments

Todo: fix problems mentioned.

Advertisement