sqlanywhere.vim (13269B)
1 " Vim indent file 2 " Language: SQL 3 " Maintainer: David Fishburn <dfishburn dot vim at gmail dot com> 4 " Last Change: 2021 Oct 11 5 " Version: 4.0 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 " functions and Oracle packages which contain stored procedures and 12 " functions. 13 " This has not been tested against Microsoft SQL Server or 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 " History: 23 " 4.0 (Oct 2021) 24 " Added b:undo_indent 25 " 26 " 3.0 (Dec 2012) 27 " Added cpo check 28 " 29 " 2.0 30 " Added the FOR keyword to SQLBlockStart to handle (Alec Tica): 31 " for i in 1..100 loop 32 " |<-- I expect to have indentation here 33 " end loop; 34 " 35 36 " Only load this indent file when no other was loaded. 37 if exists("b:did_indent") 38 finish 39 endif 40 let b:did_indent = 1 41 let b:current_indent = "sqlanywhere" 42 43 setlocal indentkeys-=0{ 44 setlocal indentkeys-=0} 45 setlocal indentkeys-=: 46 setlocal indentkeys-=0# 47 setlocal indentkeys-=e 48 49 " This indicates formatting should take place when one of these 50 " expressions is used. These expressions would normally be something 51 " you would type at the BEGINNING of a line 52 " SQL is generally case insensitive, so this files assumes that 53 " These keywords are something that would trigger an indent LEFT, not 54 " an indent right, since the SQLBlockStart is used for those keywords 55 setlocal indentkeys+==~end,=~else,=~elseif,=~elsif,0=~when,0=) 56 57 " GetSQLIndent is executed whenever one of the expressions 58 " in the indentkeys is typed 59 setlocal indentexpr=GetSQLIndent() 60 61 let b:undo_indent = "setl indentexpr< indentkeys<" 62 63 " Only define the functions once. 64 if exists("*GetSQLIndent") 65 finish 66 endif 67 68 let s:keepcpo= &cpo 69 set cpo&vim 70 71 " List of all the statements that start a new block. 72 " These are typically words that start a line. 73 " IS is excluded, since it is difficult to determine when the 74 " ending block is (especially for procedures/functions). 75 let s:SQLBlockStart = '^\s*\%('. 76 \ 'if\|else\|elseif\|elsif\|'. 77 \ 'while\|loop\|do\|for\|'. 78 \ 'begin\|'. 79 \ 'case\|when\|merge\|exception'. 80 \ '\)\>' 81 let s:SQLBlockEnd = '^\s*\(end\)\>' 82 83 " The indent level is also based on unmatched parentheses 84 " If a line has an extra "(" increase the indent 85 " If a line has an extra ")" decrease the indent 86 function! s:CountUnbalancedParen( line, paren_to_check ) 87 let l = a:line 88 let lp = substitute(l, '[^(]', '', 'g') 89 let l = a:line 90 let rp = substitute(l, '[^)]', '', 'g') 91 92 if a:paren_to_check =~ ')' 93 " echom 'CountUnbalancedParen ) returning: ' . 94 " \ (strlen(rp) - strlen(lp)) 95 return (strlen(rp) - strlen(lp)) 96 elseif a:paren_to_check =~ '(' 97 " echom 'CountUnbalancedParen ( returning: ' . 98 " \ (strlen(lp) - strlen(rp)) 99 return (strlen(lp) - strlen(rp)) 100 else 101 " echom 'CountUnbalancedParen unknown paren to check: ' . 102 " \ a:paren_to_check 103 return 0 104 endif 105 endfunction 106 107 " Unindent commands based on previous indent level 108 function! s:CheckToIgnoreRightParen( prev_lnum, num_levels ) 109 let lnum = a:prev_lnum 110 let line = getline(lnum) 111 let ends = 0 112 let num_right_paren = a:num_levels 113 let ignore_paren = 0 114 let vircol = 1 115 116 while num_right_paren > 0 117 silent! exec 'norm! '.lnum."G\<bar>".vircol."\<bar>" 118 let right_paren = search( ')', 'W' ) 119 if right_paren != lnum 120 " This should not happen since there should be at least 121 " num_right_paren matches for this line 122 break 123 endif 124 let vircol = virtcol(".") 125 126 " if getline(".") =~ '^)' 127 let matching_paren = searchpair('(', '', ')', 'bW', 128 \ 's:IsColComment(line("."), col("."))') 129 130 if matching_paren < 1 131 " No match found 132 " echom 'CTIRP - no match found, ignoring' 133 break 134 endif 135 136 if matching_paren == lnum 137 " This was not an unmatched parentheses, start the search again 138 " again after this column 139 " echom 'CTIRP - same line match, ignoring' 140 continue 141 endif 142 143 " echom 'CTIRP - match: ' . line(".") . ' ' . getline(".") 144 145 if getline(matching_paren) =~? '\(if\|while\)\>' 146 " echom 'CTIRP - if/while ignored: ' . line(".") . ' ' . getline(".") 147 let ignore_paren = ignore_paren + 1 148 endif 149 150 " One match found, decrease and check for further matches 151 let num_right_paren = num_right_paren - 1 152 153 endwhile 154 155 " Fallback - just move back one 156 " return a:prev_indent - shiftwidth() 157 return ignore_paren 158 endfunction 159 160 " Based on the keyword provided, loop through previous non empty 161 " non comment lines to find the statement that initiated the keyword. 162 " 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. 171 function! s:GetStmtStarterIndent( keyword, curr_lnum ) 172 let lnum = a:curr_lnum 173 174 " Default - reduce indent by 1 175 let ind = indent(a:curr_lnum) - shiftwidth() 176 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', 187 \ 's:IsColComment(line("."), col(".")) == 1') 188 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( 195 \ '\%(\<end\s\+\)\@<!\<case\>\|\<exception\>\|\<merge\>', 196 \ '', 197 \ '\%(\%(\<when\s\+others\>\)\|\%(\<end\s\+case\>\)\)', 198 \ 'bW', 199 \ 's:IsColComment(line("."), col(".")) == 1') 200 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 209 endfunction 210 211 212 " Check if the line is a comment 213 function! s:IsLineComment(lnum) 214 let rc = synIDattr( 215 \ synID(a:lnum, 216 \ match(getline(a:lnum), '\S')+1, 0) 217 \ , "name") 218 \ =~? "comment" 219 220 return rc 221 endfunction 222 223 224 " Check if the column is a comment 225 function! s:IsColComment(lnum, cnum) 226 let rc = synIDattr(synID(a:lnum, a:cnum, 0), "name") 227 \ =~? "comment" 228 229 return rc 230 endfunction 231 232 233 " Instead of returning a column position, return 234 " an appropriate value as a factor of shiftwidth. 235 function! s:ModuloIndent(ind) 236 let ind = a:ind 237 238 if ind > 0 239 let modulo = ind % shiftwidth() 240 241 if modulo > 0 242 let ind = ind - modulo 243 endif 244 endif 245 246 return ind 247 endfunction 248 249 250 " Find correct indent of a new line based upon the previous line 251 function! GetSQLIndent() 252 let lnum = v:lnum 253 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 258 " if s:IsLineComment(lnum) == 1 259 " return ind 260 " endif 261 262 " Get previous non-blank line 263 let prevlnum = prevnonblank(lnum - 1) 264 if prevlnum <= 0 265 return ind 266 endif 267 268 if s:IsLineComment(prevlnum) == 1 269 if getline(v:lnum) =~ '^\s*\*' 270 let ind = s:ModuloIndent(indent(prevlnum)) 271 return ind + 1 272 endif 273 " 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 283 284 " echom 'PREVIOUS INDENT: ' . indent(prevlnum) . ' LINE: ' . getline(prevlnum) 285 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 290 291 " 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 300 let ind = ind + shiftwidth() 301 " echom 'prevl - SQLBlockStart - indent ' . ind . ' line: ' . prevline 302 elseif prevline =~ '[()]' 303 if prevline =~ '(' 304 let num_unmatched_left = s:CountUnbalancedParen( prevline, '(' ) 305 else 306 let num_unmatched_left = 0 307 endif 308 if prevline =~ ')' 309 let num_unmatched_right = s:CountUnbalancedParen( prevline, ')' ) 310 else 311 let num_unmatched_right = 0 312 " let num_unmatched_right = s:CountUnbalancedParen( prevline, ')' ) 313 endif 314 if num_unmatched_left > 0 315 " There is a open left parenthesis 316 " increase indent 317 let ind = ind + ( shiftwidth() * num_unmatched_left ) 318 elseif num_unmatched_right > 0 319 " if it is an unbalanced parenthesis only unindent if 320 " 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 323 let ignore = s:CheckToIgnoreRightParen( prevlnum, num_unmatched_right ) 324 " 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 332 let ind = ind - ( shiftwidth() * (num_unmatched_right - ignore) ) 333 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' 346 " Any line when you type else will automatically back up one 347 " ident level (ie else, elseif, elsif) 348 let ind = ind - shiftwidth() 349 " echom 'curr - else - indent ' . ind 350 elseif line =~? '^\s*end\>' 351 let ind = s:GetStmtStarterIndent('end', v:lnum) 352 " General case for end 353 " let ind = ind - shiftwidth() 354 " 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' 361 " let ind = ind - shiftwidth() 362 " endif 363 " elseif line =~ '^\s*)\s*;\?\s*$' 364 " elseif line =~ '^\s*)' 365 elseif line =~ '^\s*)' 366 let num_unmatched_right = s:CountUnbalancedParen( line, ')' ) 367 let ignore = s:CheckToIgnoreRightParen( v:lnum, num_unmatched_right ) 368 " If the line ends in a ), then reduce the indent 369 " This catches items like: 370 " CREATE TABLE T1( 371 " c1 int, 372 " c2 int 373 " ); 374 " But we do not want to unindent a line like: 375 " IF ( c1 = 1 376 " AND c2 = 3 ) THEN 377 " let num_unmatched_right = s:CountUnbalancedParen( line, ')' ) 378 " if num_unmatched_right > 0 379 " elseif strpart( line, strlen(line)-1, 1 ) =~ ')' 380 " let ind = ind - shiftwidth() 381 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 387 let ind = ind - ( shiftwidth() * (num_unmatched_right - ignore) ) 388 endif 389 " endif 390 endif 391 392 " echom 'final - indent ' . ind 393 return s:ModuloIndent(ind) 394 endfunction 395 396 " Restore: 397 let &cpo= s:keepcpo 398 unlet s:keepcpo 399 " vim: ts=4 fdm=marker sw=4