blob: ba35d7671554ff0829890a27ccb923974c214de5 [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 Moolenaar34feacb2012-12-05 19:01:43 +01003" Maintainer: David Fishburn <dfishburn dot vim at gmail dot com>
Bram Moolenaar3ec574f2017-06-13 18:12:01 +02004" Last Change: 2017 Jun 13
Bram Moolenaar34feacb2012-12-05 19:01:43 +01005" Version: 3.0
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.
Bram Moolenaar34feacb2012-12-05 19:01:43 +010021"
22" History:
23" 3.0 (Dec 2012)
24" Added cpo check
25"
26" 2.0
27" Added the FOR keyword to SQLBlockStart to handle (Alec Tica):
28" for i in 1..100 loop
29" |<-- I expect to have indentation here
30" end loop;
31"
Bram Moolenaar0fd92892006-03-09 22:27:48 +000032
33" Only load this indent file when no other was loaded.
34if exists("b:did_indent")
35 finish
36endif
37let b:did_indent = 1
38let b:current_indent = "sqlanywhere"
39
40setlocal indentkeys-=0{
41setlocal indentkeys-=0}
42setlocal indentkeys-=:
43setlocal indentkeys-=0#
44setlocal indentkeys-=e
45
Bram Moolenaarc9b4b052006-04-30 18:54:39 +000046" This indicates formatting should take place when one of these
Bram Moolenaar0fd92892006-03-09 22:27:48 +000047" expressions is used. These expressions would normally be something
48" you would type at the BEGINNING of a line
49" SQL is generally case insensitive, so this files assumes that
50" These keywords are something that would trigger an indent LEFT, not
51" an indent right, since the SQLBlockStart is used for those keywords
52setlocal indentkeys+==~end,=~else,=~elseif,=~elsif,0=~when,0=)
53
Bram Moolenaarc9b4b052006-04-30 18:54:39 +000054" GetSQLIndent is executed whenever one of the expressions
Bram Moolenaar0fd92892006-03-09 22:27:48 +000055" in the indentkeys is typed
56setlocal indentexpr=GetSQLIndent()
57
Bram Moolenaara3e6bc92013-01-30 14:18:00 +010058" Only define the functions once.
59if exists("*GetSQLIndent")
60 finish
61endif
62let s:keepcpo= &cpo
63set cpo&vim
64
Bram Moolenaar0fd92892006-03-09 22:27:48 +000065" List of all the statements that start a new block.
66" These are typically words that start a line.
Bram Moolenaarc9b4b052006-04-30 18:54:39 +000067" IS is excluded, since it is difficult to determine when the
Bram Moolenaar0fd92892006-03-09 22:27:48 +000068" ending block is (especially for procedures/functions).
69let s:SQLBlockStart = '^\s*\%('.
Bram Moolenaar34feacb2012-12-05 19:01:43 +010070 \ 'if\|else\|elseif\|elsif\|'.
71 \ 'while\|loop\|do\|for\|'.
Bram Moolenaar0fd92892006-03-09 22:27:48 +000072 \ 'begin\|'.
73 \ 'case\|when\|merge\|exception'.
74 \ '\)\>'
75let s:SQLBlockEnd = '^\s*\(end\)\>'
76
77" The indent level is also based on unmatched paranethesis
78" If a line has an extra "(" increase the indent
79" If a line has an extra ")" decrease the indent
Bram Moolenaar34feacb2012-12-05 19:01:43 +010080function! s:CountUnbalancedParan( line, paran_to_check )
Bram Moolenaar0fd92892006-03-09 22:27:48 +000081 let l = a:line
82 let lp = substitute(l, '[^(]', '', 'g')
83 let l = a:line
84 let rp = substitute(l, '[^)]', '', 'g')
85
86 if a:paran_to_check =~ ')'
87 " echom 'CountUnbalancedParan ) returning: ' .
88 " \ (strlen(rp) - strlen(lp))
89 return (strlen(rp) - strlen(lp))
90 elseif a:paran_to_check =~ '('
91 " echom 'CountUnbalancedParan ( returning: ' .
92 " \ (strlen(lp) - strlen(rp))
93 return (strlen(lp) - strlen(rp))
94 else
95 " echom 'CountUnbalancedParan unknown paran to check: ' .
96 " \ a:paran_to_check
97 return 0
98 endif
99endfunction
100
101" Unindent commands based on previous indent level
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100102function! s:CheckToIgnoreRightParan( prev_lnum, num_levels )
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000103 let lnum = a:prev_lnum
104 let line = getline(lnum)
105 let ends = 0
106 let num_right_paran = a:num_levels
107 let ignore_paran = 0
108 let vircol = 1
109
110 while num_right_paran > 0
111 silent! exec 'norm! '.lnum."G\<bar>".vircol."\<bar>"
112 let right_paran = search( ')', 'W' )
113 if right_paran != lnum
114 " This should not happen since there should be at least
115 " num_right_paran matches for this line
116 break
117 endif
118 let vircol = virtcol(".")
119
120 " if getline(".") =~ '^)'
121 let matching_paran = searchpair('(', '', ')', 'bW',
Bram Moolenaara7241f52008-06-24 20:39:31 +0000122 \ 's:IsColComment(line("."), col("."))')
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000123
124 if matching_paran < 1
125 " No match found
126 " echom 'CTIRP - no match found, ignoring'
127 break
128 endif
129
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000130 if matching_paran == lnum
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000131 " This was not an unmatched parantenses, start the search again
132 " again after this column
133 " echom 'CTIRP - same line match, ignoring'
134 continue
135 endif
136
137 " echom 'CTIRP - match: ' . line(".") . ' ' . getline(".")
138
139 if getline(matching_paran) =~? '\(if\|while\)\>'
140 " echom 'CTIRP - if/while ignored: ' . line(".") . ' ' . getline(".")
141 let ignore_paran = ignore_paran + 1
142 endif
143
144 " One match found, decrease and check for further matches
145 let num_right_paran = num_right_paran - 1
146
147 endwhile
148
149 " Fallback - just move back one
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200150 " return a:prev_indent - shiftwidth()
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000151 return ignore_paran
152endfunction
153
154" Based on the keyword provided, loop through previous non empty
155" non comment lines to find the statement that initated the keyword.
156" Return its indent level
157" CASE ..
158" WHEN ...
159" Should return indent level of CASE
160" EXCEPTION ..
161" WHEN ...
162" something;
163" WHEN ...
164" Should return indent level of exception.
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100165function! s:GetStmtStarterIndent( keyword, curr_lnum )
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000166 let lnum = a:curr_lnum
167
168 " Default - reduce indent by 1
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200169 let ind = indent(a:curr_lnum) - shiftwidth()
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000170
171 if a:keyword =~? 'end'
172 exec 'normal! ^'
173 let stmts = '^\s*\%('.
174 \ '\<begin\>\|' .
175 \ '\%(\%(\<end\s\+\)\@<!\<loop\>\)\|' .
176 \ '\%(\%(\<end\s\+\)\@<!\<case\>\)\|' .
177 \ '\%(\%(\<end\s\+\)\@<!\<for\>\)\|' .
178 \ '\%(\%(\<end\s\+\)\@<!\<if\>\)'.
179 \ '\)'
180 let matching_lnum = searchpair(stmts, '', '\<end\>\zs', 'bW',
Bram Moolenaara7241f52008-06-24 20:39:31 +0000181 \ 's:IsColComment(line("."), col(".")) == 1')
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000182 exec 'normal! $'
183 if matching_lnum > 0 && matching_lnum < a:curr_lnum
184 let ind = indent(matching_lnum)
185 endif
186 elseif a:keyword =~? 'when'
187 exec 'normal! ^'
188 let matching_lnum = searchpair(
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000189 \ '\%(\<end\s\+\)\@<!\<case\>\|\<exception\>\|\<merge\>',
190 \ '',
191 \ '\%(\%(\<when\s\+others\>\)\|\%(\<end\s\+case\>\)\)',
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000192 \ 'bW',
Bram Moolenaara7241f52008-06-24 20:39:31 +0000193 \ 's:IsColComment(line("."), col(".")) == 1')
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000194 exec 'normal! $'
195 if matching_lnum > 0 && matching_lnum < a:curr_lnum
196 let ind = indent(matching_lnum)
197 else
198 let ind = indent(a:curr_lnum)
199 endif
200 endif
201
202 return ind
203endfunction
204
205
206" Check if the line is a comment
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100207function! s:IsLineComment(lnum)
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000208 let rc = synIDattr(
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000209 \ synID(a:lnum,
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000210 \ match(getline(a:lnum), '\S')+1, 0)
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000211 \ , "name")
212 \ =~? "comment"
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000213
214 return rc
215endfunction
216
217
218" Check if the column is a comment
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100219function! s:IsColComment(lnum, cnum)
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000220 let rc = synIDattr(synID(a:lnum, a:cnum, 0), "name")
221 \ =~? "comment"
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000222
223 return rc
224endfunction
225
226
Bram Moolenaara7241f52008-06-24 20:39:31 +0000227" Instead of returning a column position, return
228" an appropriate value as a factor of shiftwidth.
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100229function! s:ModuloIndent(ind)
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000230 let ind = a:ind
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000231
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000232 if ind > 0
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200233 let modulo = ind % shiftwidth()
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000234
235 if modulo > 0
236 let ind = ind - modulo
237 endif
238 endif
239
240 return ind
241endfunction
242
243
244" Find correct indent of a new line based upon the previous line
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100245function! GetSQLIndent()
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000246 let lnum = v:lnum
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000247 let ind = indent(lnum)
248
249 " If the current line is a comment, leave the indent as is
250 " Comment out this additional check since it affects the
251 " indenting of =, and will not reindent comments as it should
Bram Moolenaara7241f52008-06-24 20:39:31 +0000252 " if s:IsLineComment(lnum) == 1
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000253 " return ind
254 " endif
255
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100256 " Get previous non-blank line
257 let prevlnum = prevnonblank(lnum - 1)
258 if prevlnum <= 0
259 return ind
260 endif
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000261
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100262 if s:IsLineComment(prevlnum) == 1
263 if getline(v:lnum) =~ '^\s*\*'
264 let ind = s:ModuloIndent(indent(prevlnum))
265 return ind + 1
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000266 endif
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100267 " If the previous line is a comment, then return -1
268 " to tell Vim to use the formatoptions setting to determine
269 " the indent to use
270 " But only if the next line is blank. This would be true if
271 " the user is typing, but it would not be true if the user
272 " is reindenting the file
273 if getline(v:lnum) =~ '^\s*$'
274 return -1
275 endif
276 endif
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000277
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000278 " echom 'PREVIOUS INDENT: ' . indent(prevlnum) . ' LINE: ' . getline(prevlnum)
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000279
280 " This is the line you just hit return on, it is not the current line
281 " which is new and empty
282 " Based on this line, we can determine how much to indent the new
283 " line
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000284
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000285 " Get default indent (from prev. line)
286 let ind = indent(prevlnum)
287 let prevline = getline(prevlnum)
288
289 " Now check what's on the previous line to determine if the indent
290 " should be changed, for example IF, BEGIN, should increase the indent
291 " where END IF, END, should decrease the indent.
292 if prevline =~? s:SQLBlockStart
293 " Move indent in
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200294 let ind = ind + shiftwidth()
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000295 " echom 'prevl - SQLBlockStart - indent ' . ind . ' line: ' . prevline
296 elseif prevline =~ '[()]'
297 if prevline =~ '('
298 let num_unmatched_left = s:CountUnbalancedParan( prevline, '(' )
299 else
300 let num_unmatched_left = 0
301 endif
302 if prevline =~ ')'
303 let num_unmatched_right = s:CountUnbalancedParan( prevline, ')' )
304 else
305 let num_unmatched_right = 0
306 " let num_unmatched_right = s:CountUnbalancedParan( prevline, ')' )
307 endif
308 if num_unmatched_left > 0
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000309 " There is a open left paranethesis
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000310 " increase indent
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200311 let ind = ind + ( shiftwidth() * num_unmatched_left )
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000312 elseif num_unmatched_right > 0
313 " if it is an unbalanced paranethesis only unindent if
314 " it was part of a command (ie create table(..) )
315 " instead of part of an if (ie if (....) then) which should
316 " maintain the indent level
317 let ignore = s:CheckToIgnoreRightParan( prevlnum, num_unmatched_right )
318 " echom 'prevl - ) unbalanced - CTIRP - ignore: ' . ignore
319
320 if prevline =~ '^\s*)'
321 let ignore = ignore + 1
322 " echom 'prevl - begins ) unbalanced ignore: ' . ignore
323 endif
324
325 if (num_unmatched_right - ignore) > 0
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200326 let ind = ind - ( shiftwidth() * (num_unmatched_right - ignore) )
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000327 endif
328
329 endif
330 endif
331
332
333 " echom 'CURRENT INDENT: ' . ind . ' LINE: ' . getline(v:lnum)
334
335 " This is a new blank line since we just typed a carriage return
336 " Check current line; search for simplistic matching start-of-block
337 let line = getline(v:lnum)
338
339 if line =~? '^\s*els'
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000340 " Any line when you type else will automatically back up one
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000341 " ident level (ie else, elseif, elsif)
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200342 let ind = ind - shiftwidth()
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000343 " echom 'curr - else - indent ' . ind
344 elseif line =~? '^\s*end\>'
345 let ind = s:GetStmtStarterIndent('end', v:lnum)
346 " General case for end
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200347 " let ind = ind - shiftwidth()
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000348 " echom 'curr - end - indent ' . ind
349 elseif line =~? '^\s*when\>'
350 let ind = s:GetStmtStarterIndent('when', v:lnum)
351 " If the WHEN clause is used with a MERGE or EXCEPTION
352 " clause, do not change the indent level, since these
353 " statements do not have a corresponding END statement.
354 " if stmt_starter =~? 'case'
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200355 " let ind = ind - shiftwidth()
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000356 " endif
357 " elseif line =~ '^\s*)\s*;\?\s*$'
358 " elseif line =~ '^\s*)'
359 elseif line =~ '^\s*)'
360 let num_unmatched_right = s:CountUnbalancedParan( line, ')' )
361 let ignore = s:CheckToIgnoreRightParan( v:lnum, num_unmatched_right )
362 " If the line ends in a ), then reduce the indent
363 " This catches items like:
364 " CREATE TABLE T1(
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000365 " c1 int,
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000366 " c2 int
367 " );
368 " But we do not want to unindent a line like:
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000369 " IF ( c1 = 1
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000370 " AND c2 = 3 ) THEN
371 " let num_unmatched_right = s:CountUnbalancedParan( line, ')' )
372 " if num_unmatched_right > 0
373 " elseif strpart( line, strlen(line)-1, 1 ) =~ ')'
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200374 " let ind = ind - shiftwidth()
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000375 if line =~ '^\s*)'
376 " let ignore = ignore + 1
377 " echom 'curr - begins ) unbalanced ignore: ' . ignore
378 endif
379
380 if (num_unmatched_right - ignore) > 0
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200381 let ind = ind - ( shiftwidth() * (num_unmatched_right - ignore) )
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000382 endif
383 " endif
384 endif
385
386 " echom 'final - indent ' . ind
Bram Moolenaara7241f52008-06-24 20:39:31 +0000387 return s:ModuloIndent(ind)
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000388endfunction
389
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100390" Restore:
391let &cpo= s:keepcpo
Bram Moolenaar8e52a592012-05-18 21:49:28 +0200392unlet s:keepcpo
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100393" vim: ts=4 fdm=marker sw=4