]> ruderich.org/simon Gitweb - config/dotfiles.git/blob - vim/indent/tcl.vim
syntax: Remove mkd.vim, php.vim, privoxy.vim, tcl.vim and yaml.vim.
[config/dotfiles.git] / vim / indent / tcl.vim
1 " Vim indent file for Tcl/tk language
2 " Language:     Tcl
3 " Maintained:   SM Smithfield <m_smithfield@yahoo.com>
4 " Last Change:  02/08/2007 (06:35:02)
5 " Filenames:    *.tcl
6 " Version:      0.3.6
7 " ------------------------------------------------------------------
8 " GetLatestVimScripts: 1717 1 :AutoInstall: indent/tcl.vim
9 " ------------------------------------------------------------------
10
11 " if there is another, bail
12 if exists("b:did_indent")
13   finish
14 endif
15
16 let b:did_indent = 1
17
18 setlocal nosmartindent
19
20 " indent expression and keys that trigger it
21 setlocal indentexpr=GetTclIndent()
22 setlocal indentkeys-=:,0#
23
24 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
25
26 " say something once, why say it again
27 if exists("*GetTclIndent")
28   finish
29 endif
30
31 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
32
33 " syntax groups that should not be touched by the indent process
34 let s:syng_com = '\<tcl\%(Comment\|CommentBraces\|Todo\|Start\)\>'
35 " syntax groups that should be ignored by the indent process
36 let s:syng_strcom = '\<tcl\%(Quotes\|Comment\|CommentBraces\|SemiColon\|Special\|Todo\|Start\)\>'
37 " regexp that facilitates finding the correct mate to a brace, skipping comments, strings and such
38 let s:skip_expr = "synIDattr(synID(line('.'),col('.'),0),'name') =~ '".s:syng_strcom."'"
39
40 function s:IsComment(lnum)
41     let pos = 0
42     " try to find a solid char
43     let line = getline(a:lnum)
44     let pos1 = matchend(line, '^\s*\S', 0)
45     if pos1 > 0
46         let pos = pos1
47     endif
48     let q = synIDattr(synID(a:lnum, pos, 0), 'name')
49     return (q =~ s:syng_com)
50 endfunction
51
52 " returns 0/1 whether the cursor pos is in a string/comment syntax run or no.
53 function s:IsInStringOrComment(lnum, col)
54     let q = synIDattr(synID(a:lnum, a:col, 0), 'name') 
55     let retval = (q =~ s:syng_strcom)
56     return retval
57 endfunction
58
59 if version < 700
60     " for the purpose of the following tests, valid means that the character is
61     " not in a string/comment or other *bad* syntax run.
62
63     " returns the pos of the leftmost valid occurance of ch
64     " or -1 for no match
65     function s:leftmostChar(lnum, ch, pos0)
66         let line = getline(a:lnum)
67         let pos1 = match(line, a:ch, a:pos0)
68         if pos1>=0
69             if s:IsInStringOrComment(a:lnum, pos1+1) == 1
70                 let pos2 = pos1
71                 let pos1 = -1
72                 while pos2>=0 && s:IsInStringOrComment(a:lnum, pos2+1)
73                     let pos2 = match(line, a:ch, pos2+1)
74                 endwhile
75                 if pos2>=0 
76                     let pos1 = pos2
77                 endif
78             endif
79         endif
80         return pos1
81     endfunction
82
83     " returns the pos of the rightmost valid occurance of ch
84     " or -1 for no match
85     function s:rightmostChar(lnum, ch, pos0)
86         let line = getline(a:lnum)
87         if a:pos0 == -1
88             let posMax = strlen(line)
89         else
90             let posMax = a:pos0
91         endif
92         let pos1 = match(line, a:ch, 0)
93         let pos2 = -1
94         while pos1>=0 && pos1 < posMax
95             if !s:IsInStringOrComment(a:lnum, pos1+1)
96                 let pos2 = pos1
97             endif
98             let pos1 = match(line, a:ch, pos1+1)
99         endwhile
100         return pos2
101     endfunction
102
103 else
104     " returns the pos of the leftmost valid occurance of ch
105     " or -1 for no match
106     function s:leftmostChar(lnum, ch, pos0)
107         let line = getline(a:lnum)
108         let pos1 = stridx(line, a:ch, a:pos0)
109         if pos1>=0
110             if s:IsInStringOrComment(a:lnum, pos1+1) == 1
111                 let pos2 = pos1
112                 let pos1 = -1
113                 while pos2>=0 && s:IsInStringOrComment(a:lnum, pos2+1)
114                     let pos2 = stridx(line, a:ch, pos2+1)
115                 endwhile
116                 if pos2>=0 
117                     let pos1 = pos2
118                 endif
119             endif
120         endif
121         return pos1
122     endfunction
123
124     " returns the pos of the rightmost valid occurance of ch
125     " or -1 for no match
126     function s:rightmostChar(lnum, ch, pos0)
127         let line = getline(a:lnum)
128         if a:pos0 == -1
129             let pos = strlen(line)
130         else
131             let pos = a:pos0
132         endif
133         let pos1 = strridx(line, a:ch, pos)
134         if pos1>=0
135             if s:IsInStringOrComment(a:lnum, pos1+1) == 1
136                 let pos2 = pos1
137                 let pos1 = -1
138                 while pos2>=0 && s:IsInStringOrComment(a:lnum, pos2+1)
139                     let pos2 = strridx(line, a:ch, pos2-1)
140                 endwhile
141                 if pos2>=0
142                     let pos1 = pos2
143                 endif
144             endif
145         endif
146         return pos1
147     endfunction
148 endif
149
150
151 " returns the position of the brace that opens the current line
152 " or -1 for no match
153 function s:GetOpenBrace(lnum)
154     let openpos = s:rightmostChar(a:lnum, '{', -1)
155     let closepos = s:rightmostChar(a:lnum, '}', -1)
156
157     let sum = 0
158
159     while openpos >= 0
160         if closepos < 0
161             let sum = sum + 1
162             if sum > 0
163                 return openpos
164             endif
165             let openpos = s:rightmostChar(a:lnum, '{', openpos-1)
166         elseif openpos > closepos
167             let sum = sum + 1
168             if sum > 0
169                 return openpos
170             endif
171             let closepos = s:rightmostChar(a:lnum, '}', openpos-1)
172             let openpos = s:rightmostChar(a:lnum, '{', openpos-1)
173         else
174             let sum = sum - 1
175             let openpos = s:rightmostChar(a:lnum, '{', closepos-1)
176             let closepos = s:rightmostChar(a:lnum, '}', closepos-1)
177         endif
178     endwhile
179     return -1
180 endfunction
181
182 " returns the position of the brace that closes the current line
183 " or -1 for no match
184 function s:GetCloseBrace(lnum)
185     let openpos = s:leftmostChar(a:lnum, '{', -1)
186     let closepos = s:leftmostChar(a:lnum, '}', -1)
187
188     let sum = 0
189
190     while closepos >= 0
191         if openpos < 0
192             let sum = sum + 1
193             if sum > 0
194                 return closepos
195             endif
196             let closepos = s:leftmostChar(a:lnum, '}', closepos+1)
197         elseif closepos < openpos
198             let sum = sum + 1
199             if sum > 0
200                 return closepos
201             endif
202             let openpos = s:leftmostChar(a:lnum, '{', closepos+1)
203             let closepos = s:leftmostChar(a:lnum, '}', closepos+1)
204         else
205             let sum = sum - 1
206             let closepos = s:leftmostChar(a:lnum, '}', openpos+1)
207             let openpos = s:leftmostChar(a:lnum, '{', openpos+1)
208         endif
209     endwhile
210     return -1
211 endfunction
212
213 function s:HasLeadingStuff(lnu, pos)
214     let rv = 0
215     let line = getline(a:lnu)
216     let pos1 = matchend(line, '{\s*\S', a:pos)
217     if pos1 >= 0
218         let rv = !s:IsInStringOrComment(a:lnu,pos1)
219     endif
220     return rv
221 endfunction
222
223
224 function s:HasTrailingStuff(lnu, pos)
225     " find the first non-ws-char after matchopen, is NOT string/comment -> has stuff
226     let rv = 0
227     let line = getline(a:lnu)
228     let expr = '\S\s*\%'.(a:pos+1).'c}'
229     let pos1 = match(line, expr)
230     return (pos1 >= 0)
231 endfunction
232
233 function s:LineContinueIndent(lnu)
234     " returns a relative indent offset
235     let delt_ind = 0
236     if a:lnu > 1 
237         " echo "lc-0"
238         let pline = getline(a:lnu-1)
239         let line = getline(a:lnu)
240         if !s:IsComment(a:lnu) 
241             " echo "lc-1" line_is_comment pline_is_comment
242             if pline =~ '\\$'
243                 " echo "lc-2"
244                 if line !~ '\\$'
245                     " echo "lc-3"
246                     let delt_ind = -&sw
247                 endif
248             else
249                 " echo "lc-4"
250                 if line =~ '^\(#\)\@!.*\\$'
251                     " echo "lc-5"
252                     let delt_ind = &sw
253                 endif
254             endif
255         endif
256     endif
257     return delt_ind
258 endfunction
259
260 function s:CloseBracePriorIter(lnu,pos)
261     " what is the ind for a line that has this close brace previous?
262     " this function often depends on the previous open brace
263     let ind = -1
264     call cursor(a:lnu, a:pos+1) 
265     " seek the mate
266     let matchopenlnum = searchpair('{', '', '}', 'bW', s:skip_expr)
267     " does it have a mate
268     if  matchopenlnum >= 0
269         let closeopenpos = s:GetCloseBrace(matchopenlnum)
270         if closeopenpos >= 0
271             " recurse
272             let ind = s:CloseBracePriorIter(matchopenlnum, closeopenpos)
273         else
274             let ind = indent(matchopenlnum)
275             " if matchopenlnum, is lc-bumped, then remove that bump
276             let pline = getline(matchopenlnum-1)
277             if pline =~ '\\$'
278                 let ind = ind - &sw
279                 let ind = ind - (ind % &sw)
280             endif
281         endif
282     endif
283     return ind
284 endfunction
285
286 function s:CloseBraceInd(lnu, pos)
287     let ind = -1
288     call cursor(a:lnu, a:pos+1) 
289     " seek the mate
290     let matchopenlnum = searchpair('{', '', '}', 'bW', s:skip_expr)
291     " does it have a mate
292     if  matchopenlnum >= 0
293         let matchopen = s:GetOpenBrace(matchopenlnum)
294         let matchopenline = getline(matchopenlnum)
295         let closeopenpos = s:GetCloseBrace(matchopenlnum)
296
297         if closeopenpos >= 0
298             " recurse
299             let ind = s:CloseBracePriorIter(matchopenlnum, closeopenpos)
300         else
301             let hasLeadingStuff = s:HasLeadingStuff(matchopenlnum, matchopen)
302             let hasTrailingStuff = s:HasTrailingStuff(a:lnu, a:pos)
303
304             if hasLeadingStuff && hasTrailingStuff
305                 " seek to the first nonwhite and make a hanging indent
306                 let ind = matchend(matchopenline, '{\s*', matchopen)
307             elseif hasTrailingStuff
308                 " indent of openbrace PLUS shiftwidth
309                 let ind = indent(matchopenlnum) + &sw
310             elseif hasLeadingStuff
311                 " let ind = matchend(matchopenline, '{\s*', matchopen)
312                 let ind = indent(matchopenlnum)
313             else
314                 " indent of openbrace
315                 let ind = indent(matchopenlnum)
316             endif
317
318             let pline = getline(matchopenlnum-1)
319             if pline =~ '\\$'
320                 let ind = ind - &sw
321                 let ind = ind - (ind % &sw)
322             endif
323         endif
324
325     endif
326     return ind
327 endfunction
328
329 function s:CloseBracePriorInd(lnu,pos)
330     " what is the ind for a line that has this close brace previous?
331     " this function often depends on the previous open brace
332     let ind = -1
333     call cursor(a:lnu, a:pos+1) 
334     " seek the mate
335     let matchopenlnum = searchpair('{', '', '}', 'bW', s:skip_expr)
336     " does it have a mate
337     if  matchopenlnum >= 0
338         let closeopenpos = s:GetCloseBrace(matchopenlnum)
339         if closeopenpos >= 0
340             " recurse
341             let ind = s:CloseBracePriorIter(matchopenlnum, closeopenpos)
342         else
343             let ind = indent(matchopenlnum)
344             " if matchopenlnum, is lc-bumped, then remove that bump
345             let pline = getline(matchopenlnum-1)
346             if pline =~ '\\$'
347                 let ind = ind - &sw
348                 let ind = ind - (ind % &sw)
349             endif
350         endif
351     endif
352     return ind
353 endfunction
354
355 function s:PrevLine(lnu)
356     " for my purposes, the previous line is
357     " the previous non-blank line that is NOT a comment (nor string?)
358     " 0 ==> top of file
359     let plnu = prevnonblank(a:lnu-1)
360     while plnu > 0 && s:IsComment(plnu)
361         let plnu = prevnonblank(plnu-1)
362     endwhile
363     return plnu
364 endfunction
365
366 function s:GetTclIndent(lnum0)
367
368     " cursor-restore-position 
369     let vcol = col('.')
370     let vlnu = a:lnum0
371
372     " ------------
373     " current line
374     " ------------
375
376     let line = getline(vlnu)
377     let ind1 = -1
378     let flag = 0
379
380
381     " a line may have an 'open' open brace and an 'open' close brace
382     let openbrace = s:GetOpenBrace(vlnu)
383     let closebrace = s:GetCloseBrace(vlnu)
384
385     " does the line have an 'open' closebrace?
386     if closebrace >= 0
387         " echo "cur-0"
388         let ind = s:CloseBraceInd(vlnu, closebrace)
389         let flag = 1
390     endif
391
392     if flag == 1
393         call cursor(vlnu, vcol)
394         return ind
395     endif
396
397     " ---------
398     " prev line
399     " ---------
400
401     let flag = 0
402     " let prevlnum = prevnonblank(vlnu - 1)
403     let prevlnum = s:PrevLine(vlnu)
404     let line = getline(prevlnum)
405     let ind2 = indent(prevlnum)
406
407     " at the start? => indent = 0
408     if prevlnum == 0
409         return 0
410     endif
411
412     " is the current line a comment? => simple use the prevline
413     if s:IsComment(vlnu)
414         let prevlnum = prevnonblank(vlnu-1)
415         let ind2 = indent(prevlnum)
416         return ind2
417     endif
418
419
420     if line =~ '}'
421         " echo "prev-cb-0"
422         " upto this point, the indent is simply inherited from prevlnum
423         let closebrace = s:GetCloseBrace(prevlnum)
424         if closebrace >= 0
425             " echo "prev-cb-1"
426             let ind2 = s:CloseBracePriorInd(prevlnum, closebrace)
427             let flag = 1
428         endif
429     endif
430
431
432     " if there is an open brace
433     if line =~ '{'
434         " echo "prev-ob-0"
435         let openbrace = s:GetOpenBrace(prevlnum)
436         if openbrace >= 0 
437             " echo "prev-ob-1"
438             " does the line end in a comment? or nothing?
439             if s:HasLeadingStuff(prevlnum, openbrace)
440                 " echo "prev-ob-2"
441                 " LineContinueIndent
442                 let delt_ind = s:LineContinueIndent(prevlnum)
443                 let ind2 = matchend(line, '{\s*', openbrace) + delt_ind
444             else
445                 " echo "prev-ob-4"
446                 let ind2 = ind2 + &sw
447             endif
448             let flag = 1
449         endif
450     endif
451
452     if flag == 0
453         " echo "prev-lc-0"
454         " if nothing else has changed the indentation, check for a
455         let delt_ind = s:LineContinueIndent(prevlnum)
456         let ind2 = ind2 + delt_ind
457         " echo "prev-lc- " ind2 delt_ind
458     endif
459
460     " restore the cursor to its original position
461     call cursor(vlnu, vcol)
462     return ind2
463 endfunction
464
465 function GetTclIndent()
466     let l:val = s:GetTclIndent(v:lnum)
467     return l:val
468 endfunction
469
470 function Gpeek()
471     let lnu = line('.')
472     let val = s:GetTclIndent(lnu)
473     let openbrace = s:GetOpenBrace(lnu)
474     let closebrace = s:GetCloseBrace(lnu)
475     let iscomment = s:IsComment(lnu)
476     let prevlnum = s:PrevLine(lnu)
477     echo "ind>" val ": (" openbrace closebrace ") : " prevlnum
478 endfunction