blob: fdafb622e55deb449567eba167934528fb14b9b5 [file] [log] [blame]
Bram Moolenaar0fd92892006-03-09 22:27:48 +00001" Vim indent file
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00002" Language: SQL
Bram Moolenaar0fd92892006-03-09 22:27:48 +00003" Maintainer: David Fishburn <fishburn at ianywhere dot com>
Bram Moolenaara7241f52008-06-24 20:39:31 +00004" Last Change: Mon Apr 02 2007 9:13:47 AM
5" Version: 1.5
Bram Moolenaar0fd92892006-03-09 22:27:48 +00006" 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.
Bram Moolenaarc9b4b052006-04-30 18:54:39 +000013" This has not been tested against Microsoft SQL Server or
Bram Moolenaar0fd92892006-03-09 22:27:48 +000014" 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.
23if exists("b:did_indent")
24 finish
25endif
26let b:did_indent = 1
27let b:current_indent = "sqlanywhere"
28
29setlocal indentkeys-=0{
30setlocal indentkeys-=0}
31setlocal indentkeys-=:
32setlocal indentkeys-=0#
33setlocal indentkeys-=e
34
Bram Moolenaarc9b4b052006-04-30 18:54:39 +000035" This indicates formatting should take place when one of these
Bram Moolenaar0fd92892006-03-09 22:27:48 +000036" 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
41setlocal indentkeys+==~end,=~else,=~elseif,=~elsif,0=~when,0=)
42
Bram Moolenaarc9b4b052006-04-30 18:54:39 +000043" GetSQLIndent is executed whenever one of the expressions
Bram Moolenaar0fd92892006-03-09 22:27:48 +000044" in the indentkeys is typed
45setlocal indentexpr=GetSQLIndent()
46
47" Only define the functions once.
48if exists("*GetSQLIndent")
49 finish
50endif
51
52" List of all the statements that start a new block.
53" These are typically words that start a line.
Bram Moolenaarc9b4b052006-04-30 18:54:39 +000054" IS is excluded, since it is difficult to determine when the
Bram Moolenaar0fd92892006-03-09 22:27:48 +000055" ending block is (especially for procedures/functions).
56let s:SQLBlockStart = '^\s*\%('.
57 \ 'if\|else\|elseif\|elsif\|'.
58 \ 'while\|loop\|do\|'.
59 \ 'begin\|'.
60 \ 'case\|when\|merge\|exception'.
61 \ '\)\>'
62let 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
67function 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
86endfunction
87
88" Unindent commands based on previous indent level
89function 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',
Bram Moolenaara7241f52008-06-24 20:39:31 +0000109 \ 's:IsColComment(line("."), col("."))')
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000110
111 if matching_paran < 1
112 " No match found
113 " echom 'CTIRP - no match found, ignoring'
114 break
115 endif
116
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000117 if matching_paran == lnum
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000118 " 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
139endfunction
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.
152function 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',
Bram Moolenaara7241f52008-06-24 20:39:31 +0000168 \ 's:IsColComment(line("."), col(".")) == 1')
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000169 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(
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000176 \ '\%(\<end\s\+\)\@<!\<case\>\|\<exception\>\|\<merge\>',
177 \ '',
178 \ '\%(\%(\<when\s\+others\>\)\|\%(\<end\s\+case\>\)\)',
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000179 \ 'bW',
Bram Moolenaara7241f52008-06-24 20:39:31 +0000180 \ 's:IsColComment(line("."), col(".")) == 1')
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000181 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
190endfunction
191
192
193" Check if the line is a comment
Bram Moolenaara7241f52008-06-24 20:39:31 +0000194function s:IsLineComment(lnum)
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000195 let rc = synIDattr(
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000196 \ synID(a:lnum,
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000197 \ match(getline(a:lnum), '\S')+1, 0)
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000198 \ , "name")
199 \ =~? "comment"
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000200
201 return rc
202endfunction
203
204
205" Check if the column is a comment
Bram Moolenaara7241f52008-06-24 20:39:31 +0000206function s:IsColComment(lnum, cnum)
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000207 let rc = synIDattr(synID(a:lnum, a:cnum, 0), "name")
208 \ =~? "comment"
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000209
210 return rc
211endfunction
212
213
Bram Moolenaara7241f52008-06-24 20:39:31 +0000214" Instead of returning a column position, return
215" an appropriate value as a factor of shiftwidth.
216function s:ModuloIndent(ind)
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000217 let ind = a:ind
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000218
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000219 if ind > 0
220 let modulo = ind % &shiftwidth
221
222 if modulo > 0
223 let ind = ind - modulo
224 endif
225 endif
226
227 return ind
228endfunction
229
230
231" Find correct indent of a new line based upon the previous line
232function GetSQLIndent()
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000233 let lnum = v:lnum
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000234 let ind = indent(lnum)
235
236 " If the current line is a comment, leave the indent as is
237 " Comment out this additional check since it affects the
238 " indenting of =, and will not reindent comments as it should
Bram Moolenaara7241f52008-06-24 20:39:31 +0000239 " if s:IsLineComment(lnum) == 1
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000240 " return ind
241 " endif
242
243 " while 1
244 " Get previous non-blank line
245 let prevlnum = prevnonblank(lnum - 1)
246 if prevlnum <= 0
247 return ind
248 endif
249
Bram Moolenaara7241f52008-06-24 20:39:31 +0000250 if s:IsLineComment(prevlnum) == 1
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000251 if getline(v:lnum) =~ '^\s*\*'
Bram Moolenaara7241f52008-06-24 20:39:31 +0000252 let ind = s:ModuloIndent(indent(prevlnum))
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000253 return ind + 1
254 endif
255 " If the previous line is a comment, then return -1
256 " to tell Vim to use the formatoptions setting to determine
257 " the indent to use
258 " But only if the next line is blank. This would be true if
259 " the user is typing, but it would not be true if the user
260 " is reindenting the file
261 if getline(v:lnum) =~ '^\s*$'
262 return -1
263 endif
264 endif
265
266 " let prevline = getline(prevlnum)
267 " if prevline !~ '^\s*$'
268 " " echom 'previous non blank - break: ' . prevline
269 " break
270 " endif
271 " endwhile
272
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000273 " echom 'PREVIOUS INDENT: ' . indent(prevlnum) . ' LINE: ' . getline(prevlnum)
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000274
275 " This is the line you just hit return on, it is not the current line
276 " which is new and empty
277 " Based on this line, we can determine how much to indent the new
278 " line
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000279
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000280 " Get default indent (from prev. line)
281 let ind = indent(prevlnum)
282 let prevline = getline(prevlnum)
283
284 " Now check what's on the previous line to determine if the indent
285 " should be changed, for example IF, BEGIN, should increase the indent
286 " where END IF, END, should decrease the indent.
287 if prevline =~? s:SQLBlockStart
288 " Move indent in
289 let ind = ind + &sw
290 " echom 'prevl - SQLBlockStart - indent ' . ind . ' line: ' . prevline
291 elseif prevline =~ '[()]'
292 if prevline =~ '('
293 let num_unmatched_left = s:CountUnbalancedParan( prevline, '(' )
294 else
295 let num_unmatched_left = 0
296 endif
297 if prevline =~ ')'
298 let num_unmatched_right = s:CountUnbalancedParan( prevline, ')' )
299 else
300 let num_unmatched_right = 0
301 " let num_unmatched_right = s:CountUnbalancedParan( prevline, ')' )
302 endif
303 if num_unmatched_left > 0
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000304 " There is a open left paranethesis
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000305 " increase indent
306 let ind = ind + ( &sw * num_unmatched_left )
307 elseif num_unmatched_right > 0
308 " if it is an unbalanced paranethesis only unindent if
309 " it was part of a command (ie create table(..) )
310 " instead of part of an if (ie if (....) then) which should
311 " maintain the indent level
312 let ignore = s:CheckToIgnoreRightParan( prevlnum, num_unmatched_right )
313 " echom 'prevl - ) unbalanced - CTIRP - ignore: ' . ignore
314
315 if prevline =~ '^\s*)'
316 let ignore = ignore + 1
317 " echom 'prevl - begins ) unbalanced ignore: ' . ignore
318 endif
319
320 if (num_unmatched_right - ignore) > 0
321 let ind = ind - ( &sw * (num_unmatched_right - ignore) )
322 endif
323
324 endif
325 endif
326
327
328 " echom 'CURRENT INDENT: ' . ind . ' LINE: ' . getline(v:lnum)
329
330 " This is a new blank line since we just typed a carriage return
331 " Check current line; search for simplistic matching start-of-block
332 let line = getline(v:lnum)
333
334 if line =~? '^\s*els'
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000335 " Any line when you type else will automatically back up one
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000336 " ident level (ie else, elseif, elsif)
337 let ind = ind - &sw
338 " echom 'curr - else - indent ' . ind
339 elseif line =~? '^\s*end\>'
340 let ind = s:GetStmtStarterIndent('end', v:lnum)
341 " General case for end
342 " let ind = ind - &sw
343 " echom 'curr - end - indent ' . ind
344 elseif line =~? '^\s*when\>'
345 let ind = s:GetStmtStarterIndent('when', v:lnum)
346 " If the WHEN clause is used with a MERGE or EXCEPTION
347 " clause, do not change the indent level, since these
348 " statements do not have a corresponding END statement.
349 " if stmt_starter =~? 'case'
350 " let ind = ind - &sw
351 " endif
352 " elseif line =~ '^\s*)\s*;\?\s*$'
353 " elseif line =~ '^\s*)'
354 elseif line =~ '^\s*)'
355 let num_unmatched_right = s:CountUnbalancedParan( line, ')' )
356 let ignore = s:CheckToIgnoreRightParan( v:lnum, num_unmatched_right )
357 " If the line ends in a ), then reduce the indent
358 " This catches items like:
359 " CREATE TABLE T1(
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000360 " c1 int,
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000361 " c2 int
362 " );
363 " But we do not want to unindent a line like:
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000364 " IF ( c1 = 1
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000365 " AND c2 = 3 ) THEN
366 " let num_unmatched_right = s:CountUnbalancedParan( line, ')' )
367 " if num_unmatched_right > 0
368 " elseif strpart( line, strlen(line)-1, 1 ) =~ ')'
369 " let ind = ind - &sw
370 if line =~ '^\s*)'
371 " let ignore = ignore + 1
372 " echom 'curr - begins ) unbalanced ignore: ' . ignore
373 endif
374
375 if (num_unmatched_right - ignore) > 0
376 let ind = ind - ( &sw * (num_unmatched_right - ignore) )
377 endif
378 " endif
379 endif
380
381 " echom 'final - indent ' . ind
Bram Moolenaara7241f52008-06-24 20:39:31 +0000382 return s:ModuloIndent(ind)
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000383endfunction
384
Bram Moolenaara7241f52008-06-24 20:39:31 +0000385" vim:sw=4: