"============================================================================= " File: folding.vim " Author: Srinath Avadhanula " modifications/additions by Zhang Linbo, Gerd Wachsmuth " Created: Tue Apr 23 05:00 PM 2002 PST " " Description: functions to interact with Syntaxfolds.vim "============================================================================= nnoremap Tex_RefreshFolds :call MakeTexFolds(1, 1) augroup LatexSuite au LatexSuite User LatexSuiteFileType \ call Tex_Debug('folding.vim: catching LatexSuiteFileType', 'fold') | \ call Tex_SetFoldOptions() augroup END " Tex_SetFoldOptions: sets maps for every buffer {{{ " Description: function! Tex_SetFoldOptions() if exists('b:doneSetFoldOptions') return endif let b:doneSetFoldOptions = 1 setlocal foldtext=TexFoldTextFunction() if g:Tex_Folding call MakeTexFolds(0, 0) endif let s:ml = '' call Tex_MakeMap(s:ml."rf", "Tex_RefreshFolds", 'n', ' ') " Setup a local autocommand, if FileChangedShellPost is available if exists('##FileChangedShellPost') augroup LatexSuite autocmd FileChangedShellPost call MakeTexFolds(1, 0) augroup END endif endfunction " }}} " Tex_FoldSections: creates section folds {{{ " Description: " This function takes a comma seperated list of "sections" and creates fold " definitions for them. The first item is supposed to be the "shallowest" field " and the last is the "deepest". See g:Tex_FoldedSections for the default " definition of the lst input argument. " " **works recursively** function! Tex_FoldSections(lst, endpat) let i = match(a:lst, ',') if i > 0 let s = strpart(a:lst, 0, i) else let s = a:lst endif if s =~ '%%fakesection' let s = '^\s*' . s else let pattern = '' let prefix = '' for label in split( s, "|" ) let pattern .= prefix . '\\' . label . '\|' . '%%fake' . label let prefix = '\|' endfor " The line before the pattern could contain a mixture of "% =_" (within a " comment). " The pattern itself is ended by a non-word character "\W" or a newline. let s = '^\%(%[% =-]*\n\)\?\s*' . '\%(' . pattern . '\)' . '\%(\W\|\n\)' endif let endpat = s . '\|' . a:endpat if i > 0 call Tex_FoldSections(strpart(a:lst,i+1), endpat) endif call AddSyntaxFoldItem(s, endpat, 0, -1) endfunction " }}} " MakeTexFolds: function to create fold items for latex. {{{ " " used in conjunction with MakeSyntaxFolds(). " see ../plugin/syntaxFolds.vim for documentation function! MakeTexFolds(force, manual) if exists('g:Tex_Folding') && !g:Tex_Folding return endif if &ft != 'tex' return end " Setup folded items lists g:Tex_Foldedxxxx " 1. Use default value if g:Tex_Foldedxxxxxx is not defined " 2. prepend default value to g:Tex_Foldedxxxxxx if it starts with ',' " 3. append default value to g:Tex_Foldedxxxxxx if it ends with ',' " Folding items which are not caught in any of the standard commands, " environments or sections. let s = 'item,slide,preamble,<<<' if !exists('g:Tex_FoldedMisc') let g:Tex_FoldedMisc = s elseif g:Tex_FoldedMisc[0] == ',' let g:Tex_FoldedMisc = s . g:Tex_FoldedMisc elseif g:Tex_FoldedMisc =~ ',$' let g:Tex_FoldedMisc = g:Tex_FoldedMisc . s endif " By default do not fold any commands. It looks like trying to fold " commands is a difficult problem since commands can be arbitrarily nested " and the end patterns are not unique unlike the case of environments. " For this to work well, we need a regexp which will match a line only if " a command begins on that line but does not end on that line. This " requires a regexp which will match unbalanced curly braces and that is " apparently not doable with regexps. let s = '' if !exists('g:Tex_FoldedCommands') let g:Tex_FoldedCommands = s elseif g:Tex_FoldedCommands[0] == ',' let g:Tex_FoldedCommands = s . g:Tex_FoldedCommands elseif g:Tex_FoldedCommands =~ ',$' let g:Tex_FoldedCommands = g:Tex_FoldedCommands . s endif let s = 'verbatim,comment,eq,gather,align,figure,table,thebibliography,' \. 'keywords,abstract,titlepage' if !exists('g:Tex_FoldedEnvironments') let g:Tex_FoldedEnvironments = s elseif g:Tex_FoldedEnvironments[0] == ',' let g:Tex_FoldedEnvironments = s . g:Tex_FoldedEnvironments elseif g:Tex_FoldedEnvironments =~ ',$' let g:Tex_FoldedEnvironments = g:Tex_FoldedEnvironments . s endif if !exists('g:Tex_FoldedSections') let g:Tex_FoldedSections = 'part,chapter,section,' \. 'subsection,subsubsection,paragraph' endif " the order in which these calls are made decides the nestedness. in " latex, a table environment will always be embedded in either an item or " a section etc. not the other way around. so we first fold up all the " tables. and then proceed with the other regions. let b:numFoldItems = 0 " ======================================================================== " How to add new folding items {{{ " ======================================================================== " " Each of the following function calls defines a syntax fold region. Each " definition consists of a call to the AddSyntaxFoldItem() function. " " The order in which the folds are defined is important. Juggling the " order of the function calls will create havoc with folding. The " "deepest" folding item needs to be called first. For example, if " the \begin{table} environment is a subset (or lies within) the \section " environment, then add the definition for the \table first. " " The AddSyntaxFoldItem() function takes either 4 or 6 arguments. When it " is called with 4 arguments, it is equivalent to calling it with 6 " arguments with the last two left blank (i.e as empty strings) " " The explanation for each argument is as follows: " startpat: a line matching this pattern defines the beginning of a fold. " endpat : a line matching this pattern defines the end of a fold. " startoff: this is the offset from the starting line at which folding will " actually start " endoff : like startoff, but gives the offset of the actual fold end from " the line satisfying endpat. " startoff and endoff are necessary when the folding region does " not have a specific end pattern corresponding to a start " pattern. for example in latex, " \begin{section} " defines the beginning of a section, but its not necessary to " have a corresponding " \end{section} " the section is assumed to end 1 line _before_ another section " starts. " startskip: a pattern which defines the beginning of a "skipped" region. " " For example, suppose we define a \itemize fold as follows: " startpat = '^\s*\\item', " endpat = '^\s*\\item\|^\s*\\end{\(enumerate\|itemize\|description\)}', " startoff = 0, " endoff = -1 " " This defines a fold which starts with a line beginning with an " \item and ending one line before a line beginning with an " \item or \end{enumerate} etc. " " Then, as long as \item's are not nested things are fine. " However, once items begin to nest, the fold started by one " \item can end because of an \item in an \itemize " environment within this \item. i.e, the following can happen: " " \begin{itemize} " \item Some text <------- fold will start here " This item will contain a nested item " \begin{itemize} <----- fold will end here because next line contains \item... " \item Hello " \end{itemize} <----- ... instead of here. " \item Next item of the parent itemize " \end{itemize} " " Therefore, in order to completely define a folding item which " allows nesting, we need to also define a "skip" pattern. " startskip and end skip do that. " Leave '' when there is no nesting. " endskip: the pattern which defines the end of the "skip" pattern for " nested folds. " " Example: " 1. A syntax fold region for a latex section is " startpat = "\\section{" " endpat = "\\section{" " startoff = 0 " endoff = -1 " startskip = '' " endskip = '' " Note that the start and end patterns are thus the same and endoff has a " negative value to capture the effect of a section ending one line before " the next starts. " 2. A syntax fold region for the \itemize environment is: " startpat = '^\s*\\item', " endpat = '^\s*\\item\|^\s*\\end{\(enumerate\|itemize\|description\)}', " startoff = 0, " endoff = -1, " startskip = '^\s*\\begin{\(enumerate\|itemize\|description\)}', " endskip = '^\s*\\end{\(enumerate\|itemize\|description\)}' " Note the use of startskip and endskip to allow nesting. " " " }}} " ======================================================================== " {{{ comment lines if g:Tex_FoldedMisc =~ '\' call AddSyntaxFoldItem ( \ '^%\([^%]\|[^f]\|[^a]\|[^k]\|[^e]\)', \ '^[^%]', \ 0, \ -1 \ ) endif " }}} " {{{ items if g:Tex_FoldedMisc =~ '\' call AddSyntaxFoldItem ( \ '^\s*\\item', \ '^\s*\\item\|^\s*\\end{\(enumerate\|itemize\|description\)}', \ 0, \ -1, \ '^\s*\\begin{\(enumerate\|itemize\|description\)}', \ '^\s*\\end{\(enumerate\|itemize\|description\)}' \ ) endif " }}} " {{{ title if g:Tex_FoldedMisc =~ '\' call AddSyntaxFoldItem ( \ '^\s*\\title\W', \ '^\s*\\maketitle', \ 0, \ 0 \ ) endif " }}} " Commands and Environments {{{ " Fold the commands and environments in 2 passes. let pass = 0 while pass < 2 if pass == 0 let lst = g:Tex_FoldedCommands else let lst = g:Tex_FoldedEnvironments endif while lst != '' let i = match(lst, ',') if i > 0 let s = strpart(lst, 0, i) let lst = strpart(lst, i+1) else let s = lst let lst = '' endif if s != '' if pass == 0 " NOTE: This pattern ensures that a command which is " terminated on the same line will not start a fold. " However, it will also refuse to fold certain commands " which have not terminated. eg: " \commandname{something \textbf{text} and " will _not_ start a fold. " In other words, the pattern is safe, but not exact. call AddSyntaxFoldItem('^\s*\\'.s.'{[^{}]*$','^[^}]*}',0,0) else if s =~ 'itemize\|enumerate\|description' " These environments can nest. call AddSyntaxFoldItem('^\s*\\begin{'.s,'\(^\|\s\)\s*\\end{'.s,0,0,'^\s*\\begin{'.s,'\(^\|\s\)\s*\\end{'.s) else call AddSyntaxFoldItem('^\s*\\begin{'.s,'\(^\|\s\)\s*\\end{'.s,0,0,'','') endif endif endif endwhile let pass = pass + 1 endwhile " }}} " Sections {{{ if g:Tex_FoldedSections != '' call Tex_FoldSections(g:Tex_FoldedSections, \ '^\s*\\\%(frontmatter\|mainmatter\|backmatter\)\|' \. '^\s*\\begin{thebibliography\|^\s*\\endinput\|' \. '^\s*\\begin{slide\|^\s*\\\%(begin\|end\){document\|' \. '^\s*\\\%(\%(begin\|end\){appendix}\|appendix\)') endif " }}} " {{{ slide if g:Tex_FoldedMisc =~ '\' call AddSyntaxFoldItem ( \ '^\s*\\begin{slide', \ '^\s*\\appendix\W\|^\s*\\chapter\W\|^\s*\\end{slide\|^\s*\\end{document', \ 0, \ 0 \ ) endif " }}} " {{{ preamble if g:Tex_FoldedMisc =~ '\' call AddSyntaxFoldItem ( \ '^\s*\\document\(class\|style\)\>', \ '^\s*\\begin{document}', \ 0, \ -1 \ ) endif " }}} " Manually folded regions {{{ if g:Tex_FoldedMisc =~ '\(^\|,\)<<<\(,\|$\)' call AddSyntaxFoldItem ( \ '<<<', \ '>>>', \ 0, \ 0 \ ) endif " }}} call MakeSyntaxFolds(a:force) " Open all folds if this function was triggered automatically " and g:Tex_AutoFolding is disabled if !a:manual && !g:Tex_AutoFolding normal! zR endif endfunction " }}} " TexFoldTextFunction: create fold text for folds {{{ function! TexFoldTextFunction() " The dashes indicating the foldlevel together with " the number of lines are aligned to width '7'. let lines = v:foldend - v:foldstart + 1 let myfoldtext = repeat('-', v:foldlevel-1) . '+' \. repeat(' ', 7-(v:foldlevel-1)-len(lines)) \. lines . ' lines: ' " Add some indent per foldlevel let myfoldtext .= repeat('> ', v:foldlevel-1) if getline(v:foldstart) =~ '^\s*\\begin{' let header = matchstr(getline(v:foldstart), \ '^\s*\\begin{\zs\([:alpha:]*\)[^}]*\ze}') let title = '' let caption = '' let label = '' let i = v:foldstart while i <= v:foldend if getline(i) =~ '\\caption' " distinguish between " \caption{fulldesc} - fulldesc will be displayed " \caption[shortdesc]{fulldesc} - shortdesc will be displayed if getline(i) =~ '\\caption\[' let caption = matchstr(getline(i), '\\caption\[\zs[^\]]*') let caption = substitute(caption, '\zs\]{.*}[^}]*$', '', '') else let caption = matchstr(getline(i), '\\caption{\zs.*') let caption = substitute(caption, '\zs}[^}]*$', '', '') end elseif getline(i) =~ '\\label' let label = matchstr(getline(i), '\\label{\zs.*') " :FIXME: this does not work when \label contains a " newline or a }-character let label = substitute(label, '\([^}]*\)}.*$', '\1', '') elseif header =~ 'frame' && getline(i) =~ '\\begin{frame}.*{[^{}]*}\|\\frametitle\|%' if getline(i) =~ '\\begin{frame}' " The first argument inside {} is the frame title (the " second one is a subtitle) let title = matchstr(getline(i), '\\begin{frame}.\{-}{\zs[^{}]*\ze}') elseif getline(i) =~ '\\frametitle' let title = matchstr(getline(i), '\\frametitle{\zs[^}]*\ze}') elseif getline(i) =~ '%' && title == '' let title = substitute(getline(i), '^\(\s\|%\)*', '', '') endif end let i = i + 1 endwhile if header =~ 'frame' if title == '' let title = getline(v:foldstart + 1) end " Count frames let frnum = 0 for line in getline(1,v:foldstart) if line =~ '\\begin{frame}' let frnum=frnum+1 endif endfor " Pad with spaces to length 2 let frnum = repeat(' ', 2-len(frnum)) . frnum return myfoldtext . ': Frame ' . frnum . ': ' . title end " if no caption found, then use the second line. if caption == '' let caption = getline(v:foldstart + 1) end return myfoldtext . header. ' ('.label.'): '.caption elseif getline(v:foldstart) =~ '^\s*%\+[% =-]*$' " Useless comment. Use the next line. return myfoldtext . getline(v:foldstart+1) elseif getline(v:foldstart) =~ '^\s*%%fake' " Just strip one '%' from the fakesection. return myfoldtext . substitute(getline(v:foldstart), '^\s*%%fake', '%', '') elseif getline(v:foldstart) =~ '^\s*%' " It's any other comment. Use it. return myfoldtext . getline(v:foldstart) elseif getline(v:foldstart) =~ '^\s*\\document\(class\|style\).*{' " This is the preamble. return myfoldtext . 'Preamble: ' . getline(v:foldstart) end let section_pattern = substitute(g:Tex_FoldedSections, ',\||', '\\|', 'g') let section_pattern = '\\\%(' . section_pattern .'\)\>' if getline(v:foldstart) =~ '^\s*' . section_pattern " This is a section. Search for the content of the mandatory argument {...} let type = matchstr(getline(v:foldstart), '^\s*\zs' . section_pattern) return myfoldtext . type . s:ParseSectionTitle(v:foldstart, section_pattern) else " This is something. return myfoldtext . getline(v:foldstart) end endfunction " }}} " s:ParseSectionTitle: create fold text for sections {{{ " Search for the mandatory argument of the \section command and ignore the " optional argument. function! s:ParseSectionTitle(foldstart, section_pattern) let currlinenr = a:foldstart let currline = s:StripLine(getline(currlinenr)) let currlinelen = strlen(currline) " Look for the section title after the section macro let index = match(currline, '^\s*' . a:section_pattern . '\zs') let maxlines = 10 " Current depth of nested [] and {}: let currdepth = 0 " Do we have found the mandatory argument? " (We are looking for '{' at depth 0) let found_mandatory = 0 let string = '' while (currdepth > 0) || !found_mandatory if index >= currlinelen " Read a new line. let maxlines = maxlines - 1 if maxlines < 0 return string . ' Scanned too many lines' endif let currlinenr = currlinenr + 1 let currline = s:StripLine(getline(currlinenr)) let currlinelen = strlen(currline) let index = 0 if found_mandatory let string .= ' ' endif continue endif " Look for [] and {} at current position if currline[index] =~ '[[{]' if(currdepth == 0) && (currline[index] =~ '{') let found_mandatory = 1 end let currdepth += 1 elseif currline[index] =~ '[]}]' let currdepth -= 1 endif " Look for the next interesting character let next_index = match( currline, '[{}[\]]', index + 1 ) if next_index == -1 let next_index = currlinelen + 1 endif " Update the string if found_mandatory let string .= currline[index:next_index-1] endif let index = next_index endwhile return string endfunction " }}} " s:StripLine: strips whitespace and comments {{{ function! s:StripLine( string ) let string = matchstr( a:string, '^\s*\zs.*$') let comment = match( string, '\\\@ 0 let string = string[0:comment-1] elseif comment == 0 let string = '' endif return string endfunction " }}} " vim:fdm=marker:ff=unix:noet:ts=4:sw=4