codekingpro/portable-devtools
114k
1" Vim indent file2" Language: SQL3" Maintainer: David Fishburn <dfishburn dot vim at gmail dot com>4" Last Change: 2021 Oct 115" Version: 4.06" Download: http://vim.sourceforge.net/script.php?script_id=4957 8" Notes:9" Indenting keywords are based on Oracle and Sybase Adaptive Server10" Anywhere (ASA). Test indenting was done with ASA stored procedures and11" functions and Oracle packages which contain stored procedures and12" functions.13" This has not been tested against Microsoft SQL Server or14" Sybase Adaptive Server Enterprise (ASE) which use the Transact-SQL15" syntax. That syntax does not have end tags for IF's, which makes16" indenting more difficult.17"18" Known Issues:19" The Oracle MERGE statement does not have an end tag associated with20" 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_indent25"26" 3.0 (Dec 2012)27" Added cpo check28"29" 2.030" Added the FOR keyword to SQLBlockStart to handle (Alec Tica):31" for i in 1..100 loop32" |<-- I expect to have indentation here33" end loop;34"35 36" Only load this indent file when no other was loaded.37if exists("b:did_indent")38 finish39endif40let b:did_indent = 141let b:current_indent = "sqlanywhere"42 43setlocal indentkeys-=0{44setlocal indentkeys-=0}45setlocal indentkeys-=:46setlocal indentkeys-=0#47setlocal indentkeys-=e48 49" This indicates formatting should take place when one of these50" expressions is used. These expressions would normally be something51" you would type at the BEGINNING of a line52" SQL is generally case insensitive, so this files assumes that53" These keywords are something that would trigger an indent LEFT, not54" an indent right, since the SQLBlockStart is used for those keywords55setlocal indentkeys+==~end,=~else,=~elseif,=~elsif,0=~when,0=)56 57" GetSQLIndent is executed whenever one of the expressions58" in the indentkeys is typed59setlocal indentexpr=GetSQLIndent()60 61let b:undo_indent = "setl indentexpr< indentkeys<"62 63" Only define the functions once.64if exists("*GetSQLIndent")65 finish66endif67 68let s:keepcpo= &cpo69set cpo&vim70 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 the74" ending block is (especially for procedures/functions).75let s:SQLBlockStart = '^\s*\%('.76 \ 'if\|else\|elseif\|elsif\|'.77 \ 'while\|loop\|do\|for\|'.78 \ 'begin\|'.79 \ 'case\|when\|merge\|exception'.80 \ '\)\>'81let s:SQLBlockEnd = '^\s*\(end\)\>'82 83" The indent level is also based on unmatched parentheses84" If a line has an extra "(" increase the indent85" If a line has an extra ")" decrease the indent86function! s:CountUnbalancedParen( line, paren_to_check )87 let l = a:line88 let lp = substitute(l, '[^(]', '', 'g')89 let l = a:line90 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 else101 " echom 'CountUnbalancedParen unknown paren to check: ' .102 " \ a:paren_to_check103 return 0104 endif105endfunction106 107" Unindent commands based on previous indent level108function! s:CheckToIgnoreRightParen( prev_lnum, num_levels )109 let lnum = a:prev_lnum110 let line = getline(lnum)111 let ends = 0112 let num_right_paren = a:num_levels113 let ignore_paren = 0114 let vircol = 1115 116 while num_right_paren > 0117 silent! exec 'norm! '.lnum."G\<bar>".vircol."\<bar>"118 let right_paren = search( ')', 'W' )119 if right_paren != lnum120 " This should not happen since there should be at least121 " num_right_paren matches for this line122 break123 endif124 let vircol = virtcol(".")125 126 " if getline(".") =~ '^)'127 let matching_paren = searchpair('(', '', ')', 'bW',128 \ 's:IsColComment(line("."), col("."))')129 130 if matching_paren < 1131 " No match found132 " echom 'CTIRP - no match found, ignoring'133 break134 endif135 136 if matching_paren == lnum137 " This was not an unmatched parentheses, start the search again138 " again after this column139 " echom 'CTIRP - same line match, ignoring'140 continue141 endif142 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 + 1148 endif149 150 " One match found, decrease and check for further matches151 let num_right_paren = num_right_paren - 1152 153 endwhile154 155 " Fallback - just move back one156 " return a:prev_indent - shiftwidth()157 return ignore_paren158endfunction159 160" Based on the keyword provided, loop through previous non empty161" non comment lines to find the statement that initiated the keyword.162" Return its indent level163" CASE ..164" WHEN ...165" Should return indent level of CASE166" EXCEPTION ..167" WHEN ...168" something;169" WHEN ...170" Should return indent level of exception.171function! s:GetStmtStarterIndent( keyword, curr_lnum )172 let lnum = a:curr_lnum173 174 " Default - reduce indent by 1175 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_lnum190 let ind = indent(matching_lnum)191 endif192 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_lnum202 let ind = indent(matching_lnum)203 else204 let ind = indent(a:curr_lnum)205 endif206 endif207 208 return ind209endfunction210 211 212" Check if the line is a comment213function! 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 rc221endfunction222 223 224" Check if the column is a comment225function! s:IsColComment(lnum, cnum)226 let rc = synIDattr(synID(a:lnum, a:cnum, 0), "name")227 \ =~? "comment"228 229 return rc230endfunction231 232 233" Instead of returning a column position, return234" an appropriate value as a factor of shiftwidth.235function! s:ModuloIndent(ind)236 let ind = a:ind237 238 if ind > 0239 let modulo = ind % shiftwidth()240 241 if modulo > 0242 let ind = ind - modulo243 endif244 endif245 246 return ind247endfunction248 249 250" Find correct indent of a new line based upon the previous line251function! GetSQLIndent()252 let lnum = v:lnum253 let ind = indent(lnum)254 255 " If the current line is a comment, leave the indent as is256 " Comment out this additional check since it affects the257 " indenting of =, and will not reindent comments as it should258 " if s:IsLineComment(lnum) == 1259 " return ind260 " endif261 262 " Get previous non-blank line263 let prevlnum = prevnonblank(lnum - 1)264 if prevlnum <= 0265 return ind266 endif267 268 if s:IsLineComment(prevlnum) == 1269 if getline(v:lnum) =~ '^\s*\*'270 let ind = s:ModuloIndent(indent(prevlnum))271 return ind + 1272 endif273 " If the previous line is a comment, then return -1274 " to tell Vim to use the formatoptions setting to determine275 " the indent to use276 " But only if the next line is blank. This would be true if277 " the user is typing, but it would not be true if the user278 " is reindenting the file279 if getline(v:lnum) =~ '^\s*$'280 return -1281 endif282 endif283 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 line287 " which is new and empty288 " Based on this line, we can determine how much to indent the new289 " line290 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 indent296 " should be changed, for example IF, BEGIN, should increase the indent297 " where END IF, END, should decrease the indent.298 if prevline =~? s:SQLBlockStart299 " Move indent in300 let ind = ind + shiftwidth()301 " echom 'prevl - SQLBlockStart - indent ' . ind . ' line: ' . prevline302 elseif prevline =~ '[()]'303 if prevline =~ '('304 let num_unmatched_left = s:CountUnbalancedParen( prevline, '(' )305 else306 let num_unmatched_left = 0307 endif308 if prevline =~ ')'309 let num_unmatched_right = s:CountUnbalancedParen( prevline, ')' )310 else311 let num_unmatched_right = 0312 " let num_unmatched_right = s:CountUnbalancedParen( prevline, ')' )313 endif314 if num_unmatched_left > 0315 " There is a open left parenthesis316 " increase indent317 let ind = ind + ( shiftwidth() * num_unmatched_left )318 elseif num_unmatched_right > 0319 " if it is an unbalanced parenthesis only unindent if320 " it was part of a command (ie create table(..) )321 " instead of part of an if (ie if (....) then) which should322 " maintain the indent level323 let ignore = s:CheckToIgnoreRightParen( prevlnum, num_unmatched_right )324 " echom 'prevl - ) unbalanced - CTIRP - ignore: ' . ignore325 326 if prevline =~ '^\s*)'327 let ignore = ignore + 1328 " echom 'prevl - begins ) unbalanced ignore: ' . ignore329 endif330 331 if (num_unmatched_right - ignore) > 0332 let ind = ind - ( shiftwidth() * (num_unmatched_right - ignore) )333 endif334 335 endif336 endif337 338 339 " echom 'CURRENT INDENT: ' . ind . ' LINE: ' . getline(v:lnum)340 341 " This is a new blank line since we just typed a carriage return342 " Check current line; search for simplistic matching start-of-block343 let line = getline(v:lnum)344 345 if line =~? '^\s*els'346 " Any line when you type else will automatically back up one347 " ident level (ie else, elseif, elsif)348 let ind = ind - shiftwidth()349 " echom 'curr - else - indent ' . ind350 elseif line =~? '^\s*end\>'351 let ind = s:GetStmtStarterIndent('end', v:lnum)352 " General case for end353 " let ind = ind - shiftwidth()354 " echom 'curr - end - indent ' . ind355 elseif line =~? '^\s*when\>'356 let ind = s:GetStmtStarterIndent('when', v:lnum)357 " If the WHEN clause is used with a MERGE or EXCEPTION358 " clause, do not change the indent level, since these359 " statements do not have a corresponding END statement.360 " if stmt_starter =~? 'case'361 " let ind = ind - shiftwidth()362 " endif363 " 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 indent369 " This catches items like:370 " CREATE TABLE T1(371 " c1 int,372 " c2 int373 " );374 " But we do not want to unindent a line like:375 " IF ( c1 = 1376 " AND c2 = 3 ) THEN377 " let num_unmatched_right = s:CountUnbalancedParen( line, ')' )378 " if num_unmatched_right > 0379 " elseif strpart( line, strlen(line)-1, 1 ) =~ ')'380 " let ind = ind - shiftwidth()381 if line =~ '^\s*)'382 " let ignore = ignore + 1383 " echom 'curr - begins ) unbalanced ignore: ' . ignore384 endif385 386 if (num_unmatched_right - ignore) > 0387 let ind = ind - ( shiftwidth() * (num_unmatched_right - ignore) )388 endif389 " endif390 endif391 392 " echom 'final - indent ' . ind393 return s:ModuloIndent(ind)394endfunction395 396" Restore:397let &cpo= s:keepcpo398unlet s:keepcpo399" vim: ts=4 fdm=marker sw=4400 