718
|
1 " Vim indent file
|
856
|
2 " Language: SQL
|
718
|
3 " Maintainer: David Fishburn <fishburn at ianywhere dot com>
|
|
4 " Last Change: Wed Sep 14 2005 10:21:15 PM
|
|
5 " Version: 1.4
|
|
6 " Download: http://vim.sourceforge.net/script.php?script_id=495
|
|
7
|
|
8 " Notes:
|
|
9 " Indenting keywords are based on Oracle and Sybase Adaptive Server
|
|
10 " Anywhere (ASA). Test indenting was done with ASA stored procedures and
|
|
11 " fuctions and Oracle packages which contain stored procedures and
|
|
12 " functions.
|
856
|
13 " This has not been tested against Microsoft SQL Server or
|
718
|
14 " Sybase Adaptive Server Enterprise (ASE) which use the Transact-SQL
|
|
15 " syntax. That syntax does not have end tags for IF's, which makes
|
|
16 " indenting more difficult.
|
|
17 "
|
|
18 " Known Issues:
|
|
19 " The Oracle MERGE statement does not have an end tag associated with
|
|
20 " it, this can leave the indent hanging to the right one too many.
|
|
21
|
|
22 " Only load this indent file when no other was loaded.
|
|
23 if exists("b:did_indent")
|
|
24 finish
|
|
25 endif
|
|
26 let b:did_indent = 1
|
|
27 let b:current_indent = "sqlanywhere"
|
|
28
|
|
29 setlocal indentkeys-=0{
|
|
30 setlocal indentkeys-=0}
|
|
31 setlocal indentkeys-=:
|
|
32 setlocal indentkeys-=0#
|
|
33 setlocal indentkeys-=e
|
|
34
|
856
|
35 " This indicates formatting should take place when one of these
|
718
|
36 " expressions is used. These expressions would normally be something
|
|
37 " you would type at the BEGINNING of a line
|
|
38 " SQL is generally case insensitive, so this files assumes that
|
|
39 " These keywords are something that would trigger an indent LEFT, not
|
|
40 " an indent right, since the SQLBlockStart is used for those keywords
|
|
41 setlocal indentkeys+==~end,=~else,=~elseif,=~elsif,0=~when,0=)
|
|
42
|
856
|
43 " GetSQLIndent is executed whenever one of the expressions
|
718
|
44 " in the indentkeys is typed
|
|
45 setlocal indentexpr=GetSQLIndent()
|
|
46
|
|
47 " Only define the functions once.
|
|
48 if exists("*GetSQLIndent")
|
|
49 finish
|
|
50 endif
|
|
51
|
|
52 " List of all the statements that start a new block.
|
|
53 " These are typically words that start a line.
|
856
|
54 " IS is excluded, since it is difficult to determine when the
|
718
|
55 " ending block is (especially for procedures/functions).
|
|
56 let s:SQLBlockStart = '^\s*\%('.
|
|
57 \ 'if\|else\|elseif\|elsif\|'.
|
|
58 \ 'while\|loop\|do\|'.
|
|
59 \ 'begin\|'.
|
|
60 \ 'case\|when\|merge\|exception'.
|
|
61 \ '\)\>'
|
|
62 let s:SQLBlockEnd = '^\s*\(end\)\>'
|
|
63
|
|
64 " The indent level is also based on unmatched paranethesis
|
|
65 " If a line has an extra "(" increase the indent
|
|
66 " If a line has an extra ")" decrease the indent
|
|
67 function s:CountUnbalancedParan( line, paran_to_check )
|
|
68 let l = a:line
|
|
69 let lp = substitute(l, '[^(]', '', 'g')
|
|
70 let l = a:line
|
|
71 let rp = substitute(l, '[^)]', '', 'g')
|
|
72
|
|
73 if a:paran_to_check =~ ')'
|
|
74 " echom 'CountUnbalancedParan ) returning: ' .
|
|
75 " \ (strlen(rp) - strlen(lp))
|
|
76 return (strlen(rp) - strlen(lp))
|
|
77 elseif a:paran_to_check =~ '('
|
|
78 " echom 'CountUnbalancedParan ( returning: ' .
|
|
79 " \ (strlen(lp) - strlen(rp))
|
|
80 return (strlen(lp) - strlen(rp))
|
|
81 else
|
|
82 " echom 'CountUnbalancedParan unknown paran to check: ' .
|
|
83 " \ a:paran_to_check
|
|
84 return 0
|
|
85 endif
|
|
86 endfunction
|
|
87
|
|
88 " Unindent commands based on previous indent level
|
|
89 function s:CheckToIgnoreRightParan( prev_lnum, num_levels )
|
|
90 let lnum = a:prev_lnum
|
|
91 let line = getline(lnum)
|
|
92 let ends = 0
|
|
93 let num_right_paran = a:num_levels
|
|
94 let ignore_paran = 0
|
|
95 let vircol = 1
|
|
96
|
|
97 while num_right_paran > 0
|
|
98 silent! exec 'norm! '.lnum."G\<bar>".vircol."\<bar>"
|
|
99 let right_paran = search( ')', 'W' )
|
|
100 if right_paran != lnum
|
|
101 " This should not happen since there should be at least
|
|
102 " num_right_paran matches for this line
|
|
103 break
|
|
104 endif
|
|
105 let vircol = virtcol(".")
|
|
106
|
|
107 " if getline(".") =~ '^)'
|
|
108 let matching_paran = searchpair('(', '', ')', 'bW',
|
|
109 \ 'IsColComment(line("."), col("."))')
|
|
110
|
|
111 if matching_paran < 1
|
|
112 " No match found
|
|
113 " echom 'CTIRP - no match found, ignoring'
|
|
114 break
|
|
115 endif
|
|
116
|
856
|
117 if matching_paran == lnum
|
718
|
118 " This was not an unmatched parantenses, start the search again
|
|
119 " again after this column
|
|
120 " echom 'CTIRP - same line match, ignoring'
|
|
121 continue
|
|
122 endif
|
|
123
|
|
124 " echom 'CTIRP - match: ' . line(".") . ' ' . getline(".")
|
|
125
|
|
126 if getline(matching_paran) =~? '\(if\|while\)\>'
|
|
127 " echom 'CTIRP - if/while ignored: ' . line(".") . ' ' . getline(".")
|
|
128 let ignore_paran = ignore_paran + 1
|
|
129 endif
|
|
130
|
|
131 " One match found, decrease and check for further matches
|
|
132 let num_right_paran = num_right_paran - 1
|
|
133
|
|
134 endwhile
|
|
135
|
|
136 " Fallback - just move back one
|
|
137 " return a:prev_indent - &sw
|
|
138 return ignore_paran
|
|
139 endfunction
|
|
140
|
|
141 " Based on the keyword provided, loop through previous non empty
|
|
142 " non comment lines to find the statement that initated the keyword.
|
|
143 " Return its indent level
|
|
144 " CASE ..
|
|
145 " WHEN ...
|
|
146 " Should return indent level of CASE
|
|
147 " EXCEPTION ..
|
|
148 " WHEN ...
|
|
149 " something;
|
|
150 " WHEN ...
|
|
151 " Should return indent level of exception.
|
|
152 function s:GetStmtStarterIndent( keyword, curr_lnum )
|
|
153 let lnum = a:curr_lnum
|
|
154
|
|
155 " Default - reduce indent by 1
|
|
156 let ind = indent(a:curr_lnum) - &sw
|
|
157
|
|
158 if a:keyword =~? 'end'
|
|
159 exec 'normal! ^'
|
|
160 let stmts = '^\s*\%('.
|
|
161 \ '\<begin\>\|' .
|
|
162 \ '\%(\%(\<end\s\+\)\@<!\<loop\>\)\|' .
|
|
163 \ '\%(\%(\<end\s\+\)\@<!\<case\>\)\|' .
|
|
164 \ '\%(\%(\<end\s\+\)\@<!\<for\>\)\|' .
|
|
165 \ '\%(\%(\<end\s\+\)\@<!\<if\>\)'.
|
|
166 \ '\)'
|
|
167 let matching_lnum = searchpair(stmts, '', '\<end\>\zs', 'bW',
|
|
168 \ 'IsColComment(line("."), col(".")) == 1')
|
|
169 exec 'normal! $'
|
|
170 if matching_lnum > 0 && matching_lnum < a:curr_lnum
|
|
171 let ind = indent(matching_lnum)
|
|
172 endif
|
|
173 elseif a:keyword =~? 'when'
|
|
174 exec 'normal! ^'
|
|
175 let matching_lnum = searchpair(
|
856
|
176 \ '\%(\<end\s\+\)\@<!\<case\>\|\<exception\>\|\<merge\>',
|
|
177 \ '',
|
|
178 \ '\%(\%(\<when\s\+others\>\)\|\%(\<end\s\+case\>\)\)',
|
718
|
179 \ 'bW',
|
|
180 \ 'IsColComment(line("."), col(".")) == 1')
|
|
181 exec 'normal! $'
|
|
182 if matching_lnum > 0 && matching_lnum < a:curr_lnum
|
|
183 let ind = indent(matching_lnum)
|
|
184 else
|
|
185 let ind = indent(a:curr_lnum)
|
|
186 endif
|
|
187 endif
|
|
188
|
|
189 return ind
|
|
190 endfunction
|
|
191
|
|
192
|
|
193 " Check if the line is a comment
|
|
194 function IsLineComment(lnum)
|
|
195 let rc = synIDattr(
|
856
|
196 \ synID(a:lnum,
|
718
|
197 \ match(getline(a:lnum), '\S')+1, 0)
|
856
|
198 \ , "name")
|
|
199 \ =~? "comment"
|
718
|
200
|
|
201 return rc
|
|
202 endfunction
|
|
203
|
|
204
|
|
205 " Check if the column is a comment
|
|
206 function IsColComment(lnum, cnum)
|
856
|
207 let rc = synIDattr(synID(a:lnum, a:cnum, 0), "name")
|
|
208 \ =~? "comment"
|
718
|
209
|
|
210 return rc
|
|
211 endfunction
|
|
212
|
|
213
|
|
214 " Check if the column is a comment
|
|
215 function ModuloIndent(ind)
|
|
216 let ind = a:ind
|
856
|
217
|
718
|
218 if ind > 0
|
|
219 let modulo = ind % &shiftwidth
|
|
220
|
|
221 if modulo > 0
|
|
222 let ind = ind - modulo
|
|
223 endif
|
|
224 endif
|
|
225
|
|
226 return ind
|
|
227 endfunction
|
|
228
|
|
229
|
|
230 " Find correct indent of a new line based upon the previous line
|
|
231 function GetSQLIndent()
|
856
|
232 let lnum = v:lnum
|
718
|
233 let ind = indent(lnum)
|
|
234
|
|
235 " If the current line is a comment, leave the indent as is
|
|
236 " Comment out this additional check since it affects the
|
|
237 " indenting of =, and will not reindent comments as it should
|
|
238 " if IsLineComment(lnum) == 1
|
|
239 " return ind
|
|
240 " endif
|
|
241
|
|
242 " while 1
|
|
243 " Get previous non-blank line
|
|
244 let prevlnum = prevnonblank(lnum - 1)
|
|
245 if prevlnum <= 0
|
|
246 return ind
|
|
247 endif
|
|
248
|
856
|
249 if IsLineComment(prevlnum) == 1
|
718
|
250 if getline(v:lnum) =~ '^\s*\*'
|
|
251 let ind = ModuloIndent(indent(prevlnum))
|
|
252 return ind + 1
|
|
253 endif
|
|
254 " If the previous line is a comment, then return -1
|
|
255 " to tell Vim to use the formatoptions setting to determine
|
|
256 " the indent to use
|
|
257 " But only if the next line is blank. This would be true if
|
|
258 " the user is typing, but it would not be true if the user
|
|
259 " is reindenting the file
|
|
260 if getline(v:lnum) =~ '^\s*$'
|
|
261 return -1
|
|
262 endif
|
|
263 endif
|
|
264
|
|
265 " let prevline = getline(prevlnum)
|
|
266 " if prevline !~ '^\s*$'
|
|
267 " " echom 'previous non blank - break: ' . prevline
|
|
268 " break
|
|
269 " endif
|
|
270 " endwhile
|
|
271
|
856
|
272 " echom 'PREVIOUS INDENT: ' . indent(prevlnum) . ' LINE: ' . getline(prevlnum)
|
718
|
273
|
|
274 " This is the line you just hit return on, it is not the current line
|
|
275 " which is new and empty
|
|
276 " Based on this line, we can determine how much to indent the new
|
|
277 " line
|
856
|
278
|
718
|
279 " Get default indent (from prev. line)
|
|
280 let ind = indent(prevlnum)
|
|
281 let prevline = getline(prevlnum)
|
|
282
|
|
283 " Now check what's on the previous line to determine if the indent
|
|
284 " should be changed, for example IF, BEGIN, should increase the indent
|
|
285 " where END IF, END, should decrease the indent.
|
|
286 if prevline =~? s:SQLBlockStart
|
|
287 " Move indent in
|
|
288 let ind = ind + &sw
|
|
289 " echom 'prevl - SQLBlockStart - indent ' . ind . ' line: ' . prevline
|
|
290 elseif prevline =~ '[()]'
|
|
291 if prevline =~ '('
|
|
292 let num_unmatched_left = s:CountUnbalancedParan( prevline, '(' )
|
|
293 else
|
|
294 let num_unmatched_left = 0
|
|
295 endif
|
|
296 if prevline =~ ')'
|
|
297 let num_unmatched_right = s:CountUnbalancedParan( prevline, ')' )
|
|
298 else
|
|
299 let num_unmatched_right = 0
|
|
300 " let num_unmatched_right = s:CountUnbalancedParan( prevline, ')' )
|
|
301 endif
|
|
302 if num_unmatched_left > 0
|
856
|
303 " There is a open left paranethesis
|
718
|
304 " increase indent
|
|
305 let ind = ind + ( &sw * num_unmatched_left )
|
|
306 elseif num_unmatched_right > 0
|
|
307 " if it is an unbalanced paranethesis only unindent if
|
|
308 " it was part of a command (ie create table(..) )
|
|
309 " instead of part of an if (ie if (....) then) which should
|
|
310 " maintain the indent level
|
|
311 let ignore = s:CheckToIgnoreRightParan( prevlnum, num_unmatched_right )
|
|
312 " echom 'prevl - ) unbalanced - CTIRP - ignore: ' . ignore
|
|
313
|
|
314 if prevline =~ '^\s*)'
|
|
315 let ignore = ignore + 1
|
|
316 " echom 'prevl - begins ) unbalanced ignore: ' . ignore
|
|
317 endif
|
|
318
|
|
319 if (num_unmatched_right - ignore) > 0
|
|
320 let ind = ind - ( &sw * (num_unmatched_right - ignore) )
|
|
321 endif
|
|
322
|
|
323 endif
|
|
324 endif
|
|
325
|
|
326
|
|
327 " echom 'CURRENT INDENT: ' . ind . ' LINE: ' . getline(v:lnum)
|
|
328
|
|
329 " This is a new blank line since we just typed a carriage return
|
|
330 " Check current line; search for simplistic matching start-of-block
|
|
331 let line = getline(v:lnum)
|
|
332
|
|
333 if line =~? '^\s*els'
|
856
|
334 " Any line when you type else will automatically back up one
|
718
|
335 " ident level (ie else, elseif, elsif)
|
|
336 let ind = ind - &sw
|
|
337 " echom 'curr - else - indent ' . ind
|
|
338 elseif line =~? '^\s*end\>'
|
|
339 let ind = s:GetStmtStarterIndent('end', v:lnum)
|
|
340 " General case for end
|
|
341 " let ind = ind - &sw
|
|
342 " echom 'curr - end - indent ' . ind
|
|
343 elseif line =~? '^\s*when\>'
|
|
344 let ind = s:GetStmtStarterIndent('when', v:lnum)
|
|
345 " If the WHEN clause is used with a MERGE or EXCEPTION
|
|
346 " clause, do not change the indent level, since these
|
|
347 " statements do not have a corresponding END statement.
|
|
348 " if stmt_starter =~? 'case'
|
|
349 " let ind = ind - &sw
|
|
350 " endif
|
|
351 " elseif line =~ '^\s*)\s*;\?\s*$'
|
|
352 " elseif line =~ '^\s*)'
|
|
353 elseif line =~ '^\s*)'
|
|
354 let num_unmatched_right = s:CountUnbalancedParan( line, ')' )
|
|
355 let ignore = s:CheckToIgnoreRightParan( v:lnum, num_unmatched_right )
|
|
356 " If the line ends in a ), then reduce the indent
|
|
357 " This catches items like:
|
|
358 " CREATE TABLE T1(
|
856
|
359 " c1 int,
|
718
|
360 " c2 int
|
|
361 " );
|
|
362 " But we do not want to unindent a line like:
|
856
|
363 " IF ( c1 = 1
|
718
|
364 " AND c2 = 3 ) THEN
|
|
365 " let num_unmatched_right = s:CountUnbalancedParan( line, ')' )
|
|
366 " if num_unmatched_right > 0
|
|
367 " elseif strpart( line, strlen(line)-1, 1 ) =~ ')'
|
|
368 " let ind = ind - &sw
|
|
369 if line =~ '^\s*)'
|
|
370 " let ignore = ignore + 1
|
|
371 " echom 'curr - begins ) unbalanced ignore: ' . ignore
|
|
372 endif
|
|
373
|
|
374 if (num_unmatched_right - ignore) > 0
|
|
375 let ind = ind - ( &sw * (num_unmatched_right - ignore) )
|
|
376 endif
|
|
377 " endif
|
|
378 endif
|
|
379
|
|
380 " echom 'final - indent ' . ind
|
|
381 return ModuloIndent(ind)
|
|
382 endfunction
|
|
383
|
|
384 " vim:sw=4:ff=unix:
|