mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			400 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			VimL
		
	
	
	
	
	
			
		
		
	
	
			400 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			VimL
		
	
	
	
	
	
| " Vim indent file
 | |
| " Language:    SQL
 | |
| " Maintainer:  David Fishburn <dfishburn dot vim at gmail dot com>
 | |
| " Last Change: 2021 Oct 11
 | |
| " Version:     4.0
 | |
| " 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
 | |
| "    functions 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.
 | |
| "
 | |
| " History:
 | |
| "    4.0 (Oct 2021)
 | |
| "        Added b:undo_indent
 | |
| "
 | |
| "    3.0 (Dec 2012)
 | |
| "        Added cpo check
 | |
| "
 | |
| "    2.0
 | |
| "        Added the FOR keyword to SQLBlockStart to handle (Alec Tica):
 | |
| "            for i in 1..100 loop
 | |
| "              |<-- I expect to have indentation here
 | |
| "            end loop;
 | |
| "
 | |
| 
 | |
| " Only load this indent file when no other was loaded.
 | |
| if exists("b:did_indent")
 | |
|     finish
 | |
| endif
 | |
| let b:did_indent     = 1
 | |
| let 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 keywords
 | |
| setlocal indentkeys+==~end,=~else,=~elseif,=~elsif,0=~when,0=)
 | |
| 
 | |
| " GetSQLIndent is executed whenever one of the expressions
 | |
| " in the indentkeys is typed
 | |
| setlocal indentexpr=GetSQLIndent()
 | |
| 
 | |
| let b:undo_indent = "setl indentexpr< indentkeys<"
 | |
| 
 | |
| " Only define the functions once.
 | |
| if exists("*GetSQLIndent")
 | |
|     finish
 | |
| endif
 | |
| 
 | |
| let s:keepcpo= &cpo
 | |
| set cpo&vim
 | |
| 
 | |
| " 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\|for\|'.
 | |
|                 \ 'begin\|'.
 | |
|                 \ 'case\|when\|merge\|exception'.
 | |
|                 \ '\)\>'
 | |
| let s:SQLBlockEnd = '^\s*\(end\)\>'
 | |
| 
 | |
| " The indent level is also based on unmatched parentheses
 | |
| " If a line has an extra "(" increase the indent
 | |
| " If a line has an extra ")" decrease the indent
 | |
| function! s:CountUnbalancedParen( line, paren_to_check )
 | |
|     let l = a:line
 | |
|     let lp = substitute(l, '[^(]', '', 'g')
 | |
|     let l = a:line
 | |
|     let rp = substitute(l, '[^)]', '', 'g')
 | |
| 
 | |
|     if a:paren_to_check =~ ')'
 | |
|         " echom 'CountUnbalancedParen ) returning: ' .
 | |
|         " \ (strlen(rp) - strlen(lp))
 | |
|         return (strlen(rp) - strlen(lp))
 | |
|     elseif a:paren_to_check =~ '('
 | |
|         " echom 'CountUnbalancedParen ( returning: ' .
 | |
|         " \ (strlen(lp) - strlen(rp))
 | |
|         return (strlen(lp) - strlen(rp))
 | |
|     else
 | |
|         " echom 'CountUnbalancedParen unknown paren to check: ' .
 | |
|         " \ a:paren_to_check
 | |
|         return 0
 | |
|     endif
 | |
| endfunction
 | |
| 
 | |
| " Unindent commands based on previous indent level
 | |
| function! s:CheckToIgnoreRightParen( prev_lnum, num_levels )
 | |
|     let lnum = a:prev_lnum
 | |
|     let line = getline(lnum)
 | |
|     let ends = 0
 | |
|     let num_right_paren = a:num_levels
 | |
|     let ignore_paren = 0
 | |
|     let vircol = 1
 | |
| 
 | |
|     while num_right_paren > 0
 | |
|         silent! exec 'norm! '.lnum."G\<bar>".vircol."\<bar>"
 | |
|         let right_paren = search( ')', 'W' )
 | |
|         if right_paren != lnum
 | |
|             " This should not happen since there should be at least
 | |
|             " num_right_paren matches for this line
 | |
|             break
 | |
|         endif
 | |
|         let vircol      = virtcol(".")
 | |
| 
 | |
|         " if getline(".") =~ '^)'
 | |
|         let matching_paren = searchpair('(', '', ')', 'bW',
 | |
|                     \ 's:IsColComment(line("."), col("."))')
 | |
| 
 | |
|         if matching_paren < 1
 | |
|             " No match found
 | |
|             " echom 'CTIRP - no match found, ignoring'
 | |
|             break
 | |
|         endif
 | |
| 
 | |
|         if matching_paren == lnum
 | |
|             " This was not an unmatched parentheses, start the search again
 | |
|             " again after this column
 | |
|             " echom 'CTIRP - same line match, ignoring'
 | |
|             continue
 | |
|         endif
 | |
| 
 | |
|         " echom 'CTIRP - match: ' . line(".") . '  ' . getline(".")
 | |
| 
 | |
|         if getline(matching_paren) =~? '\(if\|while\)\>'
 | |
|             " echom 'CTIRP - if/while ignored: ' . line(".") . '  ' . getline(".")
 | |
|             let ignore_paren = ignore_paren + 1
 | |
|         endif
 | |
| 
 | |
|         " One match found, decrease and check for further matches
 | |
|         let num_right_paren = num_right_paren - 1
 | |
| 
 | |
|     endwhile
 | |
| 
 | |
|     " Fallback - just move back one
 | |
|     " return a:prev_indent - shiftwidth()
 | |
|     return ignore_paren
 | |
| endfunction
 | |
| 
 | |
| " Based on the keyword provided, loop through previous non empty
 | |
| " non comment lines to find the statement that initiated 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) - shiftwidth()
 | |
| 
 | |
|     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',
 | |
|                     \ 's: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',
 | |
|                     \ 's: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 ind
 | |
| endfunction
 | |
| 
 | |
| 
 | |
| " Check if the line is a comment
 | |
| function! s:IsLineComment(lnum)
 | |
|     let rc = synIDattr(
 | |
|                 \ synID(a:lnum,
 | |
|                 \     match(getline(a:lnum), '\S')+1, 0)
 | |
|                 \ , "name")
 | |
|                 \ =~? "comment"
 | |
| 
 | |
|     return rc
 | |
| endfunction
 | |
| 
 | |
| 
 | |
| " Check if the column is a comment
 | |
| function! s:IsColComment(lnum, cnum)
 | |
|     let rc = synIDattr(synID(a:lnum, a:cnum, 0), "name")
 | |
|                 \           =~? "comment"
 | |
| 
 | |
|     return rc
 | |
| endfunction
 | |
| 
 | |
| 
 | |
| " Instead of returning a column position, return
 | |
| " an appropriate value as a factor of shiftwidth.
 | |
| function! s:ModuloIndent(ind)
 | |
|     let ind = a:ind
 | |
| 
 | |
|     if ind > 0
 | |
|         let modulo = ind % shiftwidth()
 | |
| 
 | |
|         if modulo > 0
 | |
|             let ind = ind - modulo
 | |
|         endif
 | |
|     endif
 | |
| 
 | |
|     return ind
 | |
| endfunction
 | |
| 
 | |
| 
 | |
| " Find correct indent of a new line based upon the previous line
 | |
| function! 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 s:IsLineComment(lnum) == 1
 | |
|     "     return ind
 | |
|     " endif
 | |
| 
 | |
|     " Get previous non-blank line
 | |
|     let prevlnum = prevnonblank(lnum - 1)
 | |
|     if prevlnum <= 0
 | |
|         return ind
 | |
|     endif
 | |
| 
 | |
|     if s:IsLineComment(prevlnum) == 1
 | |
|         if getline(v:lnum) =~ '^\s*\*'
 | |
|             let ind = s: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
 | |
| 
 | |
|     " 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 + shiftwidth()
 | |
|         " echom 'prevl - SQLBlockStart - indent ' . ind . '  line: ' . prevline
 | |
|     elseif prevline =~ '[()]'
 | |
|         if prevline =~ '('
 | |
|             let num_unmatched_left = s:CountUnbalancedParen( prevline, '(' )
 | |
|         else
 | |
|             let num_unmatched_left = 0
 | |
|         endif
 | |
|         if prevline =~ ')'
 | |
|             let num_unmatched_right  = s:CountUnbalancedParen( prevline, ')' )
 | |
|         else
 | |
|             let num_unmatched_right  = 0
 | |
|             " let num_unmatched_right  = s:CountUnbalancedParen( prevline, ')' )
 | |
|         endif
 | |
|         if num_unmatched_left > 0
 | |
|             " There is a open left parenthesis
 | |
|             " increase indent
 | |
|             let ind = ind + ( shiftwidth() * num_unmatched_left )
 | |
|         elseif num_unmatched_right > 0
 | |
|             " if it is an unbalanced parenthesis 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:CheckToIgnoreRightParen( 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 - ( shiftwidth() * (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 - shiftwidth()
 | |
|         " echom 'curr - else - indent ' . ind
 | |
|     elseif line =~? '^\s*end\>'
 | |
|         let ind = s:GetStmtStarterIndent('end', v:lnum)
 | |
|         " General case for end
 | |
|         " let ind = ind - shiftwidth()
 | |
|         " 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 - shiftwidth()
 | |
|         " endif
 | |
|         " elseif line =~ '^\s*)\s*;\?\s*$'
 | |
|         " elseif line =~ '^\s*)'
 | |
|     elseif line =~ '^\s*)'
 | |
|         let num_unmatched_right  = s:CountUnbalancedParen( line, ')' )
 | |
|         let ignore = s:CheckToIgnoreRightParen( 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:CountUnbalancedParen( line, ')' )
 | |
|         " if num_unmatched_right > 0
 | |
|         " elseif strpart( line, strlen(line)-1, 1 ) =~ ')'
 | |
|         " let ind = ind - shiftwidth()
 | |
|         if line =~ '^\s*)'
 | |
|             " let ignore = ignore + 1
 | |
|             " echom 'curr - begins ) unbalanced ignore: ' . ignore
 | |
|         endif
 | |
| 
 | |
|         if (num_unmatched_right - ignore) > 0
 | |
|             let ind = ind - ( shiftwidth() * (num_unmatched_right - ignore) )
 | |
|         endif
 | |
|         " endif
 | |
|     endif
 | |
| 
 | |
|     " echom 'final - indent ' . ind
 | |
|     return s:ModuloIndent(ind)
 | |
| endfunction
 | |
| 
 | |
| "  Restore:
 | |
| let &cpo= s:keepcpo
 | |
| unlet s:keepcpo
 | |
| " vim: ts=4 fdm=marker sw=4
 | 
