blob: d39fa3240e8fea0cb9fba10d4cca693efe822088 [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 Moolenaaracc22402020-06-07 21:07:18 +02004" Last Change By Maintainer: 2017 Jun 13
5" Last Change: by Stephen Wall, #5578, 2020 Jun 07
Bram Moolenaar34feacb2012-12-05 19:01:43 +01006" Version: 3.0
Bram Moolenaar0fd92892006-03-09 22:27:48 +00007" Download: http://vim.sourceforge.net/script.php?script_id=495
8
9" Notes:
10" Indenting keywords are based on Oracle and Sybase Adaptive Server
11" Anywhere (ASA). Test indenting was done with ASA stored procedures and
Bram Moolenaar6c391a72021-09-09 21:55:11 +020012" functions and Oracle packages which contain stored procedures and
Bram Moolenaar0fd92892006-03-09 22:27:48 +000013" functions.
Bram Moolenaarc9b4b052006-04-30 18:54:39 +000014" This has not been tested against Microsoft SQL Server or
Bram Moolenaar0fd92892006-03-09 22:27:48 +000015" Sybase Adaptive Server Enterprise (ASE) which use the Transact-SQL
16" syntax. That syntax does not have end tags for IF's, which makes
17" indenting more difficult.
18"
19" Known Issues:
20" The Oracle MERGE statement does not have an end tag associated with
21" it, this can leave the indent hanging to the right one too many.
Bram Moolenaar34feacb2012-12-05 19:01:43 +010022"
23" History:
24" 3.0 (Dec 2012)
25" Added cpo check
26"
27" 2.0
28" Added the FOR keyword to SQLBlockStart to handle (Alec Tica):
29" for i in 1..100 loop
30" |<-- I expect to have indentation here
31" end loop;
32"
Bram Moolenaar0fd92892006-03-09 22:27:48 +000033
34" Only load this indent file when no other was loaded.
35if exists("b:did_indent")
36 finish
37endif
38let b:did_indent = 1
39let b:current_indent = "sqlanywhere"
40
41setlocal indentkeys-=0{
42setlocal indentkeys-=0}
43setlocal indentkeys-=:
44setlocal indentkeys-=0#
45setlocal indentkeys-=e
46
Bram Moolenaarc9b4b052006-04-30 18:54:39 +000047" This indicates formatting should take place when one of these
Bram Moolenaar0fd92892006-03-09 22:27:48 +000048" expressions is used. These expressions would normally be something
49" you would type at the BEGINNING of a line
50" SQL is generally case insensitive, so this files assumes that
51" These keywords are something that would trigger an indent LEFT, not
52" an indent right, since the SQLBlockStart is used for those keywords
53setlocal indentkeys+==~end,=~else,=~elseif,=~elsif,0=~when,0=)
54
Bram Moolenaarc9b4b052006-04-30 18:54:39 +000055" GetSQLIndent is executed whenever one of the expressions
Bram Moolenaar0fd92892006-03-09 22:27:48 +000056" in the indentkeys is typed
57setlocal indentexpr=GetSQLIndent()
58
Bram Moolenaara3e6bc92013-01-30 14:18:00 +010059" Only define the functions once.
60if exists("*GetSQLIndent")
61 finish
62endif
63let s:keepcpo= &cpo
64set cpo&vim
65
Bram Moolenaar0fd92892006-03-09 22:27:48 +000066" List of all the statements that start a new block.
67" These are typically words that start a line.
Bram Moolenaarc9b4b052006-04-30 18:54:39 +000068" IS is excluded, since it is difficult to determine when the
Bram Moolenaar0fd92892006-03-09 22:27:48 +000069" ending block is (especially for procedures/functions).
70let s:SQLBlockStart = '^\s*\%('.
Bram Moolenaaracc22402020-06-07 21:07:18 +020071 \ 'if\>.*\<then\|'.
72 \ 'then\|else\>\|'.
73 \ 'elseif\>.*\<then\|'.
74 \ 'elsif\>.(\<then\|'.
75 \ 'while\>.*\<loop\|'.
76 \ 'for\>.*\<loop\|'.
77 \ 'foreach\>.*\<loop\|'.
78 \ 'loop\|do\|declare\|begin\|'.
Bram Moolenaar0fd92892006-03-09 22:27:48 +000079 \ 'case\|when\|merge\|exception'.
80 \ '\)\>'
81let s:SQLBlockEnd = '^\s*\(end\)\>'
82
Bram Moolenaaracc22402020-06-07 21:07:18 +020083" The indent level is also based on unmatched parentheses
Bram Moolenaar0fd92892006-03-09 22:27:48 +000084" If a line has an extra "(" increase the indent
85" If a line has an extra ")" decrease the indent
Bram Moolenaaracc22402020-06-07 21:07:18 +020086function! s:CountUnbalancedParen( line, paren_to_check )
Bram Moolenaar0fd92892006-03-09 22:27:48 +000087 let l = a:line
88 let lp = substitute(l, '[^(]', '', 'g')
89 let l = a:line
90 let rp = substitute(l, '[^)]', '', 'g')
91
Bram Moolenaaracc22402020-06-07 21:07:18 +020092 if a:paren_to_check =~ ')'
93 " echom 'CountUnbalancedParen ) returning: ' .
Bram Moolenaar0fd92892006-03-09 22:27:48 +000094 " \ (strlen(rp) - strlen(lp))
95 return (strlen(rp) - strlen(lp))
Bram Moolenaaracc22402020-06-07 21:07:18 +020096 elseif a:paren_to_check =~ '('
97 " echom 'CountUnbalancedParen ( returning: ' .
Bram Moolenaar0fd92892006-03-09 22:27:48 +000098 " \ (strlen(lp) - strlen(rp))
99 return (strlen(lp) - strlen(rp))
100 else
Bram Moolenaaracc22402020-06-07 21:07:18 +0200101 " echom 'CountUnbalancedParen unknown paren to check: ' .
102 " \ a:paren_to_check
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000103 return 0
104 endif
105endfunction
106
107" Unindent commands based on previous indent level
Bram Moolenaaracc22402020-06-07 21:07:18 +0200108function! s:CheckToIgnoreRightParen( prev_lnum, num_levels )
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000109 let lnum = a:prev_lnum
110 let line = getline(lnum)
111 let ends = 0
Bram Moolenaaracc22402020-06-07 21:07:18 +0200112 let num_right_paren = a:num_levels
113 let ignore_paren = 0
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000114 let vircol = 1
115
Bram Moolenaaracc22402020-06-07 21:07:18 +0200116 while num_right_paren > 0
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000117 silent! exec 'norm! '.lnum."G\<bar>".vircol."\<bar>"
Bram Moolenaaracc22402020-06-07 21:07:18 +0200118 let right_paren = search( ')', 'W' )
119 if right_paren != lnum
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000120 " This should not happen since there should be at least
Bram Moolenaaracc22402020-06-07 21:07:18 +0200121 " num_right_paren matches for this line
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000122 break
123 endif
124 let vircol = virtcol(".")
125
126 " if getline(".") =~ '^)'
Bram Moolenaaracc22402020-06-07 21:07:18 +0200127 let matching_paren = searchpair('(', '', ')', 'bW',
Bram Moolenaara7241f52008-06-24 20:39:31 +0000128 \ 's:IsColComment(line("."), col("."))')
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000129
Bram Moolenaaracc22402020-06-07 21:07:18 +0200130 if matching_paren < 1
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000131 " No match found
132 " echom 'CTIRP - no match found, ignoring'
133 break
134 endif
135
Bram Moolenaaracc22402020-06-07 21:07:18 +0200136 if matching_paren == lnum
137 " This was not an unmatched parentheses, start the search again
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000138 " again after this column
139 " echom 'CTIRP - same line match, ignoring'
140 continue
141 endif
142
143 " echom 'CTIRP - match: ' . line(".") . ' ' . getline(".")
144
Bram Moolenaaracc22402020-06-07 21:07:18 +0200145 if getline(matching_paren) =~? '\(if\|while\)\>'
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000146 " echom 'CTIRP - if/while ignored: ' . line(".") . ' ' . getline(".")
Bram Moolenaaracc22402020-06-07 21:07:18 +0200147 let ignore_paren = ignore_paren + 1
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000148 endif
149
150 " One match found, decrease and check for further matches
Bram Moolenaaracc22402020-06-07 21:07:18 +0200151 let num_right_paren = num_right_paren - 1
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000152
153 endwhile
154
155 " Fallback - just move back one
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200156 " return a:prev_indent - shiftwidth()
Bram Moolenaaracc22402020-06-07 21:07:18 +0200157 return ignore_paren
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000158endfunction
159
160" Based on the keyword provided, loop through previous non empty
Bram Moolenaaracc22402020-06-07 21:07:18 +0200161" non comment lines to find the statement that initiated the keyword.
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000162" Return its indent level
163" CASE ..
164" WHEN ...
165" Should return indent level of CASE
166" EXCEPTION ..
167" WHEN ...
168" something;
169" WHEN ...
170" Should return indent level of exception.
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100171function! s:GetStmtStarterIndent( keyword, curr_lnum )
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000172 let lnum = a:curr_lnum
173
174 " Default - reduce indent by 1
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200175 let ind = indent(a:curr_lnum) - shiftwidth()
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000176
177 if a:keyword =~? 'end'
178 exec 'normal! ^'
179 let stmts = '^\s*\%('.
180 \ '\<begin\>\|' .
181 \ '\%(\%(\<end\s\+\)\@<!\<loop\>\)\|' .
182 \ '\%(\%(\<end\s\+\)\@<!\<case\>\)\|' .
183 \ '\%(\%(\<end\s\+\)\@<!\<for\>\)\|' .
184 \ '\%(\%(\<end\s\+\)\@<!\<if\>\)'.
185 \ '\)'
186 let matching_lnum = searchpair(stmts, '', '\<end\>\zs', 'bW',
Bram Moolenaara7241f52008-06-24 20:39:31 +0000187 \ 's:IsColComment(line("."), col(".")) == 1')
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000188 exec 'normal! $'
189 if matching_lnum > 0 && matching_lnum < a:curr_lnum
190 let ind = indent(matching_lnum)
191 endif
192 elseif a:keyword =~? 'when'
193 exec 'normal! ^'
194 let matching_lnum = searchpair(
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000195 \ '\%(\<end\s\+\)\@<!\<case\>\|\<exception\>\|\<merge\>',
196 \ '',
197 \ '\%(\%(\<when\s\+others\>\)\|\%(\<end\s\+case\>\)\)',
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000198 \ 'bW',
Bram Moolenaara7241f52008-06-24 20:39:31 +0000199 \ 's:IsColComment(line("."), col(".")) == 1')
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000200 exec 'normal! $'
201 if matching_lnum > 0 && matching_lnum < a:curr_lnum
202 let ind = indent(matching_lnum)
203 else
204 let ind = indent(a:curr_lnum)
205 endif
206 endif
207
208 return ind
209endfunction
210
211
212" Check if the line is a comment
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100213function! s:IsLineComment(lnum)
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000214 let rc = synIDattr(
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000215 \ synID(a:lnum,
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000216 \ match(getline(a:lnum), '\S')+1, 0)
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000217 \ , "name")
218 \ =~? "comment"
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000219
220 return rc
221endfunction
222
223
224" Check if the column is a comment
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100225function! s:IsColComment(lnum, cnum)
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000226 let rc = synIDattr(synID(a:lnum, a:cnum, 0), "name")
227 \ =~? "comment"
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000228
229 return rc
230endfunction
231
232
Bram Moolenaara7241f52008-06-24 20:39:31 +0000233" Instead of returning a column position, return
234" an appropriate value as a factor of shiftwidth.
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100235function! s:ModuloIndent(ind)
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000236 let ind = a:ind
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000237
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000238 if ind > 0
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200239 let modulo = ind % shiftwidth()
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000240
241 if modulo > 0
242 let ind = ind - modulo
243 endif
244 endif
245
246 return ind
247endfunction
248
249
250" Find correct indent of a new line based upon the previous line
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100251function! GetSQLIndent()
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000252 let lnum = v:lnum
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000253 let ind = indent(lnum)
254
255 " If the current line is a comment, leave the indent as is
256 " Comment out this additional check since it affects the
257 " indenting of =, and will not reindent comments as it should
Bram Moolenaara7241f52008-06-24 20:39:31 +0000258 " if s:IsLineComment(lnum) == 1
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000259 " return ind
260 " endif
261
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100262 " Get previous non-blank line
263 let prevlnum = prevnonblank(lnum - 1)
264 if prevlnum <= 0
265 return ind
266 endif
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000267
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100268 if s:IsLineComment(prevlnum) == 1
269 if getline(v:lnum) =~ '^\s*\*'
270 let ind = s:ModuloIndent(indent(prevlnum))
271 return ind + 1
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000272 endif
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100273 " If the previous line is a comment, then return -1
274 " to tell Vim to use the formatoptions setting to determine
275 " the indent to use
276 " But only if the next line is blank. This would be true if
277 " the user is typing, but it would not be true if the user
278 " is reindenting the file
279 if getline(v:lnum) =~ '^\s*$'
280 return -1
281 endif
282 endif
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000283
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000284 " echom 'PREVIOUS INDENT: ' . indent(prevlnum) . ' LINE: ' . getline(prevlnum)
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000285
286 " This is the line you just hit return on, it is not the current line
287 " which is new and empty
288 " Based on this line, we can determine how much to indent the new
289 " line
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000290
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000291 " Get default indent (from prev. line)
292 let ind = indent(prevlnum)
293 let prevline = getline(prevlnum)
294
295 " Now check what's on the previous line to determine if the indent
296 " should be changed, for example IF, BEGIN, should increase the indent
297 " where END IF, END, should decrease the indent.
298 if prevline =~? s:SQLBlockStart
299 " Move indent in
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200300 let ind = ind + shiftwidth()
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000301 " echom 'prevl - SQLBlockStart - indent ' . ind . ' line: ' . prevline
302 elseif prevline =~ '[()]'
303 if prevline =~ '('
Bram Moolenaaracc22402020-06-07 21:07:18 +0200304 let num_unmatched_left = s:CountUnbalancedParen( prevline, '(' )
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000305 else
306 let num_unmatched_left = 0
307 endif
308 if prevline =~ ')'
Bram Moolenaaracc22402020-06-07 21:07:18 +0200309 let num_unmatched_right = s:CountUnbalancedParen( prevline, ')' )
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000310 else
311 let num_unmatched_right = 0
Bram Moolenaaracc22402020-06-07 21:07:18 +0200312 " let num_unmatched_right = s:CountUnbalancedParen( prevline, ')' )
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000313 endif
314 if num_unmatched_left > 0
Bram Moolenaaracc22402020-06-07 21:07:18 +0200315 " There is a open left parenthesis
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000316 " increase indent
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200317 let ind = ind + ( shiftwidth() * num_unmatched_left )
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000318 elseif num_unmatched_right > 0
Bram Moolenaaracc22402020-06-07 21:07:18 +0200319 " if it is an unbalanced parenthesis only unindent if
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000320 " it was part of a command (ie create table(..) )
321 " instead of part of an if (ie if (....) then) which should
322 " maintain the indent level
Bram Moolenaaracc22402020-06-07 21:07:18 +0200323 let ignore = s:CheckToIgnoreRightParen( prevlnum, num_unmatched_right )
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000324 " echom 'prevl - ) unbalanced - CTIRP - ignore: ' . ignore
325
326 if prevline =~ '^\s*)'
327 let ignore = ignore + 1
328 " echom 'prevl - begins ) unbalanced ignore: ' . ignore
329 endif
330
331 if (num_unmatched_right - ignore) > 0
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200332 let ind = ind - ( shiftwidth() * (num_unmatched_right - ignore) )
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000333 endif
334
335 endif
336 endif
337
338
339 " echom 'CURRENT INDENT: ' . ind . ' LINE: ' . getline(v:lnum)
340
341 " This is a new blank line since we just typed a carriage return
342 " Check current line; search for simplistic matching start-of-block
343 let line = getline(v:lnum)
344
345 if line =~? '^\s*els'
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000346 " Any line when you type else will automatically back up one
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000347 " ident level (ie else, elseif, elsif)
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200348 let ind = ind - shiftwidth()
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000349 " echom 'curr - else - indent ' . ind
350 elseif line =~? '^\s*end\>'
351 let ind = s:GetStmtStarterIndent('end', v:lnum)
352 " General case for end
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200353 " let ind = ind - shiftwidth()
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000354 " echom 'curr - end - indent ' . ind
355 elseif line =~? '^\s*when\>'
356 let ind = s:GetStmtStarterIndent('when', v:lnum)
357 " If the WHEN clause is used with a MERGE or EXCEPTION
358 " clause, do not change the indent level, since these
359 " statements do not have a corresponding END statement.
360 " if stmt_starter =~? 'case'
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200361 " let ind = ind - shiftwidth()
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000362 " endif
363 " elseif line =~ '^\s*)\s*;\?\s*$'
364 " elseif line =~ '^\s*)'
365 elseif line =~ '^\s*)'
Bram Moolenaaracc22402020-06-07 21:07:18 +0200366 let num_unmatched_right = s:CountUnbalancedParen( line, ')' )
367 let ignore = s:CheckToIgnoreRightParen( v:lnum, num_unmatched_right )
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000368 " If the line ends in a ), then reduce the indent
369 " This catches items like:
370 " CREATE TABLE T1(
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000371 " c1 int,
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000372 " c2 int
373 " );
374 " But we do not want to unindent a line like:
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000375 " IF ( c1 = 1
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000376 " AND c2 = 3 ) THEN
Bram Moolenaaracc22402020-06-07 21:07:18 +0200377 " let num_unmatched_right = s:CountUnbalancedParen( line, ')' )
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000378 " if num_unmatched_right > 0
379 " elseif strpart( line, strlen(line)-1, 1 ) =~ ')'
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200380 " let ind = ind - shiftwidth()
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000381 if line =~ '^\s*)'
382 " let ignore = ignore + 1
383 " echom 'curr - begins ) unbalanced ignore: ' . ignore
384 endif
385
386 if (num_unmatched_right - ignore) > 0
Bram Moolenaar3ec574f2017-06-13 18:12:01 +0200387 let ind = ind - ( shiftwidth() * (num_unmatched_right - ignore) )
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000388 endif
389 " endif
390 endif
391
392 " echom 'final - indent ' . ind
Bram Moolenaara7241f52008-06-24 20:39:31 +0000393 return s:ModuloIndent(ind)
Bram Moolenaar0fd92892006-03-09 22:27:48 +0000394endfunction
395
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100396" Restore:
397let &cpo= s:keepcpo
Bram Moolenaar8e52a592012-05-18 21:49:28 +0200398unlet s:keepcpo
Bram Moolenaar34feacb2012-12-05 19:01:43 +0100399" vim: ts=4 fdm=marker sw=4