1 " Vim indent file for Tcl/tk language
3 " Maintained: SM Smithfield <m_smithfield@yahoo.com>
4 " Last Change: 02/08/2007 (06:35:02)
7 " ------------------------------------------------------------------
8 " GetLatestVimScripts: 1717 1 :AutoInstall: indent/tcl.vim
9 " ------------------------------------------------------------------
11 " if there is another, bail
12 if exists("b:did_indent")
18 setlocal nosmartindent
20 " indent expression and keys that trigger it
21 setlocal indentexpr=GetTclIndent()
22 setlocal indentkeys-=:,0#
24 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
26 " say something once, why say it again
27 if exists("*GetTclIndent")
31 """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
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."'"
40 function s:IsComment(lnum)
42 " try to find a solid char
43 let line = getline(a:lnum)
44 let pos1 = matchend(line, '^\s*\S', 0)
48 let q = synIDattr(synID(a:lnum, pos, 0), 'name')
49 return (q =~ s:syng_com)
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)
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.
63 " returns the pos of the leftmost valid occurance of ch
65 function s:leftmostChar(lnum, ch, pos0)
66 let line = getline(a:lnum)
67 let pos1 = match(line, a:ch, a:pos0)
69 if s:IsInStringOrComment(a:lnum, pos1+1) == 1
72 while pos2>=0 && s:IsInStringOrComment(a:lnum, pos2+1)
73 let pos2 = match(line, a:ch, pos2+1)
83 " returns the pos of the rightmost valid occurance of ch
85 function s:rightmostChar(lnum, ch, pos0)
86 let line = getline(a:lnum)
88 let posMax = strlen(line)
92 let pos1 = match(line, a:ch, 0)
94 while pos1>=0 && pos1 < posMax
95 if !s:IsInStringOrComment(a:lnum, pos1+1)
98 let pos1 = match(line, a:ch, pos1+1)
104 " returns the pos of the leftmost valid occurance of ch
106 function s:leftmostChar(lnum, ch, pos0)
107 let line = getline(a:lnum)
108 let pos1 = stridx(line, a:ch, a:pos0)
110 if s:IsInStringOrComment(a:lnum, pos1+1) == 1
113 while pos2>=0 && s:IsInStringOrComment(a:lnum, pos2+1)
114 let pos2 = stridx(line, a:ch, pos2+1)
124 " returns the pos of the rightmost valid occurance of ch
126 function s:rightmostChar(lnum, ch, pos0)
127 let line = getline(a:lnum)
129 let pos = strlen(line)
133 let pos1 = strridx(line, a:ch, pos)
135 if s:IsInStringOrComment(a:lnum, pos1+1) == 1
138 while pos2>=0 && s:IsInStringOrComment(a:lnum, pos2+1)
139 let pos2 = strridx(line, a:ch, pos2-1)
151 " returns the position of the brace that opens the current line
153 function s:GetOpenBrace(lnum)
154 let openpos = s:rightmostChar(a:lnum, '{', -1)
155 let closepos = s:rightmostChar(a:lnum, '}', -1)
165 let openpos = s:rightmostChar(a:lnum, '{', openpos-1)
166 elseif openpos > closepos
171 let closepos = s:rightmostChar(a:lnum, '}', openpos-1)
172 let openpos = s:rightmostChar(a:lnum, '{', openpos-1)
175 let openpos = s:rightmostChar(a:lnum, '{', closepos-1)
176 let closepos = s:rightmostChar(a:lnum, '}', closepos-1)
182 " returns the position of the brace that closes the current line
184 function s:GetCloseBrace(lnum)
185 let openpos = s:leftmostChar(a:lnum, '{', -1)
186 let closepos = s:leftmostChar(a:lnum, '}', -1)
196 let closepos = s:leftmostChar(a:lnum, '}', closepos+1)
197 elseif closepos < openpos
202 let openpos = s:leftmostChar(a:lnum, '{', closepos+1)
203 let closepos = s:leftmostChar(a:lnum, '}', closepos+1)
206 let closepos = s:leftmostChar(a:lnum, '}', openpos+1)
207 let openpos = s:leftmostChar(a:lnum, '{', openpos+1)
213 function s:HasLeadingStuff(lnu, pos)
215 let line = getline(a:lnu)
216 let pos1 = matchend(line, '{\s*\S', a:pos)
218 let rv = !s:IsInStringOrComment(a:lnu,pos1)
224 function s:HasTrailingStuff(lnu, pos)
225 " find the first non-ws-char after matchopen, is NOT string/comment -> has stuff
227 let line = getline(a:lnu)
228 let expr = '\S\s*\%'.(a:pos+1).'c}'
229 let pos1 = match(line, expr)
233 function s:LineContinueIndent(lnu)
234 " returns a relative indent offset
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
250 if line =~ '^\(#\)\@!.*\\$'
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
264 call cursor(a:lnu, a:pos+1)
266 let matchopenlnum = searchpair('{', '', '}', 'bW', s:skip_expr)
267 " does it have a mate
268 if matchopenlnum >= 0
269 let closeopenpos = s:GetCloseBrace(matchopenlnum)
272 let ind = s:CloseBracePriorIter(matchopenlnum, closeopenpos)
274 let ind = indent(matchopenlnum)
275 " if matchopenlnum, is lc-bumped, then remove that bump
276 let pline = getline(matchopenlnum-1)
279 let ind = ind - (ind % &sw)
286 function s:CloseBraceInd(lnu, pos)
288 call cursor(a:lnu, a:pos+1)
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)
299 let ind = s:CloseBracePriorIter(matchopenlnum, closeopenpos)
301 let hasLeadingStuff = s:HasLeadingStuff(matchopenlnum, matchopen)
302 let hasTrailingStuff = s:HasTrailingStuff(a:lnu, a:pos)
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)
314 " indent of openbrace
315 let ind = indent(matchopenlnum)
318 let pline = getline(matchopenlnum-1)
321 let ind = ind - (ind % &sw)
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
333 call cursor(a:lnu, a:pos+1)
335 let matchopenlnum = searchpair('{', '', '}', 'bW', s:skip_expr)
336 " does it have a mate
337 if matchopenlnum >= 0
338 let closeopenpos = s:GetCloseBrace(matchopenlnum)
341 let ind = s:CloseBracePriorIter(matchopenlnum, closeopenpos)
343 let ind = indent(matchopenlnum)
344 " if matchopenlnum, is lc-bumped, then remove that bump
345 let pline = getline(matchopenlnum-1)
348 let ind = ind - (ind % &sw)
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?)
359 let plnu = prevnonblank(a:lnu-1)
360 while plnu > 0 && s:IsComment(plnu)
361 let plnu = prevnonblank(plnu-1)
366 function s:GetTclIndent(lnum0)
368 " cursor-restore-position
376 let line = getline(vlnu)
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)
385 " does the line have an 'open' closebrace?
388 let ind = s:CloseBraceInd(vlnu, closebrace)
393 call cursor(vlnu, vcol)
402 " let prevlnum = prevnonblank(vlnu - 1)
403 let prevlnum = s:PrevLine(vlnu)
404 let line = getline(prevlnum)
405 let ind2 = indent(prevlnum)
407 " at the start? => indent = 0
412 " is the current line a comment? => simple use the prevline
414 let prevlnum = prevnonblank(vlnu-1)
415 let ind2 = indent(prevlnum)
422 " upto this point, the indent is simply inherited from prevlnum
423 let closebrace = s:GetCloseBrace(prevlnum)
426 let ind2 = s:CloseBracePriorInd(prevlnum, closebrace)
432 " if there is an open brace
435 let openbrace = s:GetOpenBrace(prevlnum)
438 " does the line end in a comment? or nothing?
439 if s:HasLeadingStuff(prevlnum, openbrace)
442 let delt_ind = s:LineContinueIndent(prevlnum)
443 let ind2 = matchend(line, '{\s*', openbrace) + delt_ind
446 let ind2 = ind2 + &sw
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
460 " restore the cursor to its original position
461 call cursor(vlnu, vcol)
465 function GetTclIndent()
466 let l:val = s:GetTclIndent(v:lnum)
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