add vim conf files
[arrow.git] / conf_slk120 / vim / _vim / plugin / SyntaxFolds.vim
blob27c622c3a3c2401dd5350aeb2a21c530c158353c
1 " ==============================================================================
2 "        File: syntaxFolds.vim
3 "      Author: Srinath Avadhanula
4 "              ( srinath@fastmail.fm )
5 " Last Change: Sun Oct 27 01:00 AM 2002 PST
6 " Description: Emulation of the syntax folding capability of vim using manual
7 "              folding
9 " This script provides an emulation of the syntax folding of vim using manual
10 " folding. Just as in syntax folding, the folds are defined by regions. Each
11 " region is specified by a call to FoldRegions() which accepts 4 parameters:
13 "    call FoldRegions(startpat, endpat, startoff, endoff)
15 "    startpat: a line matching this pattern defines the beginning of a fold.
16 "    endpat  : a line matching this pattern defines the end of a fold.
17 "    startoff: this is the offset from the starting line at which folding will
18 "              actually start
19 "    endoff  : like startoff, but gives the offset of the actual fold end from
20 "              the line satisfying endpat.
21 "              startoff and endoff are necessary when the folding region does
22 "              not have a specific end pattern corresponding to a start
23 "              pattern. for example in latex,
24 "              \begin{section}
25 "              defines the beginning of a section, but its not necessary to
26 "              have a corresponding
27 "              \end{section}
28 "              the section is assumed to end 1 line _before_ another section
29 "              starts.
30 "    startskip: a pattern which defines the beginning of a "skipped" region.
32 "               For example, suppose we define a \itemize fold as follows:
33 "               startpat =  '^\s*\\item',
34 "               endpat = '^\s*\\item\|^\s*\\end{\(enumerate\|itemize\|description\)}',
35 "               startoff = 0,
36 "               endoff = -1
38 "               This defines a fold which starts with a line beginning with an
39 "               \item and ending one line before a line beginning with an
40 "               \item or \end{enumerate} etc.
42 "               Then, as long as \item's are not nested things are fine.
43 "               However, once items begin to nest, the fold started by one
44 "               \item can end because of an \item in an \itemize
45 "               environment within this \item. i.e, the following can happen:
47 "               \begin{itemize}
48 "               \item Some text <------- fold will start here
49 "                     This item will contain a nested item
50 "                     \begin{itemize} <----- fold will end here because next line contains \item...
51 "                     \item Hello
52 "                     \end{itemize} <----- ... instead of here.
53 "               \item Next item of the parent itemize
54 "               \end{itemize}
56 "               Therefore, in order to completely define a folding item which
57 "               allows nesting, we need to also define a "skip" pattern.
58 "               startskip and end skip do that.
59 "               Leave '' when there is no nesting.
60 "    endskip: the pattern which defines the end of the "skip" pattern for
61 "             nested folds.
63 "    Example: 
64 "    1. A syntax fold region for a latex section is
65 "           startpat = "\\section{"
66 "           endpat   = "\\section{"
67 "           startoff = 0
68 "           endoff   = -1
69 "           startskip = ''
70 "           endskip = ''
71 "    Note that the start and end patterns are thus the same and endoff has a
72 "    negative value to capture the effect of a section ending one line before
73 "    the next starts.
74 "    2. A syntax fold region for the \itemize environment is:
75 "           startpat = '^\s*\\item',
76 "           endpat = '^\s*\\item\|^\s*\\end{\(enumerate\|itemize\|description\)}',
77 "           startoff = 0,
78 "           endoff = -1,
79 "           startskip = '^\s*\\begin{\(enumerate\|itemize\|description\)}',
80 "           endskip = '^\s*\\end{\(enumerate\|itemize\|description\)}'
81 "     Note the use of startskip and endskip to allow nesting.
84 " Each time a call is made to FoldRegions(), all the regions (which might be
85 " disjoint, but not nested) are folded up.
86 " Nested folds can be created by successive calls to FoldRegions(). The first
87 " call defines the region which is deepest in the folding. See MakeTexFolds()
88 " for an idea of how this works for latex files.
90 " Function: AddSyntaxFoldItem (start, end, startoff, endoff [, skipStart, skipEnd]) {{{
91 function! AddSyntaxFoldItem(start, end, startoff, endoff, ...)
92         if a:0 > 0
93                 let skipStart = a:1
94                 let skipEnd = a:2
95         else
96                 let skipStart = ''
97                 let skipEnd = ''
98         end
99         if !exists('b:numFoldItems')
100                 let b:numFoldItems = 0
101         end
102         let b:numFoldItems = b:numFoldItems + 1
104         exe 'let b:startPat_'.b:numFoldItems.' = a:start'
105         exe 'let b:endPat_'.b:numFoldItems.' = a:end'
106         exe 'let b:startOff_'.b:numFoldItems.' = a:startoff'
107         exe 'let b:endOff_'.b:numFoldItems.' = a:endoff'
108         exe 'let b:skipStartPat_'.b:numFoldItems.' = skipStart'
109         exe 'let b:skipEndPat_'.b:numFoldItems.' = skipEnd'
110 endfunction 
113 " }}}
114 " Function: MakeSyntaxFolds (force) {{{
115 " Description: This function calls FoldRegions() several times with the
116 "     parameters specifying various regions resulting in a nested fold
117 "     structure for the file.
118 function! MakeSyntaxFolds(force, ...)
119         if exists('b:doneFolding') && a:force == 0
120                 return
121         end
123         let skipEndPattern = ''
124         if a:0 > 0
125                 let line1 = a:1
126                 let skipEndPattern = '\|'.a:2
127         else
128                 let line1 = 1
129                 let r = line('.')
130                 let c = virtcol('.')
131                 
132                 setlocal fdm=manual
133                 normal! zE
134         end
135         if !exists('b:numFoldItems')
136                 b:numFoldItems = 1000000
137         end
138         
139         let i = 1
141         let maxline = line('.')
143         while exists('b:startPat_'.i) && i <= b:numFoldItems
144                 exe 'let startPat = b:startPat_'.i
145                 exe 'let endPat = b:endPat_'.i
146                 exe 'let startOff = b:startOff_'.i
147                 exe 'let endOff = b:endOff_'.i
148                 
149                 let skipStart = ''
150                 let skipEnd = ''
151                 if exists('b:skipStartPat_'.i)
152                         exe 'let skipStart = b:skipStartPat_'.i
153                         exe 'let skipEnd = b:skipEndPat_'.i
154                 end
155                 exe line1
156                 let lastLoc = line1
158                 if skipStart != ''
159                         call InitStack('BeginSkipArray')
160                         call FoldRegionsWithSkip(startPat, endPat, startOff, endOff, skipStart, skipEnd, 1, line('$'))
161                         " call PrintError('done folding ['.startPat.']')
162                 else
163                         call FoldRegionsWithNoSkip(startPat, endPat, startOff, endOff, 1, line('$'), '')
164                 end
166                 let i = i + 1
167         endwhile
169         exe maxline
170         
171         if a:0 == 0
172                 exe r
173                 exe "normal! ".c."|"
174                 if foldlevel(r) > 1
175                         exe "normal! ".(foldlevel(r) - 1)."zo"
176                 end
177                 let b:doneFolding = 0
178         end
179 endfunction
182 " }}}
183 " FoldRegionsWithSkip: folding things such as \item's which can be nested. {{{
184 function! FoldRegionsWithSkip(startpat, endpat, startoff, endoff, startskip, endskip, line1, line2)
185         exe a:line1
186         " count the regions which have been skipped as we go along. do not want to
187         " create a fold which with a beginning or end line in one of the skipped
188         " regions.
189         let skippedRegions = ''
191         " start searching for either the starting pattern or the end pattern.
192         while search(a:startskip.'\|'.a:endskip, 'W')
193         
194                 if getline('.') =~ a:endskip
196                         let lastBegin = Pop('BeginSkipArray')
197                         " call PrintError('popping '.lastBegin.' from stack and folding till '.line('.'))
198                         call FoldRegionsWithNoSkip(a:startpat, a:endpat, a:startoff, a:endoff, lastBegin, line('.'), skippedRegions)
199                         let skippedRegions = skippedRegions.lastBegin.','.line('.').'|'
202                 " if this is the beginning of a skip region, then, push this line as
203                 " the beginning of a skipped region.
204                 elseif getline('.') =~ a:startskip
206                         " call PrintError('pushing '.line('.').' ['.getline('.').'] into stack')
207                         call Push('BeginSkipArray', line('.'))
209                 end
210         endwhile
212         " call PrintError('with skip starting at '.a:line1.' returning at line# '.line('.'))
213 endfunction
215 " }}}
216 " FoldRegionsWithNoSkip: folding things such as \sections which do not nest. {{{
217 function! FoldRegionsWithNoSkip(startpat, endpat, startoff, endoff, line1, line2, skippedRegions)
218         exe a:line1
220         " call PrintError('line1 = '.a:line1.', searching from '.line('.').'... for ['.a:startpat.'')
221         let lineBegin = s:MySearch(a:startpat, 'in')
222         " call PrintError('... and finding it at '.lineBegin)
224         while lineBegin <= a:line2
225                 if IsInSkippedRegion(lineBegin, a:skippedRegions)
226                         let lineBegin = s:MySearch(a:startpat, 'out')
227                         " call PrintError(lineBegin.' is being skipped')
228                         continue
229                 end
230                 let lineEnd = s:MySearch(a:endpat, 'out')
231                 while IsInSkippedRegion(lineEnd, a:skippedRegions) && lineEnd <= a:line2
232                         let lineEnd = s:MySearch(a:endpat, 'out')
233                 endwhile
234                 if lineEnd > a:line2
235                         exe (lineBegin + a:startoff).','.a:line2.' fold'
236                         break
237                 else
238                         " call PrintError ('for ['.a:startpat.'] '.(lineBegin + a:startoff).','.(lineEnd + a:endoff).' fold')
239                         exe (lineBegin + a:startoff).','.(lineEnd + a:endoff).' fold'
240                 end
242                 " call PrintError('line1 = '.a:line1.', searching from '.line('.').'... for ['.a:startpat.'')
243                 let lineBegin = s:MySearch(a:startpat, 'in')
244                 " call PrintError('... and finding it at '.lineBegin)
245         endwhile
247         exe a:line2
248         return
249 endfunction
251 " }}}
252 " InitStack: initialize a stack {{{
253 function! InitStack(name)
254         exe 'let s:'.a:name.'_numElems = 0'
255 endfunction
256 " }}}
257 " Push: push element into stack {{{
258 function! Push(name, elem)
259         exe 'let numElems = s:'.a:name.'_numElems'
260         let numElems = numElems + 1
261         exe 'let s:'.a:name.'_Element_'.numElems.' = a:elem'
262         exe 'let s:'.a:name.'_numElems = numElems'
263 endfunction
264 " }}}
265 " Pop: pops element off stack {{{
266 function! Pop(name)
267         exe 'let numElems = s:'.a:name.'_numElems'
268         if numElems == 0
269                 return ''
270         else
271                 exe 'let ret = s:'.a:name.'_Element_'.numElems
272                 let numElems = numElems - 1
273                 exe 'let s:'.a:name.'_numElems = numElems'
274                 return ret
275         end
276 endfunction
277 " }}}
278 " MySearch: just like search(), but returns large number on failure {{{
279 function! <SID>MySearch(pat, opt)
280         if a:opt == 'in'
281                 if getline('.') =~ a:pat
282                         let ret = line('.')
283                 else
284                         let ret = search(a:pat, 'W')
285                 end
286         else
287                 normal! $
288                 let ret = search(a:pat, 'W')
289         end
291         if ret == 0
292                 let ret = line('$') + 1
293         end
294         return ret
295 endfunction
296 " }}}
297 " Function: IsInSkippedRegion (lnum, regions) {{{
298 " Description: finds whether a given line number is within one of the regions
299 "              skipped.
300 function! IsInSkippedRegion(lnum, regions)
301         let i = 1
302         let subset = s:Strntok(a:regions, '|', i)
303         while subset != ''
304                 let n1 = s:Strntok(subset, ',', 1)
305                 let n2 = s:Strntok(subset, ',', 2)
306                 if a:lnum >= n1 && a:lnum <= n2
307                         return 1
308                 end
310                 let subset = s:Strntok(a:regions, '|', i)
311                 let i = i + 1
312         endwhile
314         return 0
315 endfunction " }}}
316 " Function: Strntok (string, tok, n) {{{
317 " extract the n^th token from s seperated by tok.
318 " example: Strntok('1,23,3', ',', 2) = 23
319 fun! <SID>Strntok(s, tok, n)
320         return matchstr( a:s.a:tok[0], '\v(\zs([^'.a:tok.']*)\ze['.a:tok.']){'.a:n.'}')
321 endfun " }}}
323 " vim600:fdm=marker