384 lines · 12.6 KB · Text
RawDownload
" Vim indent file" Language:    SQL" Maintainer:  David Fishburn <fishburn at ianywhere dot com>" Last Change: Wed Sep 14 2005 10:21:15 PM" Version:     1.4" Download:    http://vim.sourceforge.net/script.php?script_id=495 " Notes:"    Indenting keywords are based on Oracle and Sybase Adaptive Server"    Anywhere (ASA).  Test indenting was done with ASA stored procedures and"    fuctions and Oracle packages which contain stored procedures and"    functions."    This has not been tested against Microsoft SQL Server or"    Sybase Adaptive Server Enterprise (ASE) which use the Transact-SQL"    syntax.  That syntax does not have end tags for IF's, which makes"    indenting more difficult."" Known Issues:"    The Oracle MERGE statement does not have an end tag associated with"    it, this can leave the indent hanging to the right one too many. " Only load this indent file when no other was loaded.if exists("b:did_indent")    finishendiflet b:did_indent     = 1let b:current_indent = "sqlanywhere" setlocal indentkeys-=0{setlocal indentkeys-=0}setlocal indentkeys-=:setlocal indentkeys-=0#setlocal indentkeys-=e " This indicates formatting should take place when one of these" expressions is used.  These expressions would normally be something" you would type at the BEGINNING of a line" SQL is generally case insensitive, so this files assumes that" These keywords are something that would trigger an indent LEFT, not" an indent right, since the SQLBlockStart is used for those keywordssetlocal indentkeys+==~end,=~else,=~elseif,=~elsif,0=~when,0=) " GetSQLIndent is executed whenever one of the expressions" in the indentkeys is typedsetlocal indentexpr=GetSQLIndent() " Only define the functions once.if exists("*GetSQLIndent")    finishendif " List of all the statements that start a new block." These are typically words that start a line." IS is excluded, since it is difficult to determine when the" ending block is (especially for procedures/functions).let s:SQLBlockStart = '^\s*\%('.            \ 'if\|else\|elseif\|elsif\|'.                \ 'while\|loop\|do\|'.                \ 'begin\|'.                \ 'case\|when\|merge\|exception'.                \ '\)\>'let s:SQLBlockEnd = '^\s*\(end\)\>' " The indent level is also based on unmatched paranethesis" If a line has an extra "(" increase the indent" If a line has an extra ")" decrease the indentfunction s:CountUnbalancedParan( line, paran_to_check )    let l = a:line    let lp = substitute(l, '[^(]', '', 'g')    let l = a:line    let rp = substitute(l, '[^)]', '', 'g')     if a:paran_to_check =~ ')'        " echom 'CountUnbalancedParan ) returning: ' .        " \ (strlen(rp) - strlen(lp))        return (strlen(rp) - strlen(lp))    elseif a:paran_to_check =~ '('        " echom 'CountUnbalancedParan ( returning: ' .        " \ (strlen(lp) - strlen(rp))        return (strlen(lp) - strlen(rp))    else        " echom 'CountUnbalancedParan unknown paran to check: ' .        " \ a:paran_to_check        return 0    endifendfunction " Unindent commands based on previous indent levelfunction s:CheckToIgnoreRightParan( prev_lnum, num_levels )    let lnum = a:prev_lnum    let line = getline(lnum)    let ends = 0    let num_right_paran = a:num_levels    let ignore_paran = 0    let vircol = 1     while num_right_paran > 0        silent! exec 'norm! '.lnum."G\<bar>".vircol."\<bar>"        let right_paran = search( ')', 'W' )        if right_paran != lnum            " This should not happen since there should be at least            " num_right_paran matches for this line            break        endif        let vircol      = virtcol(".")         " if getline(".") =~ '^)'        let matching_paran = searchpair('(', '', ')', 'bW',                    \ 'IsColComment(line("."), col("."))')         if matching_paran < 1            " No match found            " echom 'CTIRP - no match found, ignoring'            break        endif         if matching_paran == lnum            " This was not an unmatched parantenses, start the search again            " again after this column            " echom 'CTIRP - same line match, ignoring'            continue        endif         " echom 'CTIRP - match: ' . line(".") . '  ' . getline(".")         if getline(matching_paran) =~? '\(if\|while\)\>'            " echom 'CTIRP - if/while ignored: ' . line(".") . '  ' . getline(".")            let ignore_paran = ignore_paran + 1        endif         " One match found, decrease and check for further matches        let num_right_paran = num_right_paran - 1     endwhile     " Fallback - just move back one    " return a:prev_indent - &sw    return ignore_paranendfunction " Based on the keyword provided, loop through previous non empty" non comment lines to find the statement that initated the keyword." Return its indent level"    CASE .."    WHEN ..." Should return indent level of CASE"    EXCEPTION .."    WHEN ..."         something;"    WHEN ..." Should return indent level of exception.function s:GetStmtStarterIndent( keyword, curr_lnum )    let lnum  = a:curr_lnum     " Default - reduce indent by 1    let ind = indent(a:curr_lnum) - &sw     if a:keyword =~? 'end'        exec 'normal! ^'        let stmts = '^\s*\%('.                    \ '\<begin\>\|' .                    \ '\%(\%(\<end\s\+\)\@<!\<loop\>\)\|' .                    \ '\%(\%(\<end\s\+\)\@<!\<case\>\)\|' .                    \ '\%(\%(\<end\s\+\)\@<!\<for\>\)\|' .                    \ '\%(\%(\<end\s\+\)\@<!\<if\>\)'.                    \ '\)'        let matching_lnum = searchpair(stmts, '', '\<end\>\zs', 'bW',                    \ 'IsColComment(line("."), col(".")) == 1')        exec 'normal! $'        if matching_lnum > 0 && matching_lnum < a:curr_lnum            let ind = indent(matching_lnum)        endif    elseif a:keyword =~? 'when'        exec 'normal! ^'        let matching_lnum = searchpair(                    \ '\%(\<end\s\+\)\@<!\<case\>\|\<exception\>\|\<merge\>',                    \ '',                    \ '\%(\%(\<when\s\+others\>\)\|\%(\<end\s\+case\>\)\)',                    \ 'bW',                    \ 'IsColComment(line("."), col(".")) == 1')        exec 'normal! $'        if matching_lnum > 0 && matching_lnum < a:curr_lnum            let ind = indent(matching_lnum)        else            let ind = indent(a:curr_lnum)        endif    endif     return indendfunction  " Check if the line is a commentfunction IsLineComment(lnum)    let rc = synIDattr(                \ synID(a:lnum,                \     match(getline(a:lnum), '\S')+1, 0)                \ , "name")                \ =~? "comment"     return rcendfunction  " Check if the column is a commentfunction IsColComment(lnum, cnum)    let rc = synIDattr(synID(a:lnum, a:cnum, 0), "name")                \           =~? "comment"     return rcendfunction  " Check if the column is a commentfunction ModuloIndent(ind)    let ind = a:ind     if ind > 0        let modulo = ind % &shiftwidth         if modulo > 0            let ind = ind - modulo        endif    endif     return indendfunction  " Find correct indent of a new line based upon the previous linefunction GetSQLIndent()    let lnum = v:lnum    let ind = indent(lnum)     " If the current line is a comment, leave the indent as is    " Comment out this additional check since it affects the    " indenting of =, and will not reindent comments as it should    " if IsLineComment(lnum) == 1    "     return ind    " endif     " while 1        " Get previous non-blank line        let prevlnum = prevnonblank(lnum - 1)        if prevlnum <= 0            return ind        endif         if IsLineComment(prevlnum) == 1            if getline(v:lnum) =~ '^\s*\*'                let ind = ModuloIndent(indent(prevlnum))                return ind + 1            endif            " If the previous line is a comment, then return -1            " to tell Vim to use the formatoptions setting to determine            " the indent to use            " But only if the next line is blank.  This would be true if            " the user is typing, but it would not be true if the user            " is reindenting the file            if getline(v:lnum) =~ '^\s*$'                return -1            endif        endif     "     let prevline = getline(prevlnum)    "     if prevline !~ '^\s*$'    "         " echom 'previous non blank - break: ' . prevline    "         break    "     endif    " endwhile     " echom 'PREVIOUS INDENT: ' . indent(prevlnum) . '  LINE: ' . getline(prevlnum)     " This is the line you just hit return on, it is not the current line    " which is new and empty    " Based on this line, we can determine how much to indent the new    " line     " Get default indent (from prev. line)    let ind      = indent(prevlnum)    let prevline = getline(prevlnum)     " Now check what's on the previous line to determine if the indent    " should be changed, for example IF, BEGIN, should increase the indent    " where END IF, END, should decrease the indent.    if prevline =~? s:SQLBlockStart        " Move indent in        let ind = ind + &sw        " echom 'prevl - SQLBlockStart - indent ' . ind . '  line: ' . prevline    elseif prevline =~ '[()]'        if prevline =~ '('            let num_unmatched_left = s:CountUnbalancedParan( prevline, '(' )        else            let num_unmatched_left = 0        endif        if prevline =~ ')'            let num_unmatched_right  = s:CountUnbalancedParan( prevline, ')' )        else            let num_unmatched_right  = 0            " let num_unmatched_right  = s:CountUnbalancedParan( prevline, ')' )        endif        if num_unmatched_left > 0            " There is a open left paranethesis            " increase indent            let ind = ind + ( &sw * num_unmatched_left )        elseif num_unmatched_right > 0            " if it is an unbalanced paranethesis only unindent if            " it was part of a command (ie create table(..)  )            " instead of part of an if (ie if (....) then) which should            " maintain the indent level            let ignore = s:CheckToIgnoreRightParan( prevlnum, num_unmatched_right )            " echom 'prevl - ) unbalanced - CTIRP - ignore: ' . ignore             if prevline =~ '^\s*)'                let ignore = ignore + 1                " echom 'prevl - begins ) unbalanced ignore: ' . ignore            endif             if (num_unmatched_right - ignore) > 0                let ind = ind - ( &sw * (num_unmatched_right - ignore) )            endif         endif    endif      " echom 'CURRENT INDENT: ' . ind . '  LINE: '  . getline(v:lnum)     " This is a new blank line since we just typed a carriage return    " Check current line; search for simplistic matching start-of-block    let line = getline(v:lnum)     if line =~? '^\s*els'        " Any line when you type else will automatically back up one        " ident level  (ie else, elseif, elsif)        let ind = ind - &sw        " echom 'curr - else - indent ' . ind    elseif line =~? '^\s*end\>'        let ind = s:GetStmtStarterIndent('end', v:lnum)        " General case for end        " let ind = ind - &sw        " echom 'curr - end - indent ' . ind    elseif line =~? '^\s*when\>'        let ind = s:GetStmtStarterIndent('when', v:lnum)        " If the WHEN clause is used with a MERGE or EXCEPTION        " clause, do not change the indent level, since these        " statements do not have a corresponding END statement.        " if stmt_starter =~? 'case'        "    let ind = ind - &sw        " endif        " elseif line =~ '^\s*)\s*;\?\s*$'        " elseif line =~ '^\s*)'    elseif line =~ '^\s*)'        let num_unmatched_right  = s:CountUnbalancedParan( line, ')' )        let ignore = s:CheckToIgnoreRightParan( v:lnum, num_unmatched_right )        " If the line ends in a ), then reduce the indent        " This catches items like:        " CREATE TABLE T1(        "    c1 int,        "    c2 int        "    );        " But we do not want to unindent a line like:        " IF ( c1 = 1        " AND  c2 = 3 ) THEN        " let num_unmatched_right  = s:CountUnbalancedParan( line, ')' )        " if num_unmatched_right > 0        " elseif strpart( line, strlen(line)-1, 1 ) =~ ')'        " let ind = ind - &sw        if line =~ '^\s*)'            " let ignore = ignore + 1            " echom 'curr - begins ) unbalanced ignore: ' . ignore        endif         if (num_unmatched_right - ignore) > 0            let ind = ind - ( &sw * (num_unmatched_right - ignore) )        endif        " endif    endif     " echom 'final - indent ' . ind    return ModuloIndent(ind)endfunction " vim:sw=4:ff=unix: