blob: afc312f8e0e520d520d2385ff52e24982566a1ca [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
Bram Moolenaar8e52a592012-05-18 21:49:28 +020051let s:keepcpo= &cpo
52set cpo&vim
Bram Moolenaar0fd92892006-03-09 22:27:48 +000053
54" List of all the statements that start a new block.
55" These are typically words that start a line.
Bram Moolenaarc9b4b052006-04-30 18:54:39 +000056" IS is excluded, since it is difficult to determine when the
Bram Moolenaar0fd92892006-03-09 22:27:48 +000057" ending block is (especially for procedures/functions).
58let s:SQLBlockStart = '^\s*\%('.
59 \ 'if\|else\|elseif\|elsif\|'.
60 \ 'while\|loop\|do\|'.
61 \ 'begin\|'.
62 \ 'case\|when\|merge\|exception'.
63 \ '\)\>'
64let s:SQLBlockEnd = '^\s*\(end\)\>'
65
66" The indent level is also based on unmatched paranethesis
67" If a line has an extra "(" increase the indent
68" If a line has an extra ")" decrease the indent
69function s:CountUnbalancedParan( line, paran_to_check )
70 let l = a:line
71 let lp = substitute(l, '[^(]', '', 'g')
72 let l = a:line
73 let rp = substitute(l, '[^)]', '', 'g')
74
75 if a:paran_to_check =~ ')'
76 " echom 'CountUnbalancedParan ) returning: ' .
77 " \ (strlen(rp) - strlen(lp))
78 return (strlen(rp) - strlen(lp))
79 elseif a:paran_to_check =~ '('
80 " echom 'CountUnbalancedParan ( returning: ' .
81 " \ (strlen(lp) - strlen(rp))
82 return (strlen(lp) - strlen(rp))
83 else
84 " echom 'CountUnbalancedParan unknown paran to check: ' .
85 " \ a:paran_to_check
86 return 0
87 endif
88endfunction
89
90" Unindent commands based on previous indent level
91function s:CheckToIgnoreRightParan( prev_lnum, num_levels )
92 let lnum = a:prev_lnum
93 let line = getline(lnum)
94 let ends = 0
95 let num_right_paran = a:num_levels
96 let ignore_paran = 0
97 let vircol = 1
98
99 while num_right_paran > 0
100 silent! exec 'norm! '.lnum."G\<bar>".vircol."\<bar>"
101 let right_paran = search( ')', 'W' )
102 if right_paran != lnum
103 " This should not happen since there should be at least
104 " num_right_paran matches for this line
105 break
106 endif
107 let vircol = virtcol(".")
108
109 " if getline(".") =~ '^)'
110 let matching_paran = searchpair('(', '', ')', 'bW',
Bram Moolenaara7241f52008-06-24 20:39:31 +0000111 \ 's:IsColComment(line("."), col("."))')
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000112
113 if matching_paran < 1
114 " No match found
115 " echom 'CTIRP - no match found, ignoring'
116 break
117 endif
118
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000119 if matching_paran == lnum
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000120 " This was not an unmatched parantenses, start the search again
121 " again after this column
122 " echom 'CTIRP - same line match, ignoring'
123 continue
124 endif
125
126 " echom 'CTIRP - match: ' . line(".") . ' ' . getline(".")
127
128 if getline(matching_paran) =~? '\(if\|while\)\>'
129 " echom 'CTIRP - if/while ignored: ' . line(".") . ' ' . getline(".")
130 let ignore_paran = ignore_paran + 1
131 endif
132
133 " One match found, decrease and check for further matches
134 let num_right_paran = num_right_paran - 1
135
136 endwhile
137
138 " Fallback - just move back one
139 " return a:prev_indent - &sw
140 return ignore_paran
141endfunction
142
143" Based on the keyword provided, loop through previous non empty
144" non comment lines to find the statement that initated the keyword.
145" Return its indent level
146" CASE ..
147" WHEN ...
148" Should return indent level of CASE
149" EXCEPTION ..
150" WHEN ...
151" something;
152" WHEN ...
153" Should return indent level of exception.
154function s:GetStmtStarterIndent( keyword, curr_lnum )
155 let lnum = a:curr_lnum
156
157 " Default - reduce indent by 1
158 let ind = indent(a:curr_lnum) - &sw
159
160 if a:keyword =~? 'end'
161 exec 'normal! ^'
162 let stmts = '^\s*\%('.
163 \ '\<begin\>\|' .
164 \ '\%(\%(\<end\s\+\)\@<!\<loop\>\)\|' .
165 \ '\%(\%(\<end\s\+\)\@<!\<case\>\)\|' .
166 \ '\%(\%(\<end\s\+\)\@<!\<for\>\)\|' .
167 \ '\%(\%(\<end\s\+\)\@<!\<if\>\)'.
168 \ '\)'
169 let matching_lnum = searchpair(stmts, '', '\<end\>\zs', 'bW',
Bram Moolenaara7241f52008-06-24 20:39:31 +0000170 \ 's:IsColComment(line("."), col(".")) == 1')
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000171 exec 'normal! $'
172 if matching_lnum > 0 && matching_lnum < a:curr_lnum
173 let ind = indent(matching_lnum)
174 endif
175 elseif a:keyword =~? 'when'
176 exec 'normal! ^'
177 let matching_lnum = searchpair(
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000178 \ '\%(\<end\s\+\)\@<!\<case\>\|\<exception\>\|\<merge\>',
179 \ '',
180 \ '\%(\%(\<when\s\+others\>\)\|\%(\<end\s\+case\>\)\)',
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000181 \ 'bW',
Bram Moolenaara7241f52008-06-24 20:39:31 +0000182 \ 's:IsColComment(line("."), col(".")) == 1')
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000183 exec 'normal! $'
184 if matching_lnum > 0 && matching_lnum < a:curr_lnum
185 let ind = indent(matching_lnum)
186 else
187 let ind = indent(a:curr_lnum)
188 endif
189 endif
190
191 return ind
192endfunction
193
194
195" Check if the line is a comment
Bram Moolenaara7241f52008-06-24 20:39:31 +0000196function s:IsLineComment(lnum)
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000197 let rc = synIDattr(
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000198 \ synID(a:lnum,
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000199 \ match(getline(a:lnum), '\S')+1, 0)
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000200 \ , "name")
201 \ =~? "comment"
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000202
203 return rc
204endfunction
205
206
207" Check if the column is a comment
Bram Moolenaara7241f52008-06-24 20:39:31 +0000208function s:IsColComment(lnum, cnum)
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000209 let rc = synIDattr(synID(a:lnum, a:cnum, 0), "name")
210 \ =~? "comment"
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000211
212 return rc
213endfunction
214
215
Bram Moolenaara7241f52008-06-24 20:39:31 +0000216" Instead of returning a column position, return
217" an appropriate value as a factor of shiftwidth.
218function s:ModuloIndent(ind)
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000219 let ind = a:ind
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000220
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000221 if ind > 0
222 let modulo = ind % &shiftwidth
223
224 if modulo > 0
225 let ind = ind - modulo
226 endif
227 endif
228
229 return ind
230endfunction
231
232
233" Find correct indent of a new line based upon the previous line
234function GetSQLIndent()
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000235 let lnum = v:lnum
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000236 let ind = indent(lnum)
237
238 " If the current line is a comment, leave the indent as is
239 " Comment out this additional check since it affects the
240 " indenting of =, and will not reindent comments as it should
Bram Moolenaara7241f52008-06-24 20:39:31 +0000241 " if s:IsLineComment(lnum) == 1
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000242 " return ind
243 " endif
244
245 " while 1
246 " Get previous non-blank line
247 let prevlnum = prevnonblank(lnum - 1)
248 if prevlnum <= 0
249 return ind
250 endif
251
Bram Moolenaara7241f52008-06-24 20:39:31 +0000252 if s:IsLineComment(prevlnum) == 1
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000253 if getline(v:lnum) =~ '^\s*\*'
Bram Moolenaara7241f52008-06-24 20:39:31 +0000254 let ind = s:ModuloIndent(indent(prevlnum))
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000255 return ind + 1
256 endif
257 " If the previous line is a comment, then return -1
258 " to tell Vim to use the formatoptions setting to determine
259 " the indent to use
260 " But only if the next line is blank. This would be true if
261 " the user is typing, but it would not be true if the user
262 " is reindenting the file
263 if getline(v:lnum) =~ '^\s*$'
264 return -1
265 endif
266 endif
267
268 " let prevline = getline(prevlnum)
269 " if prevline !~ '^\s*$'
270 " " echom 'previous non blank - break: ' . prevline
271 " break
272 " endif
273 " endwhile
274
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000275 " echom 'PREVIOUS INDENT: ' . indent(prevlnum) . ' LINE: ' . getline(prevlnum)
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000276
277 " This is the line you just hit return on, it is not the current line
278 " which is new and empty
279 " Based on this line, we can determine how much to indent the new
280 " line
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000281
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000282 " Get default indent (from prev. line)
283 let ind = indent(prevlnum)
284 let prevline = getline(prevlnum)
285
286 " Now check what's on the previous line to determine if the indent
287 " should be changed, for example IF, BEGIN, should increase the indent
288 " where END IF, END, should decrease the indent.
289 if prevline =~? s:SQLBlockStart
290 " Move indent in
291 let ind = ind + &sw
292 " echom 'prevl - SQLBlockStart - indent ' . ind . ' line: ' . prevline
293 elseif prevline =~ '[()]'
294 if prevline =~ '('
295 let num_unmatched_left = s:CountUnbalancedParan( prevline, '(' )
296 else
297 let num_unmatched_left = 0
298 endif
299 if prevline =~ ')'
300 let num_unmatched_right = s:CountUnbalancedParan( prevline, ')' )
301 else
302 let num_unmatched_right = 0
303 " let num_unmatched_right = s:CountUnbalancedParan( prevline, ')' )
304 endif
305 if num_unmatched_left > 0
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000306 " There is a open left paranethesis
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000307 " increase indent
308 let ind = ind + ( &sw * num_unmatched_left )
309 elseif num_unmatched_right > 0
310 " if it is an unbalanced paranethesis only unindent if
311 " it was part of a command (ie create table(..) )
312 " instead of part of an if (ie if (....) then) which should
313 " maintain the indent level
314 let ignore = s:CheckToIgnoreRightParan( prevlnum, num_unmatched_right )
315 " echom 'prevl - ) unbalanced - CTIRP - ignore: ' . ignore
316
317 if prevline =~ '^\s*)'
318 let ignore = ignore + 1
319 " echom 'prevl - begins ) unbalanced ignore: ' . ignore
320 endif
321
322 if (num_unmatched_right - ignore) > 0
323 let ind = ind - ( &sw * (num_unmatched_right - ignore) )
324 endif
325
326 endif
327 endif
328
329
330 " echom 'CURRENT INDENT: ' . ind . ' LINE: ' . getline(v:lnum)
331
332 " This is a new blank line since we just typed a carriage return
333 " Check current line; search for simplistic matching start-of-block
334 let line = getline(v:lnum)
335
336 if line =~? '^\s*els'
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000337 " Any line when you type else will automatically back up one
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000338 " ident level (ie else, elseif, elsif)
339 let ind = ind - &sw
340 " echom 'curr - else - indent ' . ind
341 elseif line =~? '^\s*end\>'
342 let ind = s:GetStmtStarterIndent('end', v:lnum)
343 " General case for end
344 " let ind = ind - &sw
345 " echom 'curr - end - indent ' . ind
346 elseif line =~? '^\s*when\>'
347 let ind = s:GetStmtStarterIndent('when', v:lnum)
348 " If the WHEN clause is used with a MERGE or EXCEPTION
349 " clause, do not change the indent level, since these
350 " statements do not have a corresponding END statement.
351 " if stmt_starter =~? 'case'
352 " let ind = ind - &sw
353 " endif
354 " elseif line =~ '^\s*)\s*;\?\s*$'
355 " elseif line =~ '^\s*)'
356 elseif line =~ '^\s*)'
357 let num_unmatched_right = s:CountUnbalancedParan( line, ')' )
358 let ignore = s:CheckToIgnoreRightParan( v:lnum, num_unmatched_right )
359 " If the line ends in a ), then reduce the indent
360 " This catches items like:
361 " CREATE TABLE T1(
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000362 " c1 int,
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000363 " c2 int
364 " );
365 " But we do not want to unindent a line like:
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000366 " IF ( c1 = 1
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000367 " AND c2 = 3 ) THEN
368 " let num_unmatched_right = s:CountUnbalancedParan( line, ')' )
369 " if num_unmatched_right > 0
370 " elseif strpart( line, strlen(line)-1, 1 ) =~ ')'
371 " let ind = ind - &sw
372 if line =~ '^\s*)'
373 " let ignore = ignore + 1
374 " echom 'curr - begins ) unbalanced ignore: ' . ignore
375 endif
376
377 if (num_unmatched_right - ignore) > 0
378 let ind = ind - ( &sw * (num_unmatched_right - ignore) )
379 endif
380 " endif
381 endif
382
383 " echom 'final - indent ' . ind
Bram Moolenaara7241f52008-06-24 20:39:31 +0000384 return s:ModuloIndent(ind)
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000385endfunction
386
Bram Moolenaar8e52a592012-05-18 21:49:28 +0200387let &cpo = s:keepcpo
388unlet s:keepcpo
389
Bram Moolenaara7241f52008-06-24 20:39:31 +0000390" vim:sw=4: