diff options
Diffstat (limited to 'runtime/indent')
190 files changed, 25654 insertions, 0 deletions
diff --git a/runtime/indent/Makefile b/runtime/indent/Makefile new file mode 100644 index 0000000..f6c4473 --- /dev/null +++ b/runtime/indent/Makefile @@ -0,0 +1,14 @@ +# Portable Makefile for running indent tests. + +VIM = vim +VIMRUNTIME = .. + +# Run the tests that didn't run yet or failed previously. +# If a test succeeds a testdir/*.out file will be written. +# If a test fails a testdir/*.fail file will be written. +test: + VIMRUNTIME=$(VIMRUNTIME) $(VIM) --clean --not-a-term -u testdir/runtest.vim + + +clean testclean: + rm -f testdir/*.fail testdir/*.out diff --git a/runtime/indent/README.txt b/runtime/indent/README.txt new file mode 100644 index 0000000..05ab126 --- /dev/null +++ b/runtime/indent/README.txt @@ -0,0 +1,48 @@ +This directory contains files to automatically compute the indent for a +type of file. + +If you want to add your own indent file for your personal use, read the docs +at ":help indent-expression". Looking at the existing files should give you +inspiration. + +If you make a new indent file which would be useful for others, please send it +to the vim-dev mailing list <vim-dev@vim.org>. Include instructions for +detecting the file type for this language, by file name extension or by +checking a few lines in the file. And please stick to the rules below. + +If you have remarks about an existing file, send them to the maintainer of +that file. Only when you get no response send a message to the vim-dev +mailing list: <vim-dev@vim.org>. + +If you are the maintainer of an indent file and make improvements, e-mail the +new version to the vim-dev mailing list: <vim-dev@vim.org>. + + +Rules for making an indent file: + +You should use this check for "b:did_indent": + + " Only load this indent file when no other was loaded yet. + if exists("b:did_indent") + finish + endif + let b:did_indent = 1 + +Always use ":setlocal" to set 'indentexpr'. This avoids it being carried over +to other buffers. + +To trigger the indenting after typing a word like "endif", add the word to the +'indentkeys' option with "+=". + +You normally set 'indentexpr' to evaluate a function and then define that +function. That function only needs to be defined once for as long as Vim is +running. Add a test if the function exists and use ":finish", like this: + if exists("*GetMyIndent") + finish + endif + +The user may have several options set unlike you, try to write the file such +that it works with any option settings. Also be aware of certain features not +being compiled in. + +To test the indent file, see testdir/README.txt. diff --git a/runtime/indent/aap.vim b/runtime/indent/aap.vim new file mode 100644 index 0000000..23c1049 --- /dev/null +++ b/runtime/indent/aap.vim @@ -0,0 +1,13 @@ +" Vim indent file +" Language: Aap recipe +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Last Change: 2023 Aug 10 +" Former Maintainer: Bram Moolenaar <Bram@vim.org> + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +" Works mostly like Python. +runtime! indent/python.vim diff --git a/runtime/indent/ada.vim b/runtime/indent/ada.vim new file mode 100644 index 0000000..582d033 --- /dev/null +++ b/runtime/indent/ada.vim @@ -0,0 +1,311 @@ +"------------------------------------------------------------------------------ +" Description: Vim Ada indent file +" Language: Ada (2005) +" $Id: ada.vim 887 2008-07-08 14:29:01Z krischik $ +" Copyright: Copyright (C) 2006 Martin Krischik +" Maintainer: Martin Krischik <krischik@users.sourceforge.net> +" Neil Bird <neil@fnxweb.com> +" Ned Okie <nokie@radford.edu> +" $Author: krischik $ +" $Date: 2008-07-08 16:29:01 +0200 (Di, 08 Jul 2008) $ +" Version: 4.6 +" $Revision: 887 $ +" $HeadURL: https://gnuada.svn.sourceforge.net/svnroot/gnuada/trunk/tools/vim/indent/ada.vim $ +" History: 24.05.2006 MK Unified Headers +" 16.07.2006 MK Ada-Mode as vim-ball +" 15.10.2006 MK Bram's suggestion for runtime integration +" 05.11.2006 MK Bram suggested to save on spaces +" 19.09.2007 NO g: missing before ada#Comment +" 2022 April: b:undo_indent added by Doug Kearns +" Help Page: ft-vim-indent +"------------------------------------------------------------------------------ +" ToDo: +" Verify handling of multi-line exprs. and recovery upon the final ';'. +" Correctly find comments given '"' and "" ==> " syntax. +" Combine the two large block-indent functions into one? +"------------------------------------------------------------------------------ + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") || version < 700 + finish +endif + +let b:did_indent = 45 + +setlocal indentexpr=GetAdaIndent() +setlocal indentkeys-=0{,0} +setlocal indentkeys+=0=~then,0=~end,0=~elsif,0=~when,0=~exception,0=~begin,0=~is,0=~record + +let b:undo_indent = "setl inde< indk<" + +" Only define the functions once. +if exists("*GetAdaIndent") + finish +endif +let s:keepcpo= &cpo +set cpo&vim + +if exists("g:ada_with_gnat_project_files") + let s:AdaBlockStart = '^\s*\(if\>\|while\>\|else\>\|elsif\>\|loop\>\|for\>.*\<\(loop\|use\)\>\|declare\>\|begin\>\|type\>.*\<is\>[^;]*$\|\(type\>.*\)\=\<record\>\|procedure\>\|function\>\|accept\>\|do\>\|task\>\|package\>\|project\>\|then\>\|when\>\|is\>\)' +else + let s:AdaBlockStart = '^\s*\(if\>\|while\>\|else\>\|elsif\>\|loop\>\|for\>.*\<\(loop\|use\)\>\|declare\>\|begin\>\|type\>.*\<is\>[^;]*$\|\(type\>.*\)\=\<record\>\|procedure\>\|function\>\|accept\>\|do\>\|task\>\|package\>\|then\>\|when\>\|is\>\)' +endif + +" Section: s:MainBlockIndent {{{1 +" +" Try to find indent of the block we're in +" prev_indent = the previous line's indent +" prev_lnum = previous line (to start looking on) +" blockstart = expr. that indicates a possible start of this block +" stop_at = if non-null, if a matching line is found, gives up! +" No recursive previous block analysis: simply look for a valid line +" with a lesser or equal indent than we currently (on prev_lnum) have. +" This shouldn't work as well as it appears to with lines that are currently +" nowhere near the correct indent (e.g., start of line)! +" Seems to work OK as it 'starts' with the indent of the /previous/ line. +function s:MainBlockIndent (prev_indent, prev_lnum, blockstart, stop_at) + let lnum = a:prev_lnum + let line = substitute( getline(lnum), g:ada#Comment, '', '' ) + while lnum > 1 + if a:stop_at != '' && line =~ '^\s*' . a:stop_at && indent(lnum) < a:prev_indent + return a:prev_indent + elseif line =~ '^\s*' . a:blockstart + let ind = indent(lnum) + if ind < a:prev_indent + return ind + endif + endif + + let lnum = prevnonblank(lnum - 1) + " Get previous non-blank/non-comment-only line + while 1 + let line = substitute( getline(lnum), g:ada#Comment, '', '' ) + if line !~ '^\s*$' && line !~ '^\s*#' + break + endif + let lnum = prevnonblank(lnum - 1) + if lnum <= 0 + return a:prev_indent + endif + endwhile + endwhile + " Fallback - just move back one + return a:prev_indent - shiftwidth() +endfunction MainBlockIndent + +" Section: s:EndBlockIndent {{{1 +" +" Try to find indent of the block we're in (and about to complete), +" including handling of nested blocks. Works on the 'end' of a block. +" prev_indent = the previous line's indent +" prev_lnum = previous line (to start looking on) +" blockstart = expr. that indicates a possible start of this block +" blockend = expr. that indicates a possible end of this block +function s:EndBlockIndent( prev_indent, prev_lnum, blockstart, blockend ) + let lnum = a:prev_lnum + let line = getline(lnum) + let ends = 0 + while lnum > 1 + if getline(lnum) =~ '^\s*' . a:blockstart + let ind = indent(lnum) + if ends <= 0 + if ind < a:prev_indent + return ind + endif + else + let ends = ends - 1 + endif + elseif getline(lnum) =~ '^\s*' . a:blockend + let ends = ends + 1 + endif + + let lnum = prevnonblank(lnum - 1) + " Get previous non-blank/non-comment-only line + while 1 + let line = getline(lnum) + let line = substitute( line, g:ada#Comment, '', '' ) + if line !~ '^\s*$' + break + endif + let lnum = prevnonblank(lnum - 1) + if lnum <= 0 + return a:prev_indent + endif + endwhile + endwhile + " Fallback - just move back one + return a:prev_indent - shiftwidth() +endfunction EndBlockIndent + +" Section: s:StatementIndent {{{1 +" +" Return indent of previous statement-start +" (after we've indented due to multi-line statements). +" This time, we start searching on the line *before* the one given (which is +" the end of a statement - we want the previous beginning). +function s:StatementIndent( current_indent, prev_lnum ) + let lnum = a:prev_lnum + while lnum > 0 + let prev_lnum = lnum + let lnum = prevnonblank(lnum - 1) + " Get previous non-blank/non-comment-only line + while 1 + let line = substitute( getline(lnum), g:ada#Comment, '', '' ) + + if line !~ '^\s*$' && line !~ '^\s*#' + break + endif + let lnum = prevnonblank(lnum - 1) + if lnum <= 0 + return a:current_indent + endif + endwhile + " Leave indent alone if our ';' line is part of a ';'-delineated + " aggregate (e.g., procedure args.) or first line after a block start. + if line =~ s:AdaBlockStart || line =~ '(\s*$' + return a:current_indent + endif + if line !~ '[.=(]\s*$' + let ind = indent(prev_lnum) + if ind < a:current_indent + return ind + endif + endif + endwhile + " Fallback - just use current one + return a:current_indent +endfunction StatementIndent + + +" Section: GetAdaIndent {{{1 +" +" Find correct indent of a new line based upon what went before +" +function GetAdaIndent() + " Find a non-blank line above the current line. + let lnum = prevnonblank(v:lnum - 1) + let ind = indent(lnum) + let package_line = 0 + + " Get previous non-blank/non-comment-only/non-cpp line + while 1 + let line = substitute( getline(lnum), g:ada#Comment, '', '' ) + if line !~ '^\s*$' && line !~ '^\s*#' + break + endif + let lnum = prevnonblank(lnum - 1) + if lnum <= 0 + return ind + endif + endwhile + + " Get default indent (from prev. line) + let ind = indent(lnum) + let initind = ind + + " Now check what's on the previous line + if line =~ s:AdaBlockStart || line =~ '(\s*$' + " Check for false matches to AdaBlockStart + let false_match = 0 + if line =~ '^\s*\(procedure\|function\|package\)\>.*\<is\s*new\>' + " Generic instantiation + let false_match = 1 + elseif line =~ ')\s*;\s*$' || line =~ '^\([^(]*([^)]*)\)*[^(]*;\s*$' + " forward declaration + let false_match = 1 + endif + " Move indent in + if ! false_match + let ind = ind + shiftwidth() + endif + elseif line =~ '^\s*\(case\|exception\)\>' + " Move indent in twice (next 'when' will move back) + let ind = ind + 2 * shiftwidth() + elseif line =~ '^\s*end\s*record\>' + " Move indent back to tallying 'type' preceding the 'record'. + " Allow indent to be equal to 'end record's. + let ind = s:MainBlockIndent( ind+shiftwidth(), lnum, 'type\>', '' ) + elseif line =~ '\(^\s*new\>.*\)\@<!)\s*[;,]\s*$' + " Revert to indent of line that started this parenthesis pair + exe lnum + exe 'normal! $F)%' + if getline('.') =~ '^\s*(' + " Dire layout - use previous indent (could check for g:ada#Comment here) + let ind = indent( prevnonblank( line('.')-1 ) ) + else + let ind = indent('.') + endif + exe v:lnum + elseif line =~ '[.=(]\s*$' + " A statement continuation - move in one + let ind = ind + shiftwidth() + elseif line =~ '^\s*new\>' + " Multiple line generic instantiation ('package blah is\nnew thingy') + let ind = s:StatementIndent( ind - shiftwidth(), lnum ) + elseif line =~ ';\s*$' + " Statement end (but not 'end' ) - try to find current statement-start indent + let ind = s:StatementIndent( ind, lnum ) + endif + + " Check for potential argument list on next line + let continuation = (line =~ '[A-Za-z0-9_]\s*$') + + + " Check current line; search for simplistic matching start-of-block + let line = getline(v:lnum) + if line =~ '^\s*#' + " Start of line for ada-pp + let ind = 0 + elseif continuation && line =~ '^\s*(' + " Don't do this if we've already indented due to the previous line + if ind == initind + let ind = ind + shiftwidth() + endif + elseif line =~ '^\s*\(begin\|is\)\>' + let ind = s:MainBlockIndent( ind, lnum, '\(procedure\|function\|declare\|package\|task\)\>', 'begin\>' ) + elseif line =~ '^\s*record\>' + let ind = s:MainBlockIndent( ind, lnum, 'type\>\|for\>.*\<use\>', '' ) + shiftwidth() + elseif line =~ '^\s*\(else\|elsif\)\>' + let ind = s:MainBlockIndent( ind, lnum, 'if\>', '' ) + elseif line =~ '^\s*when\>' + " Align 'when' one /in/ from matching block start + let ind = s:MainBlockIndent( ind, lnum, '\(case\|exception\)\>', '' ) + shiftwidth() + elseif line =~ '^\s*end\>\s*\<if\>' + " End of if statements + let ind = s:EndBlockIndent( ind, lnum, 'if\>', 'end\>\s*\<if\>' ) + elseif line =~ '^\s*end\>\s*\<loop\>' + " End of loops + let ind = s:EndBlockIndent( ind, lnum, '\(\(while\|for\)\>.*\)\?\<loop\>', 'end\>\s*\<loop\>' ) + elseif line =~ '^\s*end\>\s*\<record\>' + " End of records + let ind = s:EndBlockIndent( ind, lnum, '\(type\>.*\)\=\<record\>', 'end\>\s*\<record\>' ) + elseif line =~ '^\s*end\>\s*\<procedure\>' + " End of procedures + let ind = s:EndBlockIndent( ind, lnum, 'procedure\>.*\<is\>', 'end\>\s*\<procedure\>' ) + elseif line =~ '^\s*end\>\s*\<case\>' + " End of case statement + let ind = s:EndBlockIndent( ind, lnum, 'case\>.*\<is\>', 'end\>\s*\<case\>' ) + elseif line =~ '^\s*end\>' + " General case for end + let ind = s:MainBlockIndent( ind, lnum, '\(if\|while\|for\|loop\|accept\|begin\|record\|case\|exception\|package\)\>', '' ) + elseif line =~ '^\s*exception\>' + let ind = s:MainBlockIndent( ind, lnum, 'begin\>', '' ) + elseif line =~ '^\s*then\>' + let ind = s:MainBlockIndent( ind, lnum, 'if\>', '' ) + endif + + return ind +endfunction GetAdaIndent + +let &cpo = s:keepcpo +unlet s:keepcpo + +finish " 1}}} + +"------------------------------------------------------------------------------ +" Copyright (C) 2006 Martin Krischik +" +" Vim is Charityware - see ":help license" or uganda.txt for licence details. +"------------------------------------------------------------------------------ +" vim: textwidth=78 wrap tabstop=8 shiftwidth=3 softtabstop=3 noexpandtab +" vim: foldmethod=marker diff --git a/runtime/indent/ant.vim b/runtime/indent/ant.vim new file mode 100644 index 0000000..067f272 --- /dev/null +++ b/runtime/indent/ant.vim @@ -0,0 +1,12 @@ +" Vim indent file +" Language: ANT files +" Maintainer: David Fishburn <fishburn@ianywhere.com> +" Last Change: Thu May 15 2003 10:02:54 PM + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +" Use XML formatting rules +runtime! indent/xml.vim diff --git a/runtime/indent/automake.vim b/runtime/indent/automake.vim new file mode 100644 index 0000000..7e38f92 --- /dev/null +++ b/runtime/indent/automake.vim @@ -0,0 +1,11 @@ +" Vim indent file +" Language: automake +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Latest Revision: 2006-04-19 + +if exists("b:did_indent") + finish +endif + +" same as makefile indenting for now. +runtime! indent/make.vim diff --git a/runtime/indent/awk.vim b/runtime/indent/awk.vim new file mode 100644 index 0000000..cf81322 --- /dev/null +++ b/runtime/indent/awk.vim @@ -0,0 +1,235 @@ +" vim: set sw=3 sts=3: + +" Awk indent script. It can handle multi-line statements and expressions. +" It works up to the point where the distinction between correct/incorrect +" and personal taste gets fuzzy. Drop me an e-mail for bug reports and +" reasonable style suggestions. +" +" Bugs: +" ===== +" - Some syntax errors may cause erratic indentation. +" - Same for very unusual but syntacticly correct use of { } +" - In some cases it's confused by the use of ( and { in strings constants +" - This version likes the closing brace of a multiline pattern-action be on +" character position 1 before the following pattern-action combination is +" formatted + +" Author: +" ======= +" Erik Janssen, ejanssen@itmatters.nl +" +" History: +" ======== +" 26-04-2002 Got initial version working reasonably well +" 29-04-2002 Fixed problems in function headers and max line width +" Added support for two-line if's without curly braces +" Fixed hang: 2011 Aug 31 +" 2022 April: b:undo_indent added by Doug Kearns + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +let b:did_indent = 1 + +setlocal indentexpr=GetAwkIndent() +" Mmm, copied from the tcl indent program. Is this okay? +setlocal indentkeys-=:,0# + +let b:undo_indent = "setl inde< indk<" + +" Only define the function once. +if exists("*GetAwkIndent") + finish +endif + +" This function contains a lot of exit points. It checks for simple cases +" first to get out of the function as soon as possible, thereby reducing the +" number of possibilities later on in the difficult parts + +function! GetAwkIndent() + + " Find previous line and get its indentation + let prev_lineno = s:Get_prev_line( v:lnum ) + if prev_lineno == 0 + return 0 + endif + let prev_data = getline( prev_lineno ) + let ind = indent( prev_lineno ) + + " Increase indent if the previous line contains an opening brace. Search + " for this brace the hard way to prevent errors if the previous line is a + " 'pattern { action }' (simple check match on /{/ increases the indent then) + + if s:Get_brace_balance( prev_data, '{', '}' ) > 0 + return ind + shiftwidth() + endif + + let brace_balance = s:Get_brace_balance( prev_data, '(', ')' ) + + " If prev line has positive brace_balance and starts with a word (keyword + " or function name), align the current line on the first '(' of the prev + " line + + if brace_balance > 0 && s:Starts_with_word( prev_data ) + return s:Safe_indent( ind, s:First_word_len(prev_data), getline(v:lnum)) + endif + + " If this line starts with an open brace bail out now before the line + " continuation checks. + + if getline( v:lnum ) =~ '^\s*{' + return ind + endif + + " If prev line seems to be part of multiline statement: + " 1. Prev line is first line of a multiline statement + " -> attempt to indent on first ' ' or '(' of prev line, just like we + " indented the positive brace balance case above + " 2. Prev line is not first line of a multiline statement + " -> copy indent of prev line + + let continue_mode = s:Seems_continuing( prev_data ) + if continue_mode > 0 + if s:Seems_continuing( getline(s:Get_prev_line( prev_lineno )) ) + " Case 2 + return ind + else + " Case 1 + if continue_mode == 1 + " Need continuation due to comma, backslash, etc + return s:Safe_indent( ind, s:First_word_len(prev_data), getline(v:lnum)) + else + " if/for/while without '{' + return ind + shiftwidth() + endif + endif + endif + + " If the previous line doesn't need continuation on the current line we are + " on the start of a new statement. We have to make sure we align with the + " previous statement instead of just the previous line. This is a bit + " complicated because the previous statement might be multi-line. + " + " The start of a multiline statement can be found by: + " + " 1 If the previous line contains closing braces and has negative brace + " balance, search backwards until cumulative brace balance becomes zero, + " take indent of that line + " 2 If the line before the previous needs continuation search backward + " until that's not the case anymore. Take indent of one line down. + + " Case 1 + if prev_data =~ ')' && brace_balance < 0 + while brace_balance != 0 && prev_lineno > 0 + let prev_lineno = s:Get_prev_line( prev_lineno ) + let prev_data = getline( prev_lineno ) + let brace_balance=brace_balance+s:Get_brace_balance(prev_data,'(',')' ) + endwhile + let ind = indent( prev_lineno ) + else + " Case 2 + if s:Seems_continuing( getline( prev_lineno - 1 ) ) + let prev_lineno = prev_lineno - 2 + let prev_data = getline( prev_lineno ) + while prev_lineno > 0 && (s:Seems_continuing( prev_data ) > 0) + let prev_lineno = s:Get_prev_line( prev_lineno ) + let prev_data = getline( prev_lineno ) + endwhile + let ind = indent( prev_lineno + 1 ) + endif + endif + + " Decrease indent if this line contains a '}'. + if getline(v:lnum) =~ '^\s*}' + let ind = ind - shiftwidth() + endif + + return ind +endfunction + +" Find the open and close braces in this line and return how many more open- +" than close braces there are. It's also used to determine cumulative balance +" across multiple lines. + +function! s:Get_brace_balance( line, b_open, b_close ) + let line2 = substitute( a:line, a:b_open, "", "g" ) + let openb = strlen( a:line ) - strlen( line2 ) + let line3 = substitute( line2, a:b_close, "", "g" ) + let closeb = strlen( line2 ) - strlen( line3 ) + return openb - closeb +endfunction + +" Find out whether the line starts with a word (i.e. keyword or function +" call). Might need enhancements here. + +function! s:Starts_with_word( line ) + if a:line =~ '^\s*[a-zA-Z_0-9]\+\s*(' + return 1 + endif + return 0 +endfunction + +" Find the length of the first word in a line. This is used to be able to +" align a line relative to the 'print ' or 'if (' on the previous line in case +" such a statement spans multiple lines. +" Precondition: only to be used on lines where 'Starts_with_word' returns 1. + +function! s:First_word_len( line ) + let white_end = matchend( a:line, '^\s*' ) + if match( a:line, '^\s*func' ) != -1 + let word_end = matchend( a:line, '[a-z]\+\s\+[a-zA-Z_0-9]\+[ (]*' ) + else + let word_end = matchend( a:line, '[a-zA-Z_0-9]\+[ (]*' ) + endif + return word_end - white_end +endfunction + +" Determine if 'line' completes a statement or is continued on the next line. +" This one is far from complete and accepts illegal code. Not important for +" indenting, however. + +function! s:Seems_continuing( line ) + " Unfinished lines + if a:line =~ '\(--\|++\)\s*$' + return 0 + endif + if a:line =~ '[\\,\|\&\+\-\*\%\^]\s*$' + return 1 + endif + " if/for/while (cond) eol + if a:line =~ '^\s*\(if\|while\|for\)\s*(.*)\s*$' || a:line =~ '^\s*else\s*' + return 2 + endif + return 0 +endfunction + +" Get previous relevant line. Search back until a line is that is no +" comment or blank and return the line number + +function! s:Get_prev_line( lineno ) + let lnum = a:lineno - 1 + let data = getline( lnum ) + while lnum > 0 && (data =~ '^\s*#' || data =~ '^\s*$') + let lnum = lnum - 1 + let data = getline( lnum ) + endwhile + return lnum +endfunction + +" This function checks whether an indented line exceeds a maximum linewidth +" (hardcoded 80). If so and it is possible to stay within 80 positions (or +" limit num of characters beyond linewidth) by decreasing the indent (keeping +" it > base_indent), do so. + +function! s:Safe_indent( base, wordlen, this_line ) + let line_base = matchend( a:this_line, '^\s*' ) + let line_len = strlen( a:this_line ) - line_base + let indent = a:base + if (indent + a:wordlen + line_len) > 80 + " Simple implementation good enough for the time being + let indent = indent + 3 + endif + return indent + a:wordlen +endfunction diff --git a/runtime/indent/bash.vim b/runtime/indent/bash.vim new file mode 100644 index 0000000..4070812 --- /dev/null +++ b/runtime/indent/bash.vim @@ -0,0 +1,18 @@ +" Vim indent file +" Language: bash +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Last Change: 2023 Aug 13 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +" The actual indenting is in sh.vim and controlled by buffer-local variables. +unlet! b:is_sh +unlet! b:is_kornshell +let b:is_bash = 1 + +runtime! indent/sh.vim + +" vim: ts=8 diff --git a/runtime/indent/basic.vim b/runtime/indent/basic.vim new file mode 100644 index 0000000..7228772 --- /dev/null +++ b/runtime/indent/basic.vim @@ -0,0 +1,11 @@ +" Vim indent file +" Language: BASIC (QuickBASIC 4.5) +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2022 Jan 24 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +runtime! indent/vb.vim diff --git a/runtime/indent/bib.vim b/runtime/indent/bib.vim new file mode 100644 index 0000000..d1a0636 --- /dev/null +++ b/runtime/indent/bib.vim @@ -0,0 +1,15 @@ +" Vim indent file +" Language: BibTeX +" Maintainer: Dorai Sitaram <ds26@gte.com> +" URL: http://www.ccs.neu.edu/~dorai/vimplugins/vimplugins.html +" Last Change: 2005 Mar 28 + +" Only do this when not done yet for this buffer +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal cindent + +let b:undo_indent = "setl cin<" diff --git a/runtime/indent/bitbake.vim b/runtime/indent/bitbake.vim new file mode 100644 index 0000000..f45ba74 --- /dev/null +++ b/runtime/indent/bitbake.vim @@ -0,0 +1,22 @@ +" Vim indent file +" Language: BitBake +" Copyright: Copyright (C) 2019 Agilent Technologies, Inc. +" Maintainer: Chris Laplante <chris.laplante@agilent.com> +" License: You may redistribute this under the same terms as Vim itself + +if exists("b:did_indent") + finish +endif + +runtime! indent/sh.vim + +setlocal indentexpr=bitbake#Indent(v:lnum) +setlocal autoindent +setlocal nolisp +setlocal shiftwidth=4 +setlocal expandtab +setlocal indentkeys+=<:>,=elif,=except,0=\" + +let b:undo_indent .= ' inde< ai< lisp< sw< et< indk<' + +let b:did_indent = 1 diff --git a/runtime/indent/bst.vim b/runtime/indent/bst.vim new file mode 100644 index 0000000..3dd8d71 --- /dev/null +++ b/runtime/indent/bst.vim @@ -0,0 +1,73 @@ +" Vim indent file +" Language: bst +" Author: Tim Pope <vimNOSPAM@tpope.info> +" Last Change: 2022 Mar 15 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetBstIndent(v:lnum) +setlocal cinkeys& +setlocal cinkeys-=0# +setlocal indentkeys& +let b:undo_indent = 'setlocal indentexpr< cinkeys< indentkeys<' + +" Only define the function once. +if exists("*GetBstIndent") + finish +endif + +function! s:prevgood(lnum) + " Find a non-blank line above the current line. + " Skip over comments. + let lnum = a:lnum + while lnum > 0 + let lnum = prevnonblank(lnum - 1) + if getline(lnum) !~ '^\s*%.*$' + break + endif + endwhile + return lnum +endfunction + +function! s:strip(lnum) + let line = getline(a:lnum) + let line = substitute(line,'"[^"]*"','""','g') + let line = substitute(line,'%.*','','') + let line = substitute(line,'^\s\+','','') + return line +endfunction + +function! s:count(string,char) + let str = substitute(a:string,'[^'.a:char.']','','g') + return strlen(str) +endfunction + +function! GetBstIndent(lnum) abort + if a:lnum == 1 + return 0 + endif + let lnum = s:prevgood(a:lnum) + if lnum <= 0 + return indent(a:lnum - 1) + endif + let line = s:strip(lnum) + let cline = s:strip(a:lnum) + if cline =~ '^}' && exists("b:current_syntax") + call cursor(a:lnum,indent(a:lnum)) + if searchpair('{','','}','bW',"synIDattr(synID(line('.'),col('.'),1),'name') =~? 'comment\\|string'") + if col('.')+1 == col('$') + return indent('.') + else + return virtcol('.')-1 + endif + endif + endif + let fakeline = substitute(line,'^}','','').matchstr(cline,'^}') + let ind = indent(lnum) + let ind = ind + shiftwidth() * s:count(line,'{') + let ind = ind - shiftwidth() * s:count(fakeline,'}') + return ind +endfunction diff --git a/runtime/indent/bzl.vim b/runtime/indent/bzl.vim new file mode 100644 index 0000000..cf4cfb5 --- /dev/null +++ b/runtime/indent/bzl.vim @@ -0,0 +1,105 @@ +" Vim indent file +" Language: Bazel (http://bazel.io) +" Maintainer: David Barnett (https://github.com/google/vim-ft-bzl) +" Last Change: 2021 Jul 08 + +if exists('b:did_indent') + finish +endif + +" Load base python indent. +if !exists('*GetPythonIndent') + runtime! indent/python.vim +endif + +let b:did_indent = 1 + +" Only enable bzl google indent if python google indent is enabled. +if !get(g:, 'no_google_python_indent') + setlocal indentexpr=GetBzlIndent(v:lnum) +endif + +if exists('*GetBzlIndent') + finish +endif + +let s:save_cpo = &cpo +set cpo-=C + +" Maximum number of lines to look backwards. +let s:maxoff = 50 + +"" +" Determine the correct indent level given an {lnum} in the current buffer. +function GetBzlIndent(lnum) abort + let l:use_recursive_indent = !get(g:, 'no_google_python_recursive_indent') + if l:use_recursive_indent + " Backup and override indent setting variables. + if exists('g:pyindent_nested_paren') + let l:pyindent_nested_paren = g:pyindent_nested_paren + endif + if exists('g:pyindent_open_paren') + let l:pyindent_open_paren = g:pyindent_open_paren + endif + let g:pyindent_nested_paren = 'shiftwidth()' + let g:pyindent_open_paren = 'shiftwidth()' + endif + + let l:indent = -1 + + call cursor(a:lnum, 1) + let [l:par_line, l:par_col] = searchpairpos('(\|{\|\[', '', ')\|}\|\]', 'bW', + \ "line('.') < " . (a:lnum - s:maxoff) . " ? dummy :" . + \ " synIDattr(synID(line('.'), col('.'), 1), 'name')" . + \ " =~ '\\(Comment\\|String\\)$'") + if l:par_line > 0 + " Indent inside parens. + if searchpair('(\|{\|\[', '', ')\|}\|\]', 'W', + \ "line('.') < " . (a:lnum - s:maxoff) . " ? dummy :" . + \ " synIDattr(synID(line('.'), col('.'), 1), 'name')" . + \ " =~ '\\(Comment\\|String\\)$'") && line('.') == a:lnum + " If cursor is at close parens, match indent with open parens. + " E.g. + " foo( + " ) + let l:indent = indent(l:par_line) + else + " Align with the open paren unless it is at the end of the line. + " E.g. + " open_paren_not_at_EOL(100, + " (200, + " 300), + " 400) + " open_paren_at_EOL( + " 100, 200, 300, 400) + call cursor(l:par_line, 1) + if l:par_col != col('$') - 1 + let l:indent = l:par_col + endif + endif + endif + + " Delegate the rest to the original function. + if l:indent == -1 + let l:indent = GetPythonIndent(a:lnum) + endif + + if l:use_recursive_indent + " Restore global variables. + if exists('l:pyindent_nested_paren') + let g:pyindent_nested_paren = l:pyindent_nested_paren + else + unlet g:pyindent_nested_paren + endif + if exists('l:pyindent_open_paren') + let g:pyindent_open_paren = l:pyindent_open_paren + else + unlet g:pyindent_open_paren + endif + endif + + return l:indent +endfunction + +let &cpo = s:save_cpo +unlet s:save_cpo diff --git a/runtime/indent/c.vim b/runtime/indent/c.vim new file mode 100644 index 0000000..7f285e1 --- /dev/null +++ b/runtime/indent/c.vim @@ -0,0 +1,16 @@ +" Vim indent file +" Language: C +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Last Change: 2023 Aug 10 +" Former Maintainer: Bram Moolenaar <Bram@vim.org> + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" C indenting is built-in, thus this is very simple +setlocal cindent + +let b:undo_indent = "setl cin<" diff --git a/runtime/indent/cdl.vim b/runtime/indent/cdl.vim new file mode 100644 index 0000000..da67569 --- /dev/null +++ b/runtime/indent/cdl.vim @@ -0,0 +1,136 @@ +" Description: Comshare Dimension Definition Language (CDL) +" Maintainer: Raul Segura Acevedo <raulseguraaceved@netscape.net> (Invalid email address) +" Doug Kearns <dougkearns@gmail.com> +" Last Change: 2022 Apr 06 + +if exists("b:did_indent") + "finish +endif +let b:did_indent = 1 + +setlocal indentexpr=CdlGetIndent(v:lnum) +setlocal indentkeys& +setlocal indentkeys+==~else,=~endif,=~then,;,),= + +let b:undo_indent = "setl inde< indk<" + +" Only define the function once. +if exists("*CdlGetIndent") + "finish +endif + +" find out if an "...=..." expression is an assignment (or a conditional) +" it scans 'line' first, and then the previous lines +fun! CdlAssignment(lnum, line) + let f = -1 + let lnum = a:lnum + let line = a:line + while lnum > 0 && f == -1 + " line without members [a] of [b]:[c]... + let inicio = 0 + while 1 + " keywords that help to decide + let inicio = matchend(line, '\c\<\(expr\|\a*if\|and\|or\|not\|else\|then\|memberis\|\k\+of\)\>\|[<>;]', inicio) + if inicio < 0 + break + endif + " it's formula if there's a ';', 'elsE', 'theN', 'enDif' or 'expr' + " conditional if there's a '<', '>', 'elseif', 'if', 'and', 'or', 'not', + " 'memberis', 'childrenof' and other \k\+of functions + let f = line[inicio-1] =~? '[en;]' || strpart(line, inicio-4, 4) =~? 'ndif\|expr' + endw + let lnum = prevnonblank(lnum-1) + let line = substitute(getline(lnum), '\c\(\[[^]]*]\(\s*of\s*\|:\)*\)\+', ' ', 'g') + endw + " if we hit the start of the file then f = -1, return 1 (formula) + return f != 0 +endf + +fun! CdlGetIndent(lnum) + let thisline = getline(a:lnum) + if match(thisline, '^\s*\(\k\+\|\[[^]]*]\)\s*\(,\|;\s*$\)') >= 0 + " it's an attributes line + return shiftwidth() + elseif match(thisline, '^\c\s*\([{}]\|\/[*/]\|dimension\|schedule\|group\|hierarchy\|class\)') >= 0 + " it's a header or '{' or '}' or a comment + return 0 + end + + let lnum = prevnonblank(a:lnum-1) + " Hit the start of the file, use zero indent. + if lnum == 0 + return 0 + endif + + " PREVIOUS LINE + let ind = indent(lnum) + let line = getline(lnum) + + " Whether a '=' is a conditional or an assignment. -1 means we don't know + " yet. + " One 'closing' element at the beginning of the line has already reduced the + " indent, but 'else', 'elseif' & 'then' increment it for the next line. + " '=' at the beginning already has the right indent (increased for + " asignments). + let f = -1 + let inicio = matchend(line, '^\c\s*\(else\a*\|then\|endif\|/[*/]\|[);={]\)') + if inicio > 0 + let c = line[inicio-1] + " ')' and '=' don't change indent and are useless to set 'f' + if c == '{' + return shiftwidth() + elseif c != ')' && c != '=' + let f = 1 " all but 'elseif' are followed by a formula + if c ==? 'n' || c ==? 'e' " 'then', 'else' + let ind = ind + shiftwidth() + elseif strpart(line, inicio-6, 6) ==? 'elseif' " elseif, set f to conditional + let ind = ind + shiftwidth() + let f = 0 + end + end + end + + " remove members [a] of [b]:[c]... (inicio remains valid) + let line = substitute(line, '\c\(\[[^]]*]\(\s*of\s*\|:\)*\)\+', ' ', 'g') + while 1 + " search for the next interesting element + let inicio=matchend(line, '\c\<if\|endif\|[()=;]', inicio) + if inicio < 0 + break + end + + let c = line[inicio-1] + " 'expr(...)' containing the formula + if strpart(line, inicio-5, 5) ==? 'expr(' + let ind = 0 + let f = 1 + elseif c == ')' || c== ';' || strpart(line, inicio-5, 5) ==? 'endif' + let ind = ind - shiftwidth() + elseif c == '(' || c ==? 'f' " '(' or 'if' + let ind = ind + shiftwidth() + else " c == '=' + " if it is an assignment increase indent + if f == -1 " we don't know yet, find out + let f = CdlAssignment(lnum, strpart(line, 0, inicio)) + end + if f == 1 " formula increase it + let ind = ind + shiftwidth() + end + end + endw + + " CURRENT LINE, if it starts with a closing element, decrease indent + " or if it starts with '=' (assignment), increase indent + if match(thisline, '^\c\s*\(else\|then\|endif\|[);]\)') >= 0 + let ind = ind - shiftwidth() + elseif match(thisline, '^\s*=') >= 0 + if f == -1 " we don't know yet if is an assignment, find out + let f = CdlAssignment(lnum, "") + end + if f == 1 " formula increase it + let ind = ind + shiftwidth() + end + end + + return ind +endfun diff --git a/runtime/indent/ch.vim b/runtime/indent/ch.vim new file mode 100644 index 0000000..11b1f67 --- /dev/null +++ b/runtime/indent/ch.vim @@ -0,0 +1,21 @@ +" Vim indent file +" Language: Ch +" Maintainer: SoftIntegration, Inc. <info@softintegration.com> +" URL: http://www.softintegration.com/download/vim/indent/ch.vim +" Last change: 2006 Apr 30 +" 2023 Aug 28 by Vim Project (undo_indent) +" Created based on cpp.vim +" +" Ch is a C/C++ interpreter with many high level extensions + + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" Ch indenting is built-in, thus this is very simple +setlocal cindent + +let b:undo_indent = "setlocal cindent<" diff --git a/runtime/indent/chaiscript.vim b/runtime/indent/chaiscript.vim new file mode 100644 index 0000000..b7a3fe5 --- /dev/null +++ b/runtime/indent/chaiscript.vim @@ -0,0 +1,53 @@ +" Vim indent file +" Language: ChaiScript +" Maintainer: Jason Turner <lefticus 'at' gmail com> +" Last Change: 2022 Apr 06 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetChaiScriptIndent() +setlocal autoindent + +let b:undo_indent = "setl ai< inde<" + +" Only define the function once. +if exists("*GetChaiScriptIndent") + finish +endif + +function! GetChaiScriptIndent() + " Find a non-blank line above the current line. + let lnum = prevnonblank(v:lnum - 1) + + " Hit the start of the file, use zero indent. + if lnum == 0 + return 0 + endif + + " Add a 'shiftwidth' after lines that start a block: + " lines containing a { + let ind = indent(lnum) + let flag = 0 + let prevline = getline(lnum) + if prevline =~ '^.*{.*' + let ind = ind + shiftwidth() + let flag = 1 + endif + + " Subtract a 'shiftwidth' after lines containing a { followed by a } + " to keep it balanced + if flag == 1 && prevline =~ '.*{.*}.*' + let ind = ind - shiftwidth() + endif + + " Subtract a 'shiftwidth' on lines ending with } + if getline(v:lnum) =~ '^\s*\%(}\)' + let ind = ind - shiftwidth() + endif + + return ind +endfunction diff --git a/runtime/indent/changelog.vim b/runtime/indent/changelog.vim new file mode 100644 index 0000000..522c64d --- /dev/null +++ b/runtime/indent/changelog.vim @@ -0,0 +1,14 @@ +" Vim indent file +" Language: generic Changelog file +" Maintainer: noone +" Last Change: 2005 Mar 29 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal ai + +let b:undo_indent = "setl ai<" diff --git a/runtime/indent/chatito.vim b/runtime/indent/chatito.vim new file mode 100644 index 0000000..1ff5e9e --- /dev/null +++ b/runtime/indent/chatito.vim @@ -0,0 +1,32 @@ +" Vim indent file +" Language: Chatito +" Maintainer: ObserverOfTime <chronobserver@disroot.org> +" Last Change: 2022 Sep 20 + +if exists('b:did_indent') + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetChatitoIndent() +setlocal indentkeys=o,O,*<Return>,0#,!^F + +let b:undo_indent = 'setl inde< indk<' + +if exists('*GetChatitoIndent') + finish +endif + +function GetChatitoIndent() + let l:prev = v:lnum - 1 + if getline(prevnonblank(l:prev)) =~# '^[~%@]\[' + " shift indent after definitions + return shiftwidth() + elseif getline(l:prev) !~# '^\s*$' + " maintain indent in sentences + return indent(l:prev) + else + " reset indent after a blank line + return 0 + end +endfunction diff --git a/runtime/indent/clojure.vim b/runtime/indent/clojure.vim new file mode 100644 index 0000000..5bfbfbb --- /dev/null +++ b/runtime/indent/clojure.vim @@ -0,0 +1,427 @@ +" Vim indent file +" Language: Clojure +" Maintainer: Alex Vear <alex@vear.uk> +" Former Maintainers: Sung Pae <self@sungpae.com> +" Meikel Brandmeyer <mb@kotka.de> +" URL: https://github.com/clojure-vim/clojure.vim +" License: Vim (see :h license) +" Last Change: 2022-03-24 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +let s:save_cpo = &cpo +set cpo&vim + +let b:undo_indent = 'setlocal autoindent< smartindent< expandtab< softtabstop< shiftwidth< indentexpr< indentkeys<' + +setlocal noautoindent nosmartindent +setlocal softtabstop=2 shiftwidth=2 expandtab +setlocal indentkeys=!,o,O + +if exists("*searchpairpos") + + if !exists('g:clojure_maxlines') + let g:clojure_maxlines = 300 + endif + + if !exists('g:clojure_fuzzy_indent') + let g:clojure_fuzzy_indent = 1 + endif + + if !exists('g:clojure_fuzzy_indent_patterns') + let g:clojure_fuzzy_indent_patterns = ['^with', '^def', '^let'] + endif + + if !exists('g:clojure_fuzzy_indent_blacklist') + let g:clojure_fuzzy_indent_blacklist = ['-fn$', '\v^with-%(meta|out-str|loading-context)$'] + endif + + if !exists('g:clojure_special_indent_words') + let g:clojure_special_indent_words = 'deftype,defrecord,reify,proxy,extend-type,extend-protocol,letfn' + endif + + if !exists('g:clojure_align_multiline_strings') + let g:clojure_align_multiline_strings = 0 + endif + + if !exists('g:clojure_align_subforms') + let g:clojure_align_subforms = 0 + endif + + function! s:syn_id_name() + return synIDattr(synID(line("."), col("."), 0), "name") + endfunction + + function! s:ignored_region() + return s:syn_id_name() =~? '\vstring|regex|comment|character' + endfunction + + function! s:current_char() + return getline('.')[col('.')-1] + endfunction + + function! s:current_word() + return getline('.')[col('.')-1 : searchpos('\v>', 'n', line('.'))[1]-2] + endfunction + + function! s:is_paren() + return s:current_char() =~# '\v[\(\)\[\]\{\}]' && !s:ignored_region() + endfunction + + " Returns 1 if string matches a pattern in 'patterns', which should be + " a list of patterns. + function! s:match_one(patterns, string) + for pat in a:patterns + if a:string =~# pat | return 1 | endif + endfor + endfunction + + function! s:match_pairs(open, close, stopat) + " Stop only on vector and map [ resp. {. Ignore the ones in strings and + " comments. + if a:stopat == 0 && g:clojure_maxlines > 0 + let stopat = max([line(".") - g:clojure_maxlines, 0]) + else + let stopat = a:stopat + endif + + let pos = searchpairpos(a:open, '', a:close, 'bWn', "!s:is_paren()", stopat) + return [pos[0], col(pos)] + endfunction + + function! s:clojure_check_for_string_worker() + " Check whether there is the last character of the previous line is + " highlighted as a string. If so, we check whether it's a ". In this + " case we have to check also the previous character. The " might be the + " closing one. In case the we are still in the string, we search for the + " opening ". If this is not found we take the indent of the line. + let nb = prevnonblank(v:lnum - 1) + + if nb == 0 + return -1 + endif + + call cursor(nb, 0) + call cursor(0, col("$") - 1) + if s:syn_id_name() !~? "string" + return -1 + endif + + " This will not work for a " in the first column... + if s:current_char() == '"' + call cursor(0, col("$") - 2) + if s:syn_id_name() !~? "string" + return -1 + endif + if s:current_char() != '\' + return -1 + endif + call cursor(0, col("$") - 1) + endif + + let p = searchpos('\(^\|[^\\]\)\zs"', 'bW') + + if p != [0, 0] + return p[1] - 1 + endif + + return indent(".") + endfunction + + function! s:check_for_string() + let pos = getpos('.') + try + let val = s:clojure_check_for_string_worker() + finally + call setpos('.', pos) + endtry + return val + endfunction + + function! s:strip_namespace_and_macro_chars(word) + return substitute(a:word, "\\v%(.*/|[#'`~@^,]*)(.*)", '\1', '') + endfunction + + function! s:clojure_is_method_special_case_worker(position) + " Find the next enclosing form. + call search('\S', 'Wb') + + " Special case: we are at a '(('. + if s:current_char() == '(' + return 0 + endif + call cursor(a:position) + + let next_paren = s:match_pairs('(', ')', 0) + + " Special case: we are now at toplevel. + if next_paren == [0, 0] + return 0 + endif + call cursor(next_paren) + + call search('\S', 'W') + let w = s:strip_namespace_and_macro_chars(s:current_word()) + + if g:clojure_special_indent_words =~# '\V\<' . w . '\>' + + " `letfn` is a special-special-case. + if w ==# 'letfn' + " Earlier code left the cursor at: + " (letfn [...] ...) + " ^ + + " Search and get coordinates of first `[` + " (letfn [...] ...) + " ^ + call search('\[', 'W') + let pos = getcurpos() + let letfn_bracket = [pos[1], pos[2]] + + " Move cursor to start of the form this function was + " initially called on. Grab the coordinates of the + " closest outer `[`. + call cursor(a:position) + let outer_bracket = s:match_pairs('\[', '\]', 0) + + " If the located square brackets are not the same, + " don't use special-case formatting. + if outer_bracket != letfn_bracket + return 0 + endif + endif + + return 1 + endif + + return 0 + endfunction + + function! s:is_method_special_case(position) + let pos = getpos('.') + try + let val = s:clojure_is_method_special_case_worker(a:position) + finally + call setpos('.', pos) + endtry + return val + endfunction + + " Check if form is a reader conditional, that is, it is prefixed by #? + " or #?@ + function! s:is_reader_conditional_special_case(position) + return getline(a:position[0])[a:position[1] - 3 : a:position[1] - 2] == "#?" + \|| getline(a:position[0])[a:position[1] - 4 : a:position[1] - 2] == "#?@" + endfunction + + " Returns 1 for opening brackets, -1 for _anything else_. + function! s:bracket_type(char) + return stridx('([{', a:char) > -1 ? 1 : -1 + endfunction + + " Returns: [opening-bracket-lnum, indent] + function! s:clojure_indent_pos() + " Get rid of special case. + if line(".") == 1 + return [0, 0] + endif + + " We have to apply some heuristics here to figure out, whether to use + " normal lisp indenting or not. + let i = s:check_for_string() + if i > -1 + return [0, i + !!g:clojure_align_multiline_strings] + endif + + call cursor(0, 1) + + " Find the next enclosing [ or {. We can limit the second search + " to the line, where the [ was found. If no [ was there this is + " zero and we search for an enclosing {. + let paren = s:match_pairs('(', ')', 0) + let bracket = s:match_pairs('\[', '\]', paren[0]) + let curly = s:match_pairs('{', '}', bracket[0]) + + " In case the curly brace is on a line later then the [ or - in + " case they are on the same line - in a higher column, we take the + " curly indent. + if curly[0] > bracket[0] || curly[1] > bracket[1] + if curly[0] > paren[0] || curly[1] > paren[1] + return curly + endif + endif + + " If the curly was not chosen, we take the bracket indent - if + " there was one. + if bracket[0] > paren[0] || bracket[1] > paren[1] + return bracket + endif + + " There are neither { nor [ nor (, ie. we are at the toplevel. + if paren == [0, 0] + return paren + endif + + " Now we have to reimplement lispindent. This is surprisingly easy, as + " soon as one has access to syntax items. + " + " - Check whether we are in a special position after a word in + " g:clojure_special_indent_words. These are special cases. + " - Get the next keyword after the (. + " - If its first character is also a (, we have another sexp and align + " one column to the right of the unmatched (. + " - In case it is in lispwords, we indent the next line to the column of + " the ( + sw. + " - If not, we check whether it is last word in the line. In that case + " we again use ( + sw for indent. + " - In any other case we use the column of the end of the word + 2. + call cursor(paren) + + if s:is_method_special_case(paren) + return [paren[0], paren[1] + &shiftwidth - 1] + endif + + if s:is_reader_conditional_special_case(paren) + return paren + endif + + " In case we are at the last character, we use the paren position. + if col("$") - 1 == paren[1] + return paren + endif + + " In case after the paren is a whitespace, we search for the next word. + call cursor(0, col('.') + 1) + if s:current_char() == ' ' + call search('\v\S', 'W') + endif + + " If we moved to another line, there is no word after the (. We + " use the ( position for indent. + if line(".") > paren[0] + return paren + endif + + " We still have to check, whether the keyword starts with a (, [ or {. + " In that case we use the ( position for indent. + let w = s:current_word() + if s:bracket_type(w[0]) == 1 + return paren + endif + + " If the keyword begins with #, check if it is an anonymous + " function or set, in which case we indent by the shiftwidth + " (minus one if g:clojure_align_subforms = 1), or if it is + " ignored, in which case we use the ( position for indent. + if w[0] == "#" + " TODO: Handle #=() and other rare reader invocations? + if w[1] == '(' || w[1] == '{' + return [paren[0], paren[1] + (g:clojure_align_subforms ? 0 : &shiftwidth - 1)] + elseif w[1] == '_' + return paren + endif + endif + + " Test words without namespace qualifiers and leading reader macro + " metacharacters. + " + " e.g. clojure.core/defn and #'defn should both indent like defn. + let ww = s:strip_namespace_and_macro_chars(w) + + if &lispwords =~# '\V\<' . ww . '\>' + return [paren[0], paren[1] + &shiftwidth - 1] + endif + + if g:clojure_fuzzy_indent + \ && !s:match_one(g:clojure_fuzzy_indent_blacklist, ww) + \ && s:match_one(g:clojure_fuzzy_indent_patterns, ww) + return [paren[0], paren[1] + &shiftwidth - 1] + endif + + call search('\v\_s', 'cW') + call search('\v\S', 'W') + if paren[0] < line(".") + return [paren[0], paren[1] + (g:clojure_align_subforms ? 0 : &shiftwidth - 1)] + endif + + call search('\v\S', 'bW') + return [line('.'), col('.') + 1] + endfunction + + function! GetClojureIndent() + let lnum = line('.') + let orig_lnum = lnum + let orig_col = col('.') + let [opening_lnum, indent] = s:clojure_indent_pos() + + " Account for multibyte characters + if opening_lnum > 0 + let indent -= indent - virtcol([opening_lnum, indent]) + endif + + " Return if there are no previous lines to inherit from + if opening_lnum < 1 || opening_lnum >= lnum - 1 + call cursor(orig_lnum, orig_col) + return indent + endif + + let bracket_count = 0 + + " Take the indent of the first previous non-white line that is + " at the same sexp level. cf. src/misc1.c:get_lisp_indent() + while 1 + let lnum = prevnonblank(lnum - 1) + let col = 1 + + if lnum <= opening_lnum + break + endif + + call cursor(lnum, col) + + " Handle bracket counting edge case + if s:is_paren() + let bracket_count += s:bracket_type(s:current_char()) + endif + + while 1 + if search('\v[(\[{}\])]', '', lnum) < 1 + break + elseif !s:ignored_region() + let bracket_count += s:bracket_type(s:current_char()) + endif + endwhile + + if bracket_count == 0 + " Check if this is part of a multiline string + call cursor(lnum, 1) + if s:syn_id_name() !~? '\vstring|regex' + call cursor(orig_lnum, orig_col) + return indent(lnum) + endif + endif + endwhile + + call cursor(orig_lnum, orig_col) + return indent + endfunction + + setlocal indentexpr=GetClojureIndent() + +else + + " In case we have searchpairpos not available we fall back to + " normal lisp indenting. + setlocal indentexpr= + setlocal lisp + let b:undo_indent .= '| setlocal lisp<' + +endif + +let &cpo = s:save_cpo +unlet! s:save_cpo + +" vim:sts=8:sw=8:ts=8:noet diff --git a/runtime/indent/cmake.vim b/runtime/indent/cmake.vim new file mode 100644 index 0000000..c1aa3bf --- /dev/null +++ b/runtime/indent/cmake.vim @@ -0,0 +1,99 @@ +" Vim indent file +" Language: CMake (ft=cmake) +" Author: Andy Cedilnik <andy.cedilnik@kitware.com> +" Maintainer: Dimitri Merejkowsky <d.merej@gmail.com> +" Former Maintainer: Karthik Krishnan <karthik.krishnan@kitware.com> +" Last Change: 2023 Dec 12 +" +" License: The CMake license applies to this file. See +" https://cmake.org/licensing +" This implies that distribution with Vim is allowed + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=CMakeGetIndent(v:lnum) +setlocal indentkeys+==ENDIF(,ENDFOREACH(,ENDMACRO(,ELSE(,ELSEIF(,ENDWHILE( + +let b:undo_indent = "setl inde< indk<" + +" Only define the function once. +if exists("*CMakeGetIndent") + finish +endif +let s:keepcpo= &cpo +set cpo&vim + +fun! CMakeGetIndent(lnum) + let this_line = getline(a:lnum) + + " Find a non-blank line above the current line. + let lnum = a:lnum + let lnum = prevnonblank(lnum - 1) + let previous_line = getline(lnum) + + " Hit the start of the file, use zero indent. + if lnum == 0 + return 0 + endif + + let ind = indent(lnum) + + let or = '\|' + " Regular expressions used by line indentation function. + let cmake_regex_comment = '#.*' + let cmake_regex_identifier = '[A-Za-z][A-Za-z0-9_]*' + let cmake_regex_quoted = '"\([^"\\]\|\\.\)*"' + let cmake_regex_arguments = '\(' . cmake_regex_quoted . + \ or . '\$(' . cmake_regex_identifier . ')' . + \ or . '[^()\\#"]' . or . '\\.' . '\)*' + + let cmake_indent_comment_line = '^\s*' . cmake_regex_comment + let cmake_indent_blank_regex = '^\s*$' + let cmake_indent_open_regex = '^\s*' . cmake_regex_identifier . + \ '\s*(' . cmake_regex_arguments . + \ '\(' . cmake_regex_comment . '\)\?$' + let cmake_indent_close_regex = '^' . cmake_regex_arguments . + \ ')\s*' . + \ '\(' . cmake_regex_comment . '\)\?$' + + let cmake_closing_parens_line = '^\s*\()\+\)\s*$' + + let cmake_indent_begin_regex = '^\s*\(BLOCK\|IF\|MACRO\|FOREACH\|ELSE\|ELSEIF\|WHILE\|FUNCTION\)\s*(' + let cmake_indent_end_regex = '^\s*\(ENDBLOCK\|ENDIF\|ENDFOREACH\|ENDMACRO\|ELSE\|ELSEIF\|ENDWHILE\|ENDFUNCTION\)\s*(' + + if this_line =~? cmake_closing_parens_line + if previous_line !~? cmake_indent_open_regex + let ind = ind - shiftwidth() + endif + else + " Add + if previous_line =~? cmake_indent_comment_line " Handle comments + let ind = ind + else + if previous_line =~? cmake_indent_begin_regex + let ind = ind + shiftwidth() + endif + if previous_line =~? cmake_indent_open_regex + let ind = ind + shiftwidth() + endif + endif + + " Subtract + if this_line =~? cmake_indent_end_regex + let ind = ind - shiftwidth() + endif + if previous_line !~? cmake_closing_parens_line + if previous_line =~? cmake_indent_close_regex + let ind = ind - shiftwidth() + endif + endif + endif + + return ind +endfun + +let &cpo = s:keepcpo +unlet s:keepcpo diff --git a/runtime/indent/cobol.vim b/runtime/indent/cobol.vim new file mode 100644 index 0000000..01f7212 --- /dev/null +++ b/runtime/indent/cobol.vim @@ -0,0 +1,226 @@ +" Vim indent file +" Language: cobol +" Maintainer: Ankit Jain <ajatkj@yahoo.co.in> +" (formerly Tim Pope <vimNOSPAM@tpope.info>) +" $Id: cobol.vim,v 1.1 2007/05/05 18:08:19 vimboss Exp $ +" Last Update: By Ankit Jain on 22.03.2019 +" Ankit Jain 22.03.2019 Changes & fixes: +" Allow chars in 1st 6 columns +" #C22032019 +" Ankit Jain 24.09.2021 add b:undo_indent (request by tpope) + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal expandtab +setlocal indentexpr=GetCobolIndent(v:lnum) +setlocal indentkeys& +setlocal indentkeys+=0<*>,0/,0$,0=01,=~division,=~section,0=~end,0=~then,0=~else,0=~when,*<Return>,. + +let b:undo_indent = "setlocal expandtab< indentexpr< indentkeys<" + +" Only define the function once. +if exists("*GetCobolIndent") + finish +endif + +let s:skip = 'getline(".") =~ "^.\\{6\\}[*/$-]\\|\"[^\"]*\""' + +function! s:prevgood(lnum) + " Find a non-blank line above the current line. + " Skip over comments. + let lnum = a:lnum + while lnum > 0 + let lnum = prevnonblank(lnum - 1) + let line = getline(lnum) + if line !~? '^\s*[*/$-]' && line !~? '^.\{6\}[*/$CD-]' + break + endif + endwhile + return lnum +endfunction + +function! s:stripped(lnum) + return substitute(strpart(getline(a:lnum),0,72),'^\s*','','') +endfunction + +function! s:optionalblock(lnum,ind,blocks,clauses) + let ind = a:ind + let clauses = '\c\<\%(\<NOT\s\+\)\@<!\%(NOT\s\+\)\=\%('.a:clauses.'\)' + let begin = '\c-\@<!\<\%('.a:blocks.'\)\>' + let beginfull = begin.'\ze.*\%(\n\%(\s*\%([*/$-].*\)\=\n\)*\)\=\s*\%('.clauses.'\)' + let end = '\c\<end-\%('.a:blocks.'\)\>\|\%(\.\%( \|$\)\)\@=' + let cline = s:stripped(a:lnum) + let line = s:stripped(s:prevgood(a:lnum)) + if cline =~? clauses "&& line !~? '^search\>' + call cursor(a:lnum,1) + let lastclause = searchpair(beginfull,clauses,end,'bWr',s:skip) + if getline(lastclause) =~? clauses && s:stripped(lastclause) !~? '^'.begin + let ind = indent(lastclause) + elseif lastclause > 0 + let ind = indent(lastclause) + shiftwidth() + "let ind = ind + shiftwidth() + endif + elseif line =~? clauses && cline !~? end + let ind = ind + shiftwidth() + endif + return ind +endfunction + +function! GetCobolIndent(lnum) abort + let minshft = 6 + let ashft = minshft + 1 + let bshft = ashft + 4 + " (Obsolete) numbered lines + " #C22032019: Columns 1-6 could have alphabets as well as numbers + "if getline(a:lnum) =~? '^\s*\d\{6\}\%($\|[ */$CD-]\)' + if getline(a:lnum) =~? '^\s*[a-zA-Z0-9]\{6\}\%($\|[ */$CD-]\)' + return 0 + endif + let cline = s:stripped(a:lnum) + " Comments, etc. must start in the 7th column + if cline =~? '^[*/$-]' + return minshft + elseif cline =~# '^[CD]' && indent(a:lnum) == minshft + return minshft + endif + " Divisions, sections, and file descriptions start in area A + if cline =~? '\<\(DIVISION\|SECTION\)\%($\|\.\)' || cline =~? '^[FS]D\>' + return ashft + endif + " Fields + if cline =~? '^0*\(1\|77\)\>' + return ashft + endif + if cline =~? '^\d\+\>' + let cnum = matchstr(cline,'^\d\+\>') + let default = 0 + let step = -1 + while step < 2 + let lnum = a:lnum + while lnum > 0 && lnum < line('$') && lnum > a:lnum - 500 && lnum < a:lnum + 500 + let lnum = step > 0 ? nextnonblank(lnum + step) : prevnonblank(lnum + step) + let line = getline(lnum) + let lindent = indent(lnum) + if line =~? '^\s*\d\+\>' + let num = matchstr(line,'^\s*\zs\d\+\>') + if 0+cnum == num + return lindent + elseif 0+cnum > num && default < lindent + shiftwidth() + let default = lindent + shiftwidth() + endif + elseif lindent < bshft && lindent >= ashft + break + endif + endwhile + let step = step + 2 + endwhile + return default ? default : bshft + endif + let lnum = s:prevgood(a:lnum) + " Hit the start of the file, use "zero" indent. + if lnum == 0 + return ashft + endif + " Initial spaces are ignored + let line = s:stripped(lnum) + let ind = indent(lnum) + " Paragraphs. There may be some false positives. + if cline =~? '^\(\a[A-Z0-9-]*[A-Z0-9]\|\d[A-Z0-9-]*\a\)\.' "\s*$' + if cline !~? '^EXIT\s*\.' && line =~? '\.\s*$' + return ashft + endif + endif + " Paragraphs in the identification division. + "if cline =~? '^\(PROGRAM-ID\|AUTHOR\|INSTALLATION\|' . + "\ 'DATE-WRITTEN\|DATE-COMPILED\|SECURITY\)\>' + "return ashft + "endif + if line =~? '\.$' + " XXX + return bshft + endif + if line =~? '^PERFORM\>' + let perfline = substitute(line, '\c^PERFORM\s*', "", "") + if perfline =~? '^\%(\k\+\s\+TIMES\)\=\s*$' + let ind = ind + shiftwidth() + elseif perfline =~? '^\%(WITH\s\+TEST\|VARYING\|UNTIL\)\>.*[^.]$' + let ind = ind + shiftwidth() + endif + endif + if line =~? '^\%(IF\|THEN\|ELSE\|READ\|EVALUATE\|SEARCH\|SELECT\)\>' + let ind = ind + shiftwidth() + endif + let ind = s:optionalblock(a:lnum,ind,'ADD\|COMPUTE\|DIVIDE\|MULTIPLY\|SUBTRACT','ON\s\+SIZE\s\+ERROR') + let ind = s:optionalblock(a:lnum,ind,'STRING\|UNSTRING\|ACCEPT\|DISPLAY\|CALL','ON\s\+OVERFLOW\|ON\s\+EXCEPTION') + if cline !~? '^AT\s\+END\>' || line !~? '^SEARCH\>' + let ind = s:optionalblock(a:lnum,ind,'DELETE\|REWRITE\|START\|WRITE\|READ','INVALID\s\+KEY\|AT\s\+END\|NO\s\+DATA\|AT\s\+END-OF-PAGE') + endif + if cline =~? '^WHEN\>' + call cursor(a:lnum,1) + " We also search for READ so that contained AT ENDs are skipped + let lastclause = searchpair('\c-\@<!\<\%(SEARCH\|EVALUATE\|READ\)\>','\c\<\%(WHEN\|AT\s\+END\)\>','\c\<END-\%(SEARCH\|EVALUATE\|READ\)\>','bW',s:skip) + let g:foo = s:stripped(lastclause) + if s:stripped(lastclause) =~? '\c\<\%(WHEN\|AT\s\+END\)\>' + "&& s:stripped(lastclause) !~? '^\%(SEARCH\|EVALUATE\|READ\)\>' + let ind = indent(lastclause) + elseif lastclause > 0 + let ind = indent(lastclause) + shiftwidth() + endif + elseif line =~? '^WHEN\>' + let ind = ind + shiftwidth() + endif + "I'm not sure why I had this + "if line =~? '^ELSE\>-\@!' && line !~? '\.$' + "let ind = indent(s:prevgood(lnum)) + "endif + if cline =~? '^\(END\)\>-\@!' + " On lines with just END, 'guess' a simple shift left + let ind = ind - shiftwidth() + elseif cline =~? '^\(END-IF\|THEN\|ELSE\)\>-\@!' + call cursor(a:lnum,indent(a:lnum)) + let match = searchpair('\c-\@<!\<IF\>','\c-\@<!\%(THEN\|ELSE\)\>','\c-\@<!\<END-IF\>\zs','bnW',s:skip) + if match > 0 + let ind = indent(match) + endif + elseif cline =~? '^END-[A-Z]' + let beginword = matchstr(cline,'\c\<END-\zs[A-Z0-9-]\+') + let endword = 'END-'.beginword + let first = 0 + let suffix = '.*\%(\n\%(\%(\s*\|.\{6\}\)[*/].*\n\)*\)\=\s*' + if beginword =~? '^\%(ADD\|COMPUTE\|DIVIDE\|MULTIPLY\|SUBTRACT\)$' + let beginword = beginword . suffix . '\<\%(NOT\s\+\)\=ON\s\+SIZE\s\+ERROR' + let g:beginword = beginword + let first = 1 + elseif beginword =~? '^\%(STRING\|UNSTRING\)$' + let beginword = beginword . suffix . '\<\%(NOT\s\+\)\=ON\s\+OVERFLOW' + let first = 1 + elseif beginword =~? '^\%(ACCEPT\|DISPLAY\)$' + let beginword = beginword . suffix . '\<\%(NOT\s\+\)\=ON\s\+EXCEPTION' + let first = 1 + elseif beginword ==? 'CALL' + let beginword = beginword . suffix . '\<\%(NOT\s\+\)\=ON\s\+\%(EXCEPTION\|OVERFLOW\)' + let first = 1 + elseif beginword =~? '^\%(DELETE\|REWRITE\|START\|READ\|WRITE\)$' + let first = 1 + let beginword = beginword . suffix . '\<\%(NOT\s\+\)\=\(INVALID\s\+KEY' + if beginword =~? '^READ' + let first = 0 + let beginword = beginword . '\|AT\s\+END\|NO\s\+DATA' + elseif beginword =~? '^WRITE' + let beginword = beginword . '\|AT\s\+END-OF-PAGE' + endif + let beginword = beginword . '\)' + endif + call cursor(a:lnum,indent(a:lnum)) + let match = searchpair('\c-\@<!\<'.beginword.'\>','','\c\<'.endword.'\>\zs','bnW'.(first? 'r' : ''),s:skip) + if match > 0 + let ind = indent(match) + elseif cline =~? '^\(END-\(READ\|EVALUATE\|SEARCH\|PERFORM\)\)\>' + let ind = ind - shiftwidth() + endif + endif + return ind < bshft ? bshft : ind +endfunction diff --git a/runtime/indent/config.vim b/runtime/indent/config.vim new file mode 100644 index 0000000..b840b1e --- /dev/null +++ b/runtime/indent/config.vim @@ -0,0 +1,85 @@ +" Vim indent file +" Language: Autoconf configure.{ac,in} file +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Last Change: 24 Sep 2021 + +" TODO: how about nested [()]'s in one line what's wrong with '\\\@!'? + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +runtime! indent/sh.vim " will set b:did_indent + +setlocal indentexpr=GetConfigIndent() +setlocal indentkeys=!^F,o,O,=then,=do,=else,=elif,=esac,=fi,=fin,=fil,=done +setlocal nosmartindent + +let b:undo_indent = "setl inde< indk< si<" + +" Only define the function once. +if exists("*GetConfigIndent") + finish +endif + +" get the offset (indent) of the end of the match of 'regexp' in 'line' +function s:GetOffsetOf(line, regexp) + let end = matchend(a:line, a:regexp) + let width = 0 + let i = 0 + while i < end + if a:line[i] != "\t" + let width = width + 1 + else + let width = width + &ts - (width % &ts) + endif + let i = i + 1 + endwhile + return width +endfunction + +function GetConfigIndent() + " Find a non-blank line above the current line. + let lnum = prevnonblank(v:lnum - 1) + + " Hit the start of the file, use zero indent. + if lnum == 0 + return 0 + endif + + " where to put this + let ind = GetShIndent() + let line = getline(lnum) + + " if previous line has unmatched, unescaped opening parentheses, + " indent to its position. TODO: not failsafe if multiple ('s + if line =~ '\\\@<!([^)]*$' + let ind = s:GetOffsetOf(line, '\\\@!(') + endif + + " if previous line has unmatched opening bracket, + " indent to its position. TODO: same as above + if line =~ '\[[^]]*$' + let ind = s:GetOffsetOf(line, '\[') + endif + + " if previous line had an unmatched closing parentheses, + " indent to the matching opening parentheses + if line =~ '[^(]\+\\\@<!)$' + call search(')', 'bW') + let lnum = searchpair('\\\@<!(', '', ')', 'bWn') + let ind = indent(lnum) + endif + + " if previous line had an unmatched closing bracket, + " indent to the matching opening bracket + if line =~ '[^[]\+]$' + call search(']', 'bW') + let lnum = searchpair('\[', '', ']', 'bWn') + let ind = indent(lnum) + endif + + return ind +endfunction diff --git a/runtime/indent/context.vim b/runtime/indent/context.vim new file mode 100644 index 0000000..9656151 --- /dev/null +++ b/runtime/indent/context.vim @@ -0,0 +1,65 @@ +vim9script + +# Language: ConTeXt typesetting engine +# Maintainer: Nicola Vitacolonna <nvitacolonna@gmail.com> +# Former Maintainers: Nikolai Weibull <now@bitwi.se> +# Latest Revision: 2023 Dec 26 + +if exists("b:did_indent") + finish +endif + +# Load MetaPost indentation script (this will also set b:did_indent) +runtime! indent/mp.vim + +setlocal indentexpr=ConTeXtIndent() + +b:undo_indent = "setl indentexpr<" + +def PrevNotComment(l: number): number + var prevlnum = prevnonblank(l) + + while prevlnum > 0 && getline(prevlnum) =~# '^\s*%' + prevlnum = prevnonblank(prevlnum - 1) + endwhile + + return prevlnum +enddef + +def FindPair(pstart: string, pmid: string, pend: string): number + cursor(v:lnum, 1) + return indent(searchpair(pstart, pmid, pend, 'bWn', + 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\\|comment"')) +enddef + +def ConTeXtIndent(): number + # Use MetaPost rules inside MetaPost graphic environments + if len(synstack(v:lnum, 1)) > 0 && + synIDattr(synstack(v:lnum, 1)[0], "name") ==# 'contextMPGraphic' + return g:MetaPostIndent() + endif + + const prevlnum = PrevNotComment(v:lnum - 1) + const prevind = indent(prevlnum) + const prevline = getline(prevlnum) + const currline = getline(v:lnum) + + # If the current line starts with ], match indentation. + if currline =~# '^\s*\]' + return FindPair('\[', '', '\]') + endif + + # If the current line starts with }, match indentation. + if currline =~# '^\s*}' + return FindPair('{', '', '}') + endif + + # If the previous line ends with [ or { (possibly followed by a comment) then indent. + if prevline =~# '[{[]\s*\%(%.*\)\=$' + return prevind + shiftwidth() + endif + + return -1 +enddef + +# vim: sw=2 fdm=marker diff --git a/runtime/indent/cpp.vim b/runtime/indent/cpp.vim new file mode 100644 index 0000000..bb4dfd1 --- /dev/null +++ b/runtime/indent/cpp.vim @@ -0,0 +1,16 @@ +" Vim indent file +" Language: C++ +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Last Change: 2023 Aug 10 +" Former Maintainer: Bram Moolenaar <Bram@vim.org> + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" C++ indenting is built-in, thus this is very simple +setlocal cindent + +let b:undo_indent = "setl cin<" diff --git a/runtime/indent/cs.vim b/runtime/indent/cs.vim new file mode 100644 index 0000000..acc3ba7 --- /dev/null +++ b/runtime/indent/cs.vim @@ -0,0 +1,75 @@ +" Vim indent file +" Language: C# +" Maintainer: Nick Jensen <nickspoon@gmail.com> +" Former Maintainers: Aquila Deus +" Johannes Zellner <johannes@zellner.org> +" Last Change: 2020-03-26 +" License: Vim (see :h license) +" Repository: https://github.com/nickspoons/vim-cs + +if exists('b:did_indent') + finish +endif +let b:did_indent = 1 + +let s:save_cpo = &cpoptions +set cpoptions&vim + + +setlocal indentexpr=GetCSIndent(v:lnum) + +function! s:IsCompilerDirective(line) + " Exclude #region and #endregion - these should be indented normally + return a:line =~# '^\s*#' && !s:IsRegionDirective(a:line) +endf + +function! s:IsRegionDirective(line) + return a:line =~# '^\s*#\s*region' || a:line =~# '^\s*#\s*endregion' +endf + +function! s:IsAttributeLine(line) + return a:line =~# '^\s*\[[A-Za-z]' && a:line =~# '\]$' +endf + +function! s:FindPreviousNonCompilerDirectiveLine(start_lnum) + for delta in range(0, a:start_lnum) + let lnum = a:start_lnum - delta + let line = getline(lnum) + if !s:IsCompilerDirective(line) && !s:IsRegionDirective(line) + return lnum + endif + endfor + return 0 +endf + +function! GetCSIndent(lnum) abort + " Hit the start of the file, use zero indent. + if a:lnum == 0 + return 0 + endif + + let this_line = getline(a:lnum) + + " Compiler directives use zero indent if so configured. + let is_first_col_macro = s:IsCompilerDirective(this_line) && stridx(&l:cinkeys, '0#') >= 0 + if is_first_col_macro + return cindent(a:lnum) + endif + + let lnum = s:FindPreviousNonCompilerDirectiveLine(a:lnum - 1) + let previous_code_line = getline(lnum) + if s:IsAttributeLine(previous_code_line) + return indent(lnum) + elseif s:IsRegionDirective(this_line) + return cindent(lnum) + else + return cindent(a:lnum) + endif +endfunction + +let b:undo_indent = 'setlocal indentexpr<' + +let &cpoptions = s:save_cpo +unlet s:save_cpo + +" vim:et:sw=2:sts=2 diff --git a/runtime/indent/css.vim b/runtime/indent/css.vim new file mode 100644 index 0000000..793f058 --- /dev/null +++ b/runtime/indent/css.vim @@ -0,0 +1,86 @@ +" Vim indent file +" Language: CSS +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Last Change: 24 Sep 2021 + +" Use of shiftwidth() added by Oleg Zubchenko. + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetCSSIndent() +setlocal indentkeys=0{,0},!^F,o,O +setlocal nosmartindent + +let b:undo_indent = "setl inde< indk< si<" + +if exists("*GetCSSIndent") + finish +endif +let s:keepcpo= &cpo +set cpo&vim + +function s:prevnonblanknoncomment(lnum) + let lnum = a:lnum + while lnum > 1 + let lnum = prevnonblank(lnum) + let line = getline(lnum) + if line =~ '\*/' + while lnum > 1 && line !~ '/\*' + let lnum -= 1 + endwhile + if line =~ '^\s*/\*' + let lnum -= 1 + else + break + endif + else + break + endif + endwhile + return lnum +endfunction + +function s:count_braces(lnum, count_open) + let n_open = 0 + let n_close = 0 + let line = getline(a:lnum) + let pattern = '[{}]' + let i = match(line, pattern) + while i != -1 + if synIDattr(synID(a:lnum, i + 1, 0), 'name') !~ 'css\%(Comment\|StringQ\{1,2}\)' + if line[i] == '{' + let n_open += 1 + elseif line[i] == '}' + if n_open > 0 + let n_open -= 1 + else + let n_close += 1 + endif + endif + endif + let i = match(line, pattern, i + 1) + endwhile + return a:count_open ? n_open : n_close +endfunction + +function GetCSSIndent() + let line = getline(v:lnum) + if line =~ '^\s*\*' + return cindent(v:lnum) + endif + + let pnum = s:prevnonblanknoncomment(v:lnum - 1) + if pnum == 0 + return 0 + endif + + return indent(pnum) + s:count_braces(pnum, 1) * shiftwidth() + \ - s:count_braces(v:lnum, 0) * shiftwidth() +endfunction + +let &cpo = s:keepcpo +unlet s:keepcpo diff --git a/runtime/indent/cucumber.vim b/runtime/indent/cucumber.vim new file mode 100644 index 0000000..5d144e4 --- /dev/null +++ b/runtime/indent/cucumber.vim @@ -0,0 +1,98 @@ +" Vim indent file +" Language: Cucumber +" Maintainer: Tim Pope <vimNOSPAM@tpope.org> +" Last Change: 2023 Dec 28 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal autoindent +setlocal indentexpr=GetCucumberIndent() +setlocal indentkeys=o,O,*<Return>,<:>,0<Bar>,0#,=,!^F + +let b:undo_indent = 'setl ai< inde< indk<' + +" Only define the function once. +if exists("*GetCucumberIndent") + finish +endif + +let s:headings = { + \ 'Feature': 'feature', + \ 'Rule': 'rule', + \ 'Background': 'bg_or_scenario', + \ 'Scenario': 'bg_or_scenario', + \ 'ScenarioOutline': 'bg_or_scenario', + \ 'Examples': 'examples', + \ 'Scenarios': 'examples'} + +function! s:Line(lnum) abort + if getline(a:lnum) =~# ':' + let group = matchstr(synIDattr(synID(a:lnum,1+indent(a:lnum), 1), 'name'), '^cucumber\zs.*') + if !has_key(s:headings, group) + let group = substitute(matchstr(getline(a:lnum), '^\s*\zs\%([^:]\+\)\ze:\S\@!'), '\s\+', '', 'g') + endif + else + let group = '' + endif + let char = matchstr(getline(a:lnum), '^\s*\zs[[:punct:]]') + return { + \ 'lnum': a:lnum, + \ 'indent': indent(a:lnum), + \ 'heading': get(s:headings, group, ''), + \ 'tag': char ==# '@', + \ 'table': char ==# '|', + \ 'comment': char ==# '#', + \ } +endfunction + +function! GetCucumberIndent(...) abort + let lnum = a:0 ? a:1 : v:lnum + let sw = shiftwidth() + let prev = s:Line(prevnonblank(lnum-1)) + let curr = s:Line(lnum) + let next = s:Line(nextnonblank(lnum+1)) + if curr.heading ==# 'feature' + " feature heading + return 0 + elseif curr.heading ==# 'examples' + " examples heading + return 2 * sw + elseif curr.heading ==# 'bg_or_scenario' + " background, scenario or outline heading + return sw + elseif prev.heading ==# 'feature' + " line after feature heading + return sw + elseif prev.heading ==# 'examples' + " line after examples heading + return 3 * sw + elseif prev.heading ==# 'bg_or_scenario' + " line after background, scenario or outline heading + return 2 * sw + elseif (curr.tag || curr.comment) && (next.heading ==# 'feature' || prev.indent <= 0) + " tag or comment before a feature heading + return 0 + elseif curr.tag + " other tags + return sw + elseif (curr.table || curr.comment) && prev.table + " mid-table + " preserve indent + return prev.indent + elseif curr.table && !prev.table + " first line of a table, relative indent + return prev.indent + sw + elseif !curr.table && prev.table + " line after a table, relative unindent + return prev.indent - sw + elseif curr.comment && getline(v:lnum-1) =~# '^\s*$' && next.heading ==# 'bg_or_scenario' + " comments on scenarios + return sw + endif + return prev.indent < 0 ? 0 : prev.indent +endfunction + +" vim:set sts=2 sw=2: diff --git a/runtime/indent/cuda.vim b/runtime/indent/cuda.vim new file mode 100644 index 0000000..5980ddd --- /dev/null +++ b/runtime/indent/cuda.vim @@ -0,0 +1,16 @@ +" Vim indent file +" Language: CUDA +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Last Change: 2023 Aug 10 +" Former Maintainer: Bram Moolenaar <Bram@vim.org> + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" It's just like C indenting +setlocal cindent + +let b:undo_indent = "setl cin<" diff --git a/runtime/indent/d.vim b/runtime/indent/d.vim new file mode 100644 index 0000000..80c9a2f --- /dev/null +++ b/runtime/indent/d.vim @@ -0,0 +1,24 @@ +" Vim indent file for the D programming language (version 0.137). +" Language: D +" Maintainer: Jason Mills <jmills@cs.mun.ca> (Invalid email address) +" Doug Kearns <dougkearns@gmail.com> +" Last Change: 2022 Apr 06 +" Version: 0.1 +" +" Please email me with bugs, comments, and suggestion. Put vim in the subject +" to ensure the email will not be marked has spam. +" + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +let b:did_indent = 1 + +" D indenting is a lot like the built-in C indenting. +setlocal cindent + +let b:undo_indent = "setl cin<" + +" vim: ts=8 noet diff --git a/runtime/indent/dictconf.vim b/runtime/indent/dictconf.vim new file mode 100644 index 0000000..fa40585 --- /dev/null +++ b/runtime/indent/dictconf.vim @@ -0,0 +1,15 @@ +" Vim indent file +" Language: dict(1) configuration file +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Last Change: 2022 Apr 06 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentkeys=0{,0},!^F,o,O cinwords= autoindent smartindent +setlocal nosmartindent +inoremap <buffer> # X# + +let b:undo_indent = "setl ai< cinw< indk< si< | silent! iunmap <buffer> #" diff --git a/runtime/indent/dictdconf.vim b/runtime/indent/dictdconf.vim new file mode 100644 index 0000000..5c0e7c5 --- /dev/null +++ b/runtime/indent/dictdconf.vim @@ -0,0 +1,15 @@ +" Vim indent file +" Language: dictd(8) configuration file +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Latest Revision: 2006-12-20 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentkeys=0{,0},!^F,o,O cinwords= autoindent smartindent +setlocal nosmartindent +inoremap <buffer> # X# + +let b:undo_indent = "setl ai< cinw< indk< si< | silent! iunmap <buffer> #" diff --git a/runtime/indent/docbk.vim b/runtime/indent/docbk.vim new file mode 100644 index 0000000..d8661ff --- /dev/null +++ b/runtime/indent/docbk.vim @@ -0,0 +1,15 @@ +" Vim indent file +" Language: DocBook Documentation Format +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Latest Revision: 2006-04-19 + +if exists("b:did_indent") + finish +endif + +" Same as XML indenting for now. +runtime! indent/xml.vim + +if exists('*XmlIndentGet') + setlocal indentexpr=XmlIndentGet(v:lnum,0) +endif diff --git a/runtime/indent/dosbatch.vim b/runtime/indent/dosbatch.vim new file mode 100644 index 0000000..d24b139 --- /dev/null +++ b/runtime/indent/dosbatch.vim @@ -0,0 +1,61 @@ +" Vim indent file +" Language: MSDOS batch file (with NT command extensions) +" Maintainer: Ken Takata +" URL: https://github.com/k-takata/vim-dosbatch-indent +" Last Change: 2021-10-18 +" Filenames: *.bat +" License: VIM License + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal nosmartindent +setlocal noautoindent +setlocal indentexpr=GetDosBatchIndent(v:lnum) +setlocal indentkeys=!^F,o,O +setlocal indentkeys+=0=) + +let b:undo_indent = "setl ai< inde< indk< si<" + +if exists("*GetDosBatchIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +function! GetDosBatchIndent(lnum) + let l:prevlnum = prevnonblank(a:lnum-1) + if l:prevlnum == 0 + " top of file + return 0 + endif + + " grab the previous and current line, stripping comments. + let l:prevl = substitute(getline(l:prevlnum), '\c^\s*\%(@\s*\)\?rem\>.*$', '', '') + let l:thisl = getline(a:lnum) + let l:previ = indent(l:prevlnum) + + let l:ind = l:previ + + if l:prevl =~? '^\s*@\=if\>.*(\s*$' || + \ l:prevl =~? '\<do\>\s*(\s*$' || + \ l:prevl =~? '\<else\>\s*\%(if\>.*\)\?(\s*$' || + \ l:prevl =~? '^.*\(&&\|||\)\s*(\s*$' + " previous line opened a block + let l:ind += shiftwidth() + endif + if l:thisl =~ '^\s*)' + " this line closed a block + let l:ind -= shiftwidth() + endif + + return l:ind +endfunction + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim: ts=8 sw=2 sts=2 diff --git a/runtime/indent/dtd.vim b/runtime/indent/dtd.vim new file mode 100644 index 0000000..9fca296 --- /dev/null +++ b/runtime/indent/dtd.vim @@ -0,0 +1,334 @@ +" Vim indent file +" Language: DTD (Document Type Definition for XML) +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Last Change: 24 Sep 2021 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetDTDIndent() +setlocal indentkeys=!^F,o,O,> +setlocal nosmartindent + +let b:undo_indent = "setl inde< indk< si<" + +if exists("*GetDTDIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +" TODO: Needs to be adjusted to stop at [, <, and ]. +let s:token_pattern = '^[^[:space:]]\+' + +function s:lex1(input, start, ...) + let pattern = a:0 > 0 ? a:1 : s:token_pattern + let start = matchend(a:input, '^\_s*', a:start) + if start == -1 + return ["", a:start] + endif + let end = matchend(a:input, pattern, start) + if end == -1 + return ["", a:start] + endif + let token = strpart(a:input, start, end - start) + return [token, end] +endfunction + +function s:lex(input, start, ...) + let pattern = a:0 > 0 ? a:1 : s:token_pattern + let info = s:lex1(a:input, a:start, pattern) + while info[0] == '--' + let info = s:lex1(a:input, info[1], pattern) + while info[0] != "" && info[0] != '--' + let info = s:lex1(a:input, info[1], pattern) + endwhile + if info[0] == "" + return info + endif + let info = s:lex1(a:input, info[1], pattern) + endwhile + return info +endfunction + +function s:indent_to_innermost_parentheses(line, end) + let token = '(' + let end = a:end + let parentheses = [end - 1] + while token != "" + let [token, end] = s:lex(a:line, end, '^\%([(),|]\|[A-Za-z0-9_-]\+\|#P\=CDATA\|%[A-Za-z0-9_-]\+;\)[?*+]\=') + if token[0] == '(' + call add(parentheses, end - 1) + elseif token[0] == ')' + if len(parentheses) == 1 + return [-1, end] + endif + call remove(parentheses, -1) + endif + endwhile + return [parentheses[-1] - strridx(a:line, "\n", parentheses[-1]), end] +endfunction + +" TODO: Line and end could be script global (think OO members). +function GetDTDIndent() + if v:lnum == 1 + return 0 + endif + + " Begin by searching back for a <! that isn’t inside a comment. + " From here, depending on what follows immediately after, parse to + " where we’re at to determine what to do. + if search('<!', 'bceW') == 0 + return indent(v:lnum - 1) + endif + let lnum = line('.') + let col = col('.') + let indent = indent('.') + let line = lnum == v:lnum ? getline(lnum) : join(getline(lnum, v:lnum - 1), "\n") + + let [declaration, end] = s:lex1(line, col) + if declaration == "" + return indent + shiftwidth() + elseif declaration == '--' + " We’re looking at a comment. Now, simply determine if the comment is + " terminated or not. If it isn’t, let Vim take care of that using + " 'comments' and 'autoindent'. Otherwise, indent to the first lines level. + while declaration != "" + let [declaration, end] = s:lex(line, end) + if declaration == "-->" + return indent + endif + endwhile + return -1 + elseif declaration == 'ELEMENT' + " Check for element name. If none exists, indent one level. + let [name, end] = s:lex(line, end) + if name == "" + return indent + shiftwidth() + endif + + " Check for token following element name. This can be a specification of + " whether the start or end tag may be omitted. If nothing is found, indent + " one level. + let [token, end] = s:lex(line, end, '^\%([-O(]\|ANY\|EMPTY\)') + let n = 0 + while token =~ '[-O]' && n < 2 + let [token, end] = s:lex(line, end, '^\%([-O(]\|ANY\|EMPTY\)') + let n += 1 + endwhile + if token == "" + return indent + shiftwidth() + endif + + " Next comes the content model. If the token we’ve found isn’t a + " parenthesis it must be either ANY, EMPTY or some random junk. Either + " way, we’re done indenting this element, so set it to that of the first + " line so that the terminating “>” winds up having the same indentation. + if token != '(' + return indent + endif + + " Now go through the content model. We need to keep track of the nesting + " of parentheses. As soon as we hit 0 we’re done. If that happens we must + " have a complete content model. Thus set indentation to be the same as that + " of the first line so that the terminating “>” winds up having the same + " indentation. Otherwise, we’ll indent to the innermost parentheses not yet + " matched. + let [indent_of_innermost, end] = s:indent_to_innermost_parentheses(line, end) + if indent_of_innermost != -1 + return indent_of_innermost + endif + + " Finally, look for any additions and/or exceptions to the content model. + " This is defined by a “+” or “-” followed by another content model + " declaration. + " TODO: Can the “-” be separated by whitespace from the “(”? + let seen = { '+(': 0, '-(': 0 } + while 1 + let [additions_exceptions, end] = s:lex(line, end, '^[+-](') + if additions_exceptions != '+(' && additions_exceptions != '-(' + let [token, end] = s:lex(line, end) + if token == '>' + return indent + endif + " TODO: Should use s:lex here on getline(v:lnum) and check for >. + return getline(v:lnum) =~ '^\s*>' || count(values(seen), 0) == 0 ? indent : (indent + shiftwidth()) + endif + + " If we’ve seen an addition or exception already and this is of the same + " kind, the user is writing a broken DTD. Time to bail. + if seen[additions_exceptions] + return indent + endif + let seen[additions_exceptions] = 1 + + let [indent_of_innermost, end] = s:indent_to_innermost_parentheses(line, end) + if indent_of_innermost != -1 + return indent_of_innermost + endif + endwhile + elseif declaration == 'ATTLIST' + " Check for element name. If none exists, indent one level. + let [name, end] = s:lex(line, end) + if name == "" + return indent + shiftwidth() + endif + + " Check for any number of attributes. + while 1 + " Check for attribute name. If none exists, indent one level, unless the + " current line is a lone “>”, in which case we indent to the same level + " as the first line. Otherwise, if the attribute name is “>”, we have + " actually hit the end of the attribute list, in which case we indent to + " the same level as the first line. + let [name, end] = s:lex(line, end) + if name == "" + " TODO: Should use s:lex here on getline(v:lnum) and check for >. + return getline(v:lnum) =~ '^\s*>' ? indent : (indent + shiftwidth()) + elseif name == ">" + return indent + endif + + " Check for attribute value declaration. If none exists, indent two + " levels. Otherwise, if it’s an enumerated value, check for nested + " parentheses and indent to the innermost one if we don’t reach the end + " of the listc. Otherwise, just continue with looking for the default + " attribute value. + " TODO: Do validation of keywords + " (CDATA|NMTOKEN|NMTOKENS|ID|IDREF|IDREFS|ENTITY|ENTITIES)? + let [value, end] = s:lex(line, end, '^\%((\|[^[:space:]]\+\)') + if value == "" + return indent + shiftwidth() * 2 + elseif value == 'NOTATION' + " If this is a enumerated value based on notations, read another token + " for the actual value. If it doesn’t exist, indent three levels. + " TODO: If validating according to above, value must be equal to '('. + let [value, end] = s:lex(line, end, '^\%((\|[^[:space:]]\+\)') + if value == "" + return indent + shiftwidth() * 3 + endif + endif + + if value == '(' + let [indent_of_innermost, end] = s:indent_to_innermost_parentheses(line, end) + if indent_of_innermost != -1 + return indent_of_innermost + endif + endif + + " Finally look for the attribute’s default value. If non exists, indent + " two levels. + let [default, end] = s:lex(line, end, '^\%("\_[^"]*"\|#\(REQUIRED\|IMPLIED\|FIXED\)\)') + if default == "" + return indent + shiftwidth() * 2 + elseif default == '#FIXED' + " We need to look for the fixed value. If non exists, indent three + " levels. + let [default, end] = s:lex(line, end, '^"\_[^"]*"') + if default == "" + return indent + shiftwidth() * 3 + endif + endif + endwhile + elseif declaration == 'ENTITY' + " Check for entity name. If none exists, indent one level. Otherwise, if + " the name actually turns out to be a percent sign, “%”, this is a + " parameter entity. Read another token to determine the entity name and, + " again, if none exists, indent one level. + let [name, end] = s:lex(line, end) + if name == "" + return indent + shiftwidth() + elseif name == '%' + let [name, end] = s:lex(line, end) + if name == "" + return indent + shiftwidth() + endif + endif + + " Now check for the entity value. If none exists, indent one level. If it + " does exist, indent to same level as first line, as we’re now done with + " this entity. + " + " The entity value can be a string in single or double quotes (no escapes + " to worry about, as entities are used instead). However, it can also be + " that this is an external unparsed entity. In that case we have to look + " further for (possibly) a public ID and an URI followed by the NDATA + " keyword and the actual notation name. For the public ID and URI, indent + " two levels, if they don’t exist. If the NDATA keyword doesn’t exist, + " indent one level. Otherwise, if the actual notation name doesn’t exist, + " indent two level. If it does, indent to same level as first line, as + " we’re now done with this entity. + let [value, end] = s:lex(line, end) + if value == "" + return indent + shiftwidth() + elseif value == 'SYSTEM' || value == 'PUBLIC' + let [quoted_string, end] = s:lex(line, end, '\%("[^"]\+"\|''[^'']\+''\)') + if quoted_string == "" + return indent + shiftwidth() * 2 + endif + + if value == 'PUBLIC' + let [quoted_string, end] = s:lex(line, end, '\%("[^"]\+"\|''[^'']\+''\)') + if quoted_string == "" + return indent + shiftwidth() * 2 + endif + endif + + let [ndata, end] = s:lex(line, end) + if ndata == "" + return indent + shiftwidth() + endif + + let [name, end] = s:lex(line, end) + return name == "" ? (indent + shiftwidth() * 2) : indent + else + return indent + endif + elseif declaration == 'NOTATION' + " Check for notation name. If none exists, indent one level. + let [name, end] = s:lex(line, end) + if name == "" + return indent + shiftwidth() + endif + + " Now check for the external ID. If none exists, indent one level. + let [id, end] = s:lex(line, end) + if id == "" + return indent + shiftwidth() + elseif id == 'SYSTEM' || id == 'PUBLIC' + let [quoted_string, end] = s:lex(line, end, '\%("[^"]\+"\|''[^'']\+''\)') + if quoted_string == "" + return indent + shiftwidth() * 2 + endif + + if id == 'PUBLIC' + let [quoted_string, end] = s:lex(line, end, '\%("[^"]\+"\|''[^'']\+''\|>\)') + if quoted_string == "" + " TODO: Should use s:lex here on getline(v:lnum) and check for >. + return getline(v:lnum) =~ '^\s*>' ? indent : (indent + shiftwidth() * 2) + elseif quoted_string == '>' + return indent + endif + endif + endif + + return indent + endif + + " TODO: Processing directives could be indented I suppose. But perhaps it’s + " just as well to let the user decide how to indent them (perhaps extending + " this function to include proper support for whatever processing directive + " language they want to use). + + " Conditional sections are simply passed along to let Vim decide what to do + " (and hence the user). + return -1 +endfunction + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/indent/dtrace.vim b/runtime/indent/dtrace.vim new file mode 100644 index 0000000..e41d398 --- /dev/null +++ b/runtime/indent/dtrace.vim @@ -0,0 +1,17 @@ +" Vim indent file +" Language: D script as described in "Solaris Dynamic Tracing Guide", +" http://docs.sun.com/app/docs/doc/817-6223 +" Last Change: 2008/03/20 +" Version: 1.2 +" Maintainer: Nicolas Weber <nicolasweber@gmx.de> + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" Built-in C indenting works nicely for dtrace. +setlocal cindent + +let b:undo_indent = "setl cin<" diff --git a/runtime/indent/dts.vim b/runtime/indent/dts.vim new file mode 100644 index 0000000..e87f815 --- /dev/null +++ b/runtime/indent/dts.vim @@ -0,0 +1,63 @@ +" Vim indent file +" Language: Device Tree +" Maintainer: Roland Hieber, Pengutronix <rhi@pengutronix.de> +" +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal autoindent +setlocal nosmartindent +setlocal indentkeys=o,O,0},0<>>,!<Ctrl-F> +setlocal indentexpr=GetDTSIndent() +setlocal nolisp + +let b:undo_indent = 'setl autoindent< smartindent< indentkeys< indentexpr< lisp<' + +function GetDTSIndent() + let sw = shiftwidth() + let lnum = v:lnum + let line = getline(lnum) + let prevline = getline(prevnonblank(lnum-1)) + let prevind = indent(prevnonblank(lnum-1)) + + if prevnonblank(lnum-1) < 1 + return 0 + endif + + " Don't indent header and preprocessor directives + if line =~ '^\s*\(/dts-\|#\(include\|define\|undef\|warn\(ing\)\?\|error\|if\(n\?def\)\?\|else\|elif\|endif\)\)' + return 0 + + " Don't indent /node and &label blocks + elseif line =~ '^\s*[/&].\+{\s*$' + return 0 + + " Indent to matching bracket or remove one shiftwidth if line begins with } or > + elseif line =~ '^\s*[}>]' + " set cursor to closing bracket on current line + let col = matchend(line, '^\s*[>}]') + call cursor(lnum, col) + + " determine bracket type, {} or <> + let pair = strpart('{}<>', stridx('}>', line[col-1]) * 2, 2) + + " find matching bracket pair + let pairline = searchpair(pair[0], '', pair[1], 'bW') + + if pairline > 0 + return indent(pairline) + else + return prevind - sw + endif + + " else, add one level of indent if line ends in { or < or = or , + elseif prevline =~ '[{<=,]$' + return prevind + sw + + else + return prevind + endif + +endfunction diff --git a/runtime/indent/dune.vim b/runtime/indent/dune.vim new file mode 100644 index 0000000..a9349e4 --- /dev/null +++ b/runtime/indent/dune.vim @@ -0,0 +1,16 @@ +" Vim indent file +" Language: dune +" Maintainers: Markus Mottl <markus.mottl@gmail.com> +" URL: https://github.com/ocaml/vim-ocaml +" Last Change: 2021 Jan 01 +" 2023 Aug 28 by Vim Project (undo_indent) + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" dune format-dune-file uses 1 space to indent +setlocal softtabstop=1 shiftwidth=1 expandtab + +let b:undo_indent = "setl et< sts< sw<" diff --git a/runtime/indent/dylan.vim b/runtime/indent/dylan.vim new file mode 100644 index 0000000..e2a6d10 --- /dev/null +++ b/runtime/indent/dylan.vim @@ -0,0 +1,94 @@ +" Vim indent file +" Language: Dylan +" Maintainer: Brent A. Fulgham <bfulgham@debian.org> (Invalid email address) +" Doug Kearns <dougkearns@gmail.com> +" Version: 0.01 +" Last Change: 2022 Apr 06 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentkeys+==~begin,=~block,=~case,=~cleanup,=~define,=~end,=~else,=~elseif,=~exception,=~for,=~finally,=~if,=~otherwise,=~select,=~unless,=~while + +" Define the appropriate indent function but only once +setlocal indentexpr=DylanGetIndent() + +let b:undo_indent = "setl inde< indk<" + +if exists("*DylanGetIndent") + finish +endif + +function DylanGetIndent() + " Get the line to be indented + let cline = getline(v:lnum) + + " Don't reindent comments on first column + if cline =~ '^/\[/\*]' + return 0 + endif + + "Find the previous non-blank line + let lnum = prevnonblank(v:lnum - 1) + "Use zero indent at the top of the file + if lnum == 0 + return 0 + endif + + let prevline=getline(lnum) + let ind = indent(lnum) + let chg = 0 + + " If previous line was a comment, use its indent + if prevline =~ '^\s*//' + return ind + endif + + " If previous line was a 'define', indent + if prevline =~? '\(^\s*\(begin\|block\|case\|define\|else\|elseif\|for\|finally\|if\|select\|unless\|while\)\|\s*\S*\s*=>$\)' + let chg = shiftwidth() + " local methods indent the shift-width, plus 6 for the 'local' + elseif prevline =~? '^\s*local' + let chg = shiftwidth() + 6 + " If previous line was a let with no closing semicolon, indent + elseif prevline =~? '^\s*let.*[^;]\s*$' + let chg = shiftwidth() + " If previous line opened a parenthesis, and did not close it, indent + elseif prevline =~ '^.*(\s*[^)]*\((.*)\)*[^)]*$' + return = match( prevline, '(.*\((.*)\|[^)]\)*.*$') + 1 + "elseif prevline =~ '^.*(\s*[^)]*\((.*)\)*[^)]*$' + elseif prevline =~ '^[^(]*)\s*$' + " This line closes a parenthesis. Find opening + let curr_line = prevnonblank(lnum - 1) + while curr_line >= 0 + let str = getline(curr_line) + if str !~ '^.*(\s*[^)]*\((.*)\)*[^)]*$' + let curr_line = prevnonblank(curr_line - 1) + else + break + endif + endwhile + if curr_line < 0 + return -1 + endif + let ind = indent(curr_line) + " Although we found the closing parenthesis, make sure this + " line doesn't start with an indentable command: + let curr_str = getline(curr_line) + if curr_str =~? '^\s*\(begin\|block\|case\|define\|else\|elseif\|for\|finally\|if\|select\|unless\|while\)' + let chg = shiftwidth() + endif + endif + + " If a line starts with end, un-indent (even if we just indented!) + if cline =~? '^\s*\(cleanup\|end\|else\|elseif\|exception\|finally\|otherwise\)' + let chg = chg - shiftwidth() + endif + + return ind + chg +endfunction + +" vim:sw=2 tw=130 diff --git a/runtime/indent/eiffel.vim b/runtime/indent/eiffel.vim new file mode 100644 index 0000000..d7667a8 --- /dev/null +++ b/runtime/indent/eiffel.vim @@ -0,0 +1,115 @@ +" Vim indent file +" Language: Eiffel +" Maintainer: Jocelyn Fiat <jfiat@eiffel.com> +" Previous-Maintainer: David Clarke <gadicath@dishevelled.net> +" Contributions from: Takuya Fujiwara +" Contributions from: Thilo Six +" $Date: 2017/03/08 06:00:00 $ +" $Revision: 1.4 $ +" URL: https://github.com/eiffelhub/vim-eiffel + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetEiffelIndent() +setlocal nolisp +setlocal nosmartindent +setlocal nocindent +setlocal autoindent +setlocal comments=:-- +setlocal indentkeys+==end,=else,=ensure,=require,=check,=loop,=until +setlocal indentkeys+==creation,=feature,=inherit,=class,=is,=redefine,=rename,=variant +setlocal indentkeys+==invariant,=do,=local,=export + +let b:undo_indent = "setl smartindent< indentkeys< indentexpr< autoindent< comments< " + +" Define some stuff +" keywords grouped by indenting +let s:trust_user_indent = '\(+\)\(\s*\(--\).*\)\=$' +let s:relative_indent = '^\s*\(deferred\|class\|feature\|creation\|inherit\|loop\|from\|across\|until\|if\|else\|elseif\|ensure\|require\|check\|do\|local\|invariant\|variant\|rename\|redefine\|do\|export\)\>' +let s:outdent = '^\s*\(else\|invariant\|variant\|do\|require\|until\|loop\|local\)\>' +let s:no_indent = '^\s*\(class\|feature\|creation\|inherit\)\>' +let s:single_dent = '^[^-]\+[[:alnum:]]\+ is\(\s*\(--\).*\)\=$' +let s:inheritance_dent = '\s*\(redefine\|rename\|export\)\>' + + +" Only define the function once. +if exists("*GetEiffelIndent") + finish +endif + +let s:keepcpo= &cpo +set cpo&vim + +function GetEiffelIndent() + + " Eiffel Class indenting + " + " Find a non-blank line above the current line. + let lnum = prevnonblank(v:lnum - 1) + + " At the start of the file use zero indent. + if lnum == 0 + return 0 + endif + + " trust the user's indenting + if getline(lnum) =~ s:trust_user_indent + return -1 + endif + + " Add a 'shiftwidth' after lines that start with an indent word + let ind = indent(lnum) + if getline(lnum) =~ s:relative_indent + let ind = ind + shiftwidth() + endif + + " Indent to single indent + if getline(v:lnum) =~ s:single_dent && getline(v:lnum) !~ s:relative_indent + \ && getline(v:lnum) !~ '\s*\<\(and\|or\|implies\)\>' + let ind = shiftwidth() + endif + + " Indent to double indent + if getline(v:lnum) =~ s:inheritance_dent + let ind = 2 * shiftwidth() + endif + + " Indent line after the first line of the function definition + if getline(lnum) =~ s:single_dent + let ind = ind + shiftwidth() + endif + + " The following should always be at the start of a line, no indenting + if getline(v:lnum) =~ s:no_indent + let ind = 0 + endif + + " Subtract a 'shiftwidth', if this isn't the first thing after the 'is' + " or first thing after the 'do' + if getline(v:lnum) =~ s:outdent && getline(v:lnum - 1) !~ s:single_dent + \ && getline(v:lnum - 1) !~ '^\s*do\>' + let ind = ind - shiftwidth() + endif + + " Subtract a shiftwidth for end statements + if getline(v:lnum) =~ '^\s*end\>' + let ind = ind - shiftwidth() + endif + + " set indent of zero end statements that are at an indent of 3, this should + " only ever be the class's end. + if getline(v:lnum) =~ '^\s*end\>' && ind == shiftwidth() + let ind = 0 + endif + + return ind +endfunction + +let &cpo = s:keepcpo +unlet s:keepcpo + +" vim:sw=2 diff --git a/runtime/indent/elm.vim b/runtime/indent/elm.vim new file mode 100644 index 0000000..7b08de7 --- /dev/null +++ b/runtime/indent/elm.vim @@ -0,0 +1,116 @@ +" Elm indent plugin file +" Language: Elm +" Maintainer: Andreas Scharf <as@99n.de> +" Original Author: Joseph Hager <ajhager@gmail.com> +" Copyright: Joseph Hager <ajhager@gmail.com> +" License: BSD3 +" Latest Revision: 2021-09-29 + +" Only load this indent file when no other was loaded. +if exists('b:did_indent') + finish +endif +let b:did_indent = 1 + +" Local defaults +setlocal expandtab +setlocal indentexpr=GetElmIndent() +setlocal indentkeys+=0=else,0=if,0=of,0=import,0=then,0=type,0\|,0},0\],0),=-},0=in +setlocal nolisp +setlocal nosmartindent + +let b:undo_indent = "setl et< inde< indk< lisp< si<" + +" Only define the function once. +if exists('*GetElmIndent') + finish +endif + +" Indent pairs +function! s:FindPair(pstart, pmid, pend) + "call search(a:pend, 'bW') + return indent(searchpair(a:pstart, a:pmid, a:pend, 'bWn', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\\|comment"')) +endfunction + +function! GetElmIndent() + let l:lnum = v:lnum - 1 + + " Ident 0 if the first line of the file: + if l:lnum == 0 + return 0 + endif + + let l:ind = indent(l:lnum) + let l:lline = getline(l:lnum) + let l:line = getline(v:lnum) + + " Indent if current line begins with '}': + if l:line =~? '^\s*}' + return s:FindPair('{', '', '}') + + " Indent if current line begins with 'else': + elseif l:line =~# '^\s*else\>' + if l:lline !~# '^\s*\(if\|then\)\>' + return s:FindPair('\<if\>', '', '\<else\>') + endif + + " Indent if current line begins with 'then': + elseif l:line =~# '^\s*then\>' + if l:lline !~# '^\s*\(if\|else\)\>' + return s:FindPair('\<if\>', '', '\<then\>') + endif + + " HACK: Indent lines in case with nearest case clause: + elseif l:line =~# '->' && l:line !~# ':' && l:line !~# '\\' + return indent(search('^\s*case', 'bWn')) + &shiftwidth + + " HACK: Don't change the indentation if the last line is a comment. + elseif l:lline =~# '^\s*--' + return l:ind + + " Align the end of block comments with the start + elseif l:line =~# '^\s*-}' + return indent(search('{-', 'bWn')) + + " Indent double shift after let with an empty rhs + elseif l:lline =~# '\<let\>.*\s=$' + return l:ind + 4 + &shiftwidth + + " Align 'in' with the parent let. + elseif l:line =~# '^\s*in\>' + return indent(search('^\s*let', 'bWn')) + + " Align bindings with the parent let. + elseif l:lline =~# '\<let\>' + return l:ind + 4 + + " Align bindings with the parent in. + elseif l:lline =~# '^\s*in\>' + return l:ind + + endif + + " Add a 'shiftwidth' after lines ending with: + if l:lline =~# '\(|\|=\|->\|<-\|(\|\[\|{\|\<\(of\|else\|if\|then\)\)\s*$' + let l:ind = l:ind + &shiftwidth + + " Add a 'shiftwidth' after lines starting with type ending with '=': + elseif l:lline =~# '^\s*type' && l:line =~# '^\s*=' + let l:ind = l:ind + &shiftwidth + + " Back to normal indent after comments: + elseif l:lline =~# '-}\s*$' + call search('-}', 'bW') + let l:ind = indent(searchpair('{-', '', '-}', 'bWn', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string"')) + + " Ident some operators if there aren't any starting the last line. + elseif l:line =~# '^\s*\(!\|&\|(\|`\|+\||\|{\|[\|,\)=' && l:lline !~# '^\s*\(!\|&\|(\|`\|+\||\|{\|[\|,\)=' && l:lline !~# '^\s*$' + let l:ind = l:ind + &shiftwidth + + elseif l:lline ==# '' && getline(l:lnum - 1) !=# '' + let l:ind = indent(search('^\s*\S+', 'bWn')) + + endif + + return l:ind +endfunc diff --git a/runtime/indent/erlang.vim b/runtime/indent/erlang.vim new file mode 100644 index 0000000..5682c31 --- /dev/null +++ b/runtime/indent/erlang.vim @@ -0,0 +1,1536 @@ +" Vim indent file +" Language: Erlang (http://www.erlang.org) +" Author: Csaba Hoch <csaba.hoch@gmail.com> +" Contributors: Edwin Fine <efine145_nospam01 at usa dot net> +" Pawel 'kTT' Salata <rockplayer.pl@gmail.com> +" Ricardo Catalinas Jiménez <jimenezrick@gmail.com> +" Last Update: 2022-Sep-06 +" License: Vim license +" URL: https://github.com/vim-erlang/vim-erlang-runtime + +" Note About Usage: +" This indentation script works best with the Erlang syntax file created by +" Kreąimir Marľić (Kresimir Marzic) and maintained by Csaba Hoch. + +" Notes About Implementation: +" +" - LTI = Line to indent. +" - The index of the first line is 1, but the index of the first column is 0. + + +" Initialization {{{1 +" ============== + +" Only load this indent file when no other was loaded +" Vim 7 or later is needed +if exists("b:did_indent") || version < 700 + finish +else + let b:did_indent = 1 +endif + +setlocal indentexpr=ErlangIndent() +setlocal indentkeys+=0=end,0=of,0=catch,0=after,0=else,0=when,0=),0=],0=},0=>> + +let b:undo_indent = "setl inde< indk<" + +" Only define the functions once +if exists("*ErlangIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +" Logging library {{{1 +" =============== + +" Purpose: +" Logs the given string using the ErlangIndentLog function if it exists. +" Parameters: +" s: string +function! s:Log(s) + if exists("*ErlangIndentLog") + call ErlangIndentLog(a:s) + endif +endfunction + +" Line tokenizer library {{{1 +" ====================== + +" Indtokens are "indentation tokens". See their exact format in the +" documentation of the s:GetTokensFromLine function. + +" Purpose: +" Calculate the new virtual column after the given segment of a line. +" Parameters: +" line: string +" first_index: integer -- the index of the first character of the segment +" last_index: integer -- the index of the last character of the segment +" vcol: integer -- the virtual column of the first character of the token +" tabstop: integer -- the value of the 'tabstop' option to be used +" Returns: +" vcol: integer +" Example: +" " index: 0 12 34567 +" " vcol: 0 45 89 +" s:CalcVCol("\t'\tx', b", 1, 4, 4) -> 10 +function! s:CalcVCol(line, first_index, last_index, vcol, tabstop) + + " We copy the relevant segment of the line, otherwise if the line were + " e.g. `"\t", term` then the else branch below would consume the `", term` + " part at once. + let line = a:line[a:first_index : a:last_index] + + let i = 0 + let last_index = a:last_index - a:first_index + let vcol = a:vcol + + while 0 <= i && i <= last_index + + if line[i] ==# "\t" + " Example (when tabstop == 4): + " + " vcol + tab -> next_vcol + " 0 + tab -> 4 + " 1 + tab -> 4 + " 2 + tab -> 4 + " 3 + tab -> 4 + " 4 + tab -> 8 + " + " next_i - i == the number of tabs + let next_i = matchend(line, '\t*', i + 1) + let vcol = (vcol / a:tabstop + (next_i - i)) * a:tabstop + call s:Log('new vcol after tab: '. vcol) + else + let next_i = matchend(line, '[^\t]*', i + 1) + let vcol += next_i - i + call s:Log('new vcol after other: '. vcol) + endif + let i = next_i + endwhile + + return vcol +endfunction + +" Purpose: +" Go through the whole line and return the tokens in the line. +" Parameters: +" line: string -- the line to be examined +" string_continuation: bool +" atom_continuation: bool +" Returns: +" indtokens = [indtoken] +" indtoken = [token, vcol, col] +" token = string (examples: 'begin', '<quoted_atom>', '}') +" vcol = integer (the virtual column of the first character of the token; +" counting starts from 0) +" col = integer (counting starts from 0) +function! s:GetTokensFromLine(line, string_continuation, atom_continuation, + \tabstop) + + let linelen = strlen(a:line) " The length of the line + let i = 0 " The index of the current character in the line + let vcol = 0 " The virtual column of the current character + let indtokens = [] + + if a:string_continuation + let i = matchend(a:line, '^\%([^"\\]\|\\.\)*"', 0) + if i ==# -1 + call s:Log(' Whole line is string continuation -> ignore') + return [] + else + let vcol = s:CalcVCol(a:line, 0, i - 1, 0, a:tabstop) + call add(indtokens, ['<string_end>', vcol, i]) + endif + elseif a:atom_continuation + let i = matchend(a:line, "^\\%([^'\\\\]\\|\\\\.\\)*'", 0) + if i ==# -1 + call s:Log(' Whole line is quoted atom continuation -> ignore') + return [] + else + let vcol = s:CalcVCol(a:line, 0, i - 1, 0, a:tabstop) + call add(indtokens, ['<quoted_atom_end>', vcol, i]) + endif + endif + + while 0 <= i && i < linelen + + let next_vcol = '' + + " Spaces + if a:line[i] ==# ' ' + let next_i = matchend(a:line, ' *', i + 1) + + " Tabs + elseif a:line[i] ==# "\t" + let next_i = matchend(a:line, '\t*', i + 1) + + " See example in s:CalcVCol + let next_vcol = (vcol / a:tabstop + (next_i - i)) * a:tabstop + + " Comment + elseif a:line[i] ==# '%' + let next_i = linelen + + " String token: "..." + elseif a:line[i] ==# '"' + let next_i = matchend(a:line, '\%([^"\\]\|\\.\)*"', i + 1) + if next_i ==# -1 + call add(indtokens, ['<string_start>', vcol, i]) + else + let next_vcol = s:CalcVCol(a:line, i, next_i - 1, vcol, a:tabstop) + call add(indtokens, ['<string>', vcol, i]) + endif + + " Quoted atom token: '...' + elseif a:line[i] ==# "'" + let next_i = matchend(a:line, "\\%([^'\\\\]\\|\\\\.\\)*'", i + 1) + if next_i ==# -1 + call add(indtokens, ['<quoted_atom_start>', vcol, i]) + else + let next_vcol = s:CalcVCol(a:line, i, next_i - 1, vcol, a:tabstop) + call add(indtokens, ['<quoted_atom>', vcol, i]) + endif + + " Keyword or atom or variable token or number + elseif a:line[i] =~# '[a-zA-Z_@0-9]' + let next_i = matchend(a:line, + \'[[:alnum:]_@:]*\%(\s*#\s*[[:alnum:]_@:]*\)\=', + \i + 1) + call add(indtokens, [a:line[(i):(next_i - 1)], vcol, i]) + + " Character token: $<char> (as in: $a) + elseif a:line[i] ==# '$' + call add(indtokens, ['$.', vcol, i]) + let next_i = i + 2 + + " Dot token: . + elseif a:line[i] ==# '.' + + let next_i = i + 1 + + if i + 1 ==# linelen || a:line[i + 1] =~# '[[:blank:]%]' + " End of clause token: . (as in: f() -> ok.) + call add(indtokens, ['<end_of_clause>', vcol, i]) + + else + " Possibilities: + " - Dot token in float: . (as in: 3.14) + " - Dot token in record: . (as in: #myrec.myfield) + call add(indtokens, ['.', vcol, i]) + endif + + " Equal sign + elseif a:line[i] ==# '=' + " This is handled separately so that "=<<" will be parsed as + " ['=', '<<'] instead of ['=<', '<']. Although Erlang parses it + " currently in the latter way, that may be fixed some day. + call add(indtokens, [a:line[i], vcol, i]) + let next_i = i + 1 + + " Three-character tokens + elseif i + 1 < linelen && + \ index(['=:=', '=/='], a:line[i : i + 1]) != -1 + call add(indtokens, [a:line[i : i + 1], vcol, i]) + let next_i = i + 2 + + " Two-character tokens + elseif i + 1 < linelen && + \ index(['->', '<<', '>>', '||', '==', '/=', '=<', '>=', '?=', '++', + \ '--', '::'], + \ a:line[i : i + 1]) != -1 + call add(indtokens, [a:line[i : i + 1], vcol, i]) + let next_i = i + 2 + + " Other character: , ; < > ( ) [ ] { } # + - * / : ? = ! | + else + call add(indtokens, [a:line[i], vcol, i]) + let next_i = i + 1 + + endif + + if next_vcol ==# '' + let vcol += next_i - i + else + let vcol = next_vcol + endif + + let i = next_i + + endwhile + + return indtokens + +endfunction + +" TODO: doc, handle "not found" case +function! s:GetIndtokenAtCol(indtokens, col) + let i = 0 + while i < len(a:indtokens) + if a:indtokens[i][2] ==# a:col + return [1, i] + elseif a:indtokens[i][2] > a:col + return [0, s:IndentError('No token at col ' . a:col . ', ' . + \'indtokens = ' . string(a:indtokens), + \'', '')] + endif + let i += 1 + endwhile + return [0, s:IndentError('No token at col ' . a:col . ', ' . + \'indtokens = ' . string(a:indtokens), + \'', '')] +endfunction + +" Stack library {{{1 +" ============= + +" Purpose: +" Push a token onto the parser's stack. +" Parameters: +" stack: [token] +" token: string +function! s:Push(stack, token) + call s:Log(' Stack Push: "' . a:token . '" into ' . string(a:stack)) + call insert(a:stack, a:token) +endfunction + +" Purpose: +" Pop a token from the parser's stack. +" Parameters: +" stack: [token] +" token: string +" Returns: +" token: string -- the removed element +function! s:Pop(stack) + let head = remove(a:stack, 0) + call s:Log(' Stack Pop: "' . head . '" from ' . string(a:stack)) + return head +endfunction + +" Library for accessing and storing tokenized lines {{{1 +" ================================================= + +" The Erlang token cache: an `lnum -> indtokens` dictionary that stores the +" tokenized lines. +let s:all_tokens = {} +let s:file_name = '' +let s:last_changedtick = -1 + +" Purpose: +" Clear the Erlang token cache if we have a different file or the file has +" been changed since the last indentation. +function! s:ClearTokenCacheIfNeeded() + let file_name = expand('%:p') + if file_name != s:file_name || + \ b:changedtick != s:last_changedtick + let s:file_name = file_name + let s:last_changedtick = b:changedtick + let s:all_tokens = {} + endif +endfunction + +" Purpose: +" Return the tokens of line `lnum`, if that line is not empty. If it is +" empty, find the first non-empty line in the given `direction` and return +" the tokens of that line. +" Parameters: +" lnum: integer +" direction: 'up' | 'down' +" Returns: +" result: [] -- the result is an empty list if we hit the beginning or end +" of the file +" | [lnum, indtokens] +" lnum: integer -- the index of the non-empty line that was found and +" tokenized +" indtokens: [indtoken] -- the tokens of line `lnum` +function! s:TokenizeLine(lnum, direction) + + call s:Log('Tokenizing starts from line ' . a:lnum) + if a:direction ==# 'up' + let lnum = prevnonblank(a:lnum) + else " a:direction ==# 'down' + let lnum = nextnonblank(a:lnum) + endif + + " We hit the beginning or end of the file + if lnum ==# 0 + let indtokens = [] + call s:Log(' We hit the beginning or end of the file.') + + " The line has already been parsed + elseif has_key(s:all_tokens, lnum) + let indtokens = s:all_tokens[lnum] + call s:Log('Cached line ' . lnum . ': ' . getline(lnum)) + call s:Log(" Tokens in the line:\n - " . join(indtokens, "\n - ")) + + " The line should be parsed now + else + + " Parse the line + let line = getline(lnum) + let string_continuation = s:IsLineStringContinuation(lnum) + let atom_continuation = s:IsLineAtomContinuation(lnum) + let indtokens = s:GetTokensFromLine(line, string_continuation, + \atom_continuation, &tabstop) + let s:all_tokens[lnum] = indtokens + call s:Log('Tokenizing line ' . lnum . ': ' . line) + call s:Log(" Tokens in the line:\n - " . join(indtokens, "\n - ")) + + endif + + return [lnum, indtokens] +endfunction + +" Purpose: +" As a helper function for PrevIndToken and NextIndToken, the FindIndToken +" function finds the first line with at least one token in the given +" direction. +" Parameters: +" lnum: integer +" direction: 'up' | 'down' +" Returns: +" result: [[], 0, 0] +" -- the result is an empty list if we hit the beginning or end of +" the file +" | [indtoken, lnum, i] +" -- the content, lnum and token index of the next (or previous) +" indtoken +function! s:FindIndToken(lnum, dir) + let lnum = a:lnum + while 1 + let lnum += (a:dir ==# 'up' ? -1 : 1) + let [lnum, indtokens] = s:TokenizeLine(lnum, a:dir) + if lnum ==# 0 + " We hit the beginning or end of the file + return [[], 0, 0] + elseif !empty(indtokens) + " We found a non-empty line. If we were moving up, we return the last + " token of this line. Otherwise we return the first token if this line. + let i = (a:dir ==# 'up' ? len(indtokens) - 1 : 0) + return [indtokens[i], lnum, i] + endif + endwhile +endfunction + +" Purpose: +" Find the token that directly precedes the given token. +" Parameters: +" lnum: integer -- the line of the given token +" i: the index of the given token within line `lnum` +" Returns: +" result = [] -- the result is an empty list if the given token is the first +" token of the file +" | indtoken +function! s:PrevIndToken(lnum, i) + call s:Log(' PrevIndToken called: lnum=' . a:lnum . ', i =' . a:i) + + " If the current line has a previous token, return that + if a:i > 0 + return [s:all_tokens[a:lnum][a:i - 1], a:lnum, a:i - 1] + else + return s:FindIndToken(a:lnum, 'up') + endif +endfunction + +" Purpose: +" Find the token that directly succeeds the given token. +" Parameters: +" lnum: integer -- the line of the given token +" i: the index of the given token within line `lnum` +" Returns: +" result = [] -- the result is an empty list if the given token is the last +" token of the file +" | indtoken +function! s:NextIndToken(lnum, i) + call s:Log(' NextIndToken called: lnum=' . a:lnum . ', i =' . a:i) + + " If the current line has a next token, return that + if len(s:all_tokens[a:lnum]) > a:i + 1 + return [s:all_tokens[a:lnum][a:i + 1], a:lnum, a:i + 1] + else + return s:FindIndToken(a:lnum, 'down') + endif +endfunction + +" ErlangCalcIndent helper functions {{{1 +" ================================= + +" Purpose: +" This function is called when the parser encounters a syntax error. +" +" If we encounter a syntax error, we return +" g:erlang_unexpected_token_indent, which is -1 by default. This means that +" the indentation of the LTI will not be changed. +" Parameter: +" msg: string +" token: string +" stack: [token] +" Returns: +" indent: integer +function! s:IndentError(msg, token, stack) + call s:Log('Indent error: ' . a:msg . ' -> return') + call s:Log(' Token = ' . a:token . ', ' . + \' stack = ' . string(a:stack)) + return g:erlang_unexpected_token_indent +endfunction + +" Purpose: +" This function is called when the parser encounters an unexpected token, +" and the parser will return the number given back by UnexpectedToken. +" +" If we encounter an unexpected token, we return +" g:erlang_unexpected_token_indent, which is -1 by default. This means that +" the indentation of the LTI will not be changed. +" Parameter: +" token: string +" stack: [token] +" Returns: +" indent: integer +function! s:UnexpectedToken(token, stack) + call s:Log(' Unexpected token ' . a:token . ', stack = ' . + \string(a:stack) . ' -> return') + return g:erlang_unexpected_token_indent +endfunction + +if !exists('g:erlang_unexpected_token_indent') + let g:erlang_unexpected_token_indent = -1 +endif + +" Purpose: +" Return whether the given line starts with a string continuation. +" Parameter: +" lnum: integer +" Returns: +" result: bool +" Example: +" f() -> % IsLineStringContinuation = false +" "This is a % IsLineStringContinuation = false +" multiline % IsLineStringContinuation = true +" string". % IsLineStringContinuation = true +function! s:IsLineStringContinuation(lnum) + if has('syntax_items') + return synIDattr(synID(a:lnum, 1, 0), 'name') =~# '^erlangString' + else + return 0 + endif +endfunction + +" Purpose: +" Return whether the given line starts with an atom continuation. +" Parameter: +" lnum: integer +" Returns: +" result: bool +" Example: +" 'function with % IsLineAtomContinuation = true, but should be false +" weird name'() -> % IsLineAtomContinuation = true +" ok. % IsLineAtomContinuation = false +function! s:IsLineAtomContinuation(lnum) + if has('syntax_items') + let syn_name = synIDattr(synID(a:lnum, 1, 0), 'name') + return syn_name =~# '^erlangQuotedAtom' || + \ syn_name =~# '^erlangQuotedRecord' + else + return 0 + endif +endfunction + +" Purpose: +" Return whether the 'catch' token (which should be the `i`th token in line +" `lnum`) is standalone or part of a try-catch block, based on the preceding +" token. +" Parameters: +" lnum: integer +" i: integer +" Return: +" is_standalone: bool +function! s:IsCatchStandalone(lnum, i) + call s:Log(' IsCatchStandalone called: lnum=' . a:lnum . ', i=' . a:i) + let [prev_indtoken, _, _] = s:PrevIndToken(a:lnum, a:i) + + " If we hit the beginning of the file, it is not a catch in a try block + if prev_indtoken == [] + return 1 + endif + + let prev_token = prev_indtoken[0] + + if prev_token =~# '^[A-Z_@0-9]' + let is_standalone = 0 + elseif prev_token =~# '[a-z]' + if index(['after', 'and', 'andalso', 'band', 'begin', 'bnot', 'bor', 'bsl', + \ 'bsr', 'bxor', 'case', 'catch', 'div', 'maybe', 'not', 'or', + \ 'orelse', 'rem', 'try', 'xor'], prev_token) != -1 + " If catch is after these keywords, it is standalone + let is_standalone = 1 + else + " If catch is after another keyword (e.g. 'end') or an atom, it is + " part of try-catch. + " + " Keywords: + " - may precede 'catch': end + " - may not precede 'catch': else fun if of receive when + " - unused: cond let query + let is_standalone = 0 + endif + elseif index([')', ']', '}', '<string>', '<string_end>', '<quoted_atom>', + \ '<quoted_atom_end>', '$.'], prev_token) != -1 + let is_standalone = 0 + else + " This 'else' branch includes the following tokens: + " -> == /= =< < >= > ?= =:= =/= + - * / ++ -- :: < > ; ( [ { ? = ! . | + let is_standalone = 1 + endif + + call s:Log(' "catch" preceded by "' . prev_token . '" -> catch ' . + \(is_standalone ? 'is standalone' : 'belongs to try-catch')) + return is_standalone + +endfunction + +" Purpose: +" This function is called when a begin-type element ('begin', 'case', +" '[', '<<', etc.) is found. It asks the caller to return if the stack +" if already empty. +" Parameters: +" stack: [token] +" token: string +" curr_vcol: integer +" stored_vcol: integer +" sw: integer -- number of spaces to be used after the begin element as +" indentation +" Returns: +" result: [should_return, indent] +" should_return: bool -- if true, the caller should return `indent` to Vim +" indent -- integer +function! s:BeginElementFoundIfEmpty(stack, token, curr_vcol, stored_vcol, sw) + if empty(a:stack) + if a:stored_vcol ==# -1 + call s:Log(' "' . a:token . '" directly precedes LTI -> return') + return [1, a:curr_vcol + a:sw] + else + call s:Log(' "' . a:token . + \'" token (whose expression includes LTI) found -> return') + return [1, a:stored_vcol] + endif + else + return [0, 0] + endif +endfunction + +" Purpose: +" This function is called when a begin-type element ('begin', 'case', '[', +" '<<', etc.) is found, and in some cases when 'after' and 'when' is found. +" It asks the caller to return if the stack is already empty. +" Parameters: +" stack: [token] +" token: string +" curr_vcol: integer +" stored_vcol: integer +" end_token: end token that belongs to the begin element found (e.g. if the +" begin element is 'begin', the end token is 'end') +" sw: integer -- number of spaces to be used after the begin element as +" indentation +" Returns: +" result: [should_return, indent] +" should_return: bool -- if true, the caller should return `indent` to Vim +" indent -- integer +function! s:BeginElementFound(stack, token, curr_vcol, stored_vcol, end_token, sw) + + " Return 'return' if the stack is empty + let [ret, res] = s:BeginElementFoundIfEmpty(a:stack, a:token, a:curr_vcol, + \a:stored_vcol, a:sw) + if ret | return [ret, res] | endif + + if a:stack[0] ==# a:end_token + call s:Log(' "' . a:token . '" pops "' . a:end_token . '"') + call s:Pop(a:stack) + if !empty(a:stack) && a:stack[0] ==# 'align_to_begin_element' + call s:Pop(a:stack) + if empty(a:stack) + return [1, a:curr_vcol] + else + return [1, s:UnexpectedToken(a:token, a:stack)] + endif + else + return [0, 0] + endif + else + return [1, s:UnexpectedToken(a:token, a:stack)] + endif +endfunction + +" Purpose: +" This function is called when we hit the beginning of a file or an +" end-of-clause token -- i.e. when we found the beginning of the current +" clause. +" +" If the stack contains an '->' or 'when', this means that we can return +" now, since we were looking for the beginning of the clause. +" Parameters: +" stack: [token] +" token: string +" stored_vcol: integer +" lnum: the line number of the "end of clause" mark (or 0 if we hit the +" beginning of the file) +" i: the index of the "end of clause" token within its own line +" Returns: +" result: [should_return, indent] +" should_return: bool -- if true, the caller should return `indent` to Vim +" indent -- integer +function! s:BeginningOfClauseFound(stack, token, stored_vcol, lnum, i) + if !empty(a:stack) && a:stack[0] ==# 'when' + call s:Log(' BeginningOfClauseFound: "when" found in stack') + call s:Pop(a:stack) + if empty(a:stack) + call s:Log(' Stack is ["when"], so LTI is in a guard -> return') + return [1, a:stored_vcol + shiftwidth() + 2] + else + return [1, s:UnexpectedToken(a:token, a:stack)] + endif + elseif !empty(a:stack) && a:stack[0] ==# '->' + call s:Log(' BeginningOfClauseFound: "->" found in stack') + call s:Pop(a:stack) + if empty(a:stack) + call s:Log(' Stack is ["->"], so LTI is in function body -> return') + return [1, a:stored_vcol + shiftwidth()] + elseif a:stack[0] ==# ';' + call s:Pop(a:stack) + + if !empty(a:stack) + return [1, s:UnexpectedToken(a:token, a:stack)] + endif + + if a:lnum ==# 0 + " Set lnum and i to be NextIndToken-friendly + let lnum = 1 + let i = -1 + else + let lnum = a:lnum + let i = a:i + endif + + " Are we after a "-spec func() ...;" clause? + let [next1_indtoken, next1_lnum, next1_i] = s:NextIndToken(lnum, i) + if !empty(next1_indtoken) && next1_indtoken[0] =~# '-' + let [next2_indtoken, next2_lnum, next2_i] = + \s:NextIndToken(next1_lnum, next1_i) + if !empty(next2_indtoken) && next2_indtoken[0] =~# 'spec' + let [next3_indtoken, next3_lnum, next3_i] = + \s:NextIndToken(next2_lnum, next2_i) + if !empty(next3_indtoken) + let [next4_indtoken, next4_lnum, next4_i] = + \s:NextIndToken(next3_lnum, next3_i) + if !empty(next4_indtoken) + " Yes, we are. + call s:Log(' Stack is ["->", ";"], so LTI is in a "-spec" ' . + \'attribute -> return') + return [1, next4_indtoken[1]] + endif + endif + endif + endif + + call s:Log(' Stack is ["->", ";"], so LTI is in a function head ' . + \'-> return') + return [1, a:stored_vcol] + + else + return [1, s:UnexpectedToken(a:token, a:stack)] + endif + else + return [0, 0] + endif +endfunction + +let g:erlang_indent_searchpair_timeout = 2000 + +" TODO +function! s:SearchPair(lnum, curr_col, start, middle, end) + call cursor(a:lnum, a:curr_col + 1) + let [lnum_new, col1_new] = + \searchpairpos(a:start, a:middle, a:end, 'bW', + \'synIDattr(synID(line("."), col("."), 0), "name") ' . + \'=~? "string\\|quotedatom\\|todo\\|comment\\|' . + \'erlangmodifier"', + \0, g:erlang_indent_searchpair_timeout) + return [lnum_new, col1_new - 1] +endfunction + +function! s:SearchEndPair(lnum, curr_col) + return s:SearchPair( + \ a:lnum, a:curr_col, + \ '\C\<\%(case\|try\|begin\|receive\|if\|maybe\)\>\|' . + \ '\<fun\>\%(\s\|\n\|%.*$\|[A-Z_@][a-zA-Z_@]*\)*(', + \ '', + \ '\<end\>') +endfunction + +" ErlangCalcIndent {{{1 +" ================ + +" Purpose: +" Calculate the indentation of the given line. +" Parameters: +" lnum: integer -- index of the line for which the indentation should be +" calculated +" stack: [token] -- initial stack +" Return: +" indent: integer -- if -1, that means "don't change the indentation"; +" otherwise it means "indent the line with `indent` +" number of spaces or equivalent tabs" +function! s:ErlangCalcIndent(lnum, stack) + let res = s:ErlangCalcIndent2(a:lnum, a:stack) + call s:Log("ErlangCalcIndent returned: " . res) + return res +endfunction + +function! s:ErlangCalcIndent2(lnum, stack) + + let lnum = a:lnum + let stored_vcol = -1 " Virtual column of the first character of the token that + " we currently think we might align to. + let mode = 'normal' + let stack = a:stack + let semicolon_abscol = '' + + " Walk through the lines of the buffer backwards (starting from the + " previous line) until we can decide how to indent the current line. + while 1 + + let [lnum, indtokens] = s:TokenizeLine(lnum, 'up') + + " Hit the start of the file + if lnum ==# 0 + let [ret, res] = s:BeginningOfClauseFound(stack, 'beginning_of_file', + \stored_vcol, 0, 0) + if ret | return res | endif + + return 0 + endif + + let i = len(indtokens) - 1 + let last_token_of_line = 1 + + while i >= 0 + + let [token, curr_vcol, curr_col] = indtokens[i] + call s:Log(' Analyzing the following token: ' . string(indtokens[i])) + + if len(stack) > 256 " TODO: magic number + return s:IndentError('Stack too long', token, stack) + endif + + if token ==# '<end_of_clause>' + let [ret, res] = s:BeginningOfClauseFound(stack, token, stored_vcol, + \lnum, i) + if ret | return res | endif + + if stored_vcol ==# -1 + call s:Log(' End of clause directly precedes LTI -> return') + return 0 + else + call s:Log(' End of clause (but not end of line) -> return') + return stored_vcol + endif + + elseif stack == ['prev_term_plus'] + if token =~# '[a-zA-Z_@#]' || + \ token ==# '<string>' || token ==# '<string_start>' || + \ token ==# '<quoted_atom>' || token ==# '<quoted_atom_start>' + call s:Log(' previous token found: curr_vcol + plus = ' . + \curr_vcol . " + " . plus) + return curr_vcol + plus + endif + + elseif token ==# 'begin' + let [ret, res] = s:BeginElementFound(stack, token, curr_vcol, + \stored_vcol, 'end', shiftwidth()) + if ret | return res | endif + + " case EXPR of BRANCHES end + " if BRANCHES end + " try EXPR catch BRANCHES end + " try EXPR after BODY end + " try EXPR catch BRANCHES after BODY end + " try EXPR of BRANCHES catch BRANCHES end + " try EXPR of BRANCHES after BODY end + " try EXPR of BRANCHES catch BRANCHES after BODY end + " receive BRANCHES end + " receive BRANCHES after BRANCHES end + " maybe EXPR end + " maybe EXPR else BRANCHES end + + " This branch is not Emacs-compatible + elseif (index(['of', 'receive', 'after', 'if', 'else'], token) != -1 || + \ (token ==# 'catch' && !s:IsCatchStandalone(lnum, i))) && + \ !last_token_of_line && + \ (empty(stack) || stack ==# ['when'] || stack ==# ['->'] || + \ stack ==# ['->', ';']) + + " If we are after of/receive/etc, but these are not the last + " tokens of the line, we want to indent like this: + " + " % stack == [] + " receive stored_vcol, + " LTI + " + " % stack == ['->', ';'] + " receive stored_vcol -> + " B; + " LTI + " + " % stack == ['->'] + " receive stored_vcol -> + " LTI + " + " % stack == ['when'] + " receive stored_vcol when + " LTI + + " stack = [] => LTI is a condition + " stack = ['->'] => LTI is a branch + " stack = ['->', ';'] => LTI is a condition + " stack = ['when'] => LTI is a guard + if empty(stack) || stack == ['->', ';'] + call s:Log(' LTI is in a condition after ' . + \'"of/receive/after/if/else/catch" -> return') + return stored_vcol + elseif stack == ['->'] + call s:Log(' LTI is in a branch after ' . + \'"of/receive/after/if/else/catch" -> return') + return stored_vcol + shiftwidth() + elseif stack == ['when'] + call s:Log(' LTI is in a guard after ' . + \'"of/receive/after/if/else/catch" -> return') + return stored_vcol + shiftwidth() + else + return s:UnexpectedToken(token, stack) + endif + + elseif index(['case', 'if', 'try', 'receive', 'maybe'], token) != -1 + + " stack = [] => LTI is a condition + " stack = ['->'] => LTI is a branch + " stack = ['->', ';'] => LTI is a condition + " stack = ['when'] => LTI is in a guard + if empty(stack) + " pass + elseif (token ==# 'case' && stack[0] ==# 'of') || + \ (token ==# 'if') || + \ (token ==# 'maybe' && stack[0] ==# 'else') || + \ (token ==# 'try' && (stack[0] ==# 'of' || + \ stack[0] ==# 'catch' || + \ stack[0] ==# 'after')) || + \ (token ==# 'receive') + + " From the indentation point of view, the keyword + " (of/catch/after/else/end) before the LTI is what counts, so + " when we reached these tokens, and the stack already had + " a catch/after/else/end, we didn't modify it. + " + " This way when we reach case/try/receive/maybe (i.e. now), + " there is at most one of/catch/after/else/end token in the + " stack. + if token ==# 'case' || token ==# 'try' || + \ (token ==# 'receive' && stack[0] ==# 'after') || + \ (token ==# 'maybe' && stack[0] ==# 'else') + call s:Pop(stack) + endif + + if empty(stack) + call s:Log(' LTI is in a condition; matching ' . + \'"case/if/try/receive/maybe" found') + let stored_vcol = curr_vcol + shiftwidth() + elseif stack[0] ==# 'align_to_begin_element' + call s:Pop(stack) + let stored_vcol = curr_vcol + elseif len(stack) > 1 && stack[0] ==# '->' && stack[1] ==# ';' + call s:Log(' LTI is in a condition; matching ' . + \'"case/if/try/receive/maybe" found') + call s:Pop(stack) + call s:Pop(stack) + let stored_vcol = curr_vcol + shiftwidth() + elseif stack[0] ==# '->' + call s:Log(' LTI is in a branch; matching ' . + \'"case/if/try/receive/maybe" found') + call s:Pop(stack) + let stored_vcol = curr_vcol + 2 * shiftwidth() + elseif stack[0] ==# 'when' + call s:Log(' LTI is in a guard; matching ' . + \'"case/if/try/receive/maybe" found') + call s:Pop(stack) + let stored_vcol = curr_vcol + 2 * shiftwidth() + 2 + endif + + endif + + let [ret, res] = s:BeginElementFound(stack, token, curr_vcol, + \stored_vcol, 'end', shiftwidth()) + if ret | return res | endif + + elseif token ==# 'fun' + let [next_indtoken, next_lnum, next_i] = s:NextIndToken(lnum, i) + call s:Log(' Next indtoken = ' . string(next_indtoken)) + + if !empty(next_indtoken) && next_indtoken[0] =~# '^[A-Z_@]' + " The "fun" is followed by a variable, so we might have a named fun: + " "fun Fun() -> ok end". Thus we take the next token to decide + " whether this is a function definition ("fun()") or just a function + " reference ("fun Mod:Fun"). + let [next_indtoken, _, _] = s:NextIndToken(next_lnum, next_i) + call s:Log(' Next indtoken = ' . string(next_indtoken)) + endif + + if !empty(next_indtoken) && next_indtoken[0] ==# '(' + " We have an anonymous function definition + " (e.g. "fun () -> ok end") + + " stack = [] => LTI is a condition + " stack = ['->'] => LTI is a branch + " stack = ['->', ';'] => LTI is a condition + " stack = ['when'] => LTI is in a guard + if empty(stack) + call s:Log(' LTI is in a condition; matching "fun" found') + let stored_vcol = curr_vcol + shiftwidth() + elseif len(stack) > 1 && stack[0] ==# '->' && stack[1] ==# ';' + call s:Log(' LTI is in a condition; matching "fun" found') + call s:Pop(stack) + call s:Pop(stack) + elseif stack[0] ==# '->' + call s:Log(' LTI is in a branch; matching "fun" found') + call s:Pop(stack) + let stored_vcol = curr_vcol + 2 * shiftwidth() + elseif stack[0] ==# 'when' + call s:Log(' LTI is in a guard; matching "fun" found') + call s:Pop(stack) + let stored_vcol = curr_vcol + 2 * shiftwidth() + 2 + endif + + let [ret, res] = s:BeginElementFound(stack, token, curr_vcol, + \stored_vcol, 'end', shiftwidth()) + if ret | return res | endif + else + " Pass: we have a function reference (e.g. "fun f/0") + endif + + elseif token ==# '[' + " Emacs compatibility + let [ret, res] = s:BeginElementFound(stack, token, curr_vcol, + \stored_vcol, ']', 1) + if ret | return res | endif + + elseif token ==# '<<' + " Emacs compatibility + let [ret, res] = s:BeginElementFound(stack, token, curr_vcol, + \stored_vcol, '>>', 2) + if ret | return res | endif + + elseif token ==# '(' || token ==# '{' + + let end_token = (token ==# '(' ? ')' : + \token ==# '{' ? '}' : 'error') + + if empty(stack) + " We found the opening paren whose block contains the LTI. + let mode = 'inside' + elseif stack[0] ==# end_token + call s:Log(' "' . token . '" pops "' . end_token . '"') + call s:Pop(stack) + + if !empty(stack) && stack[0] ==# 'align_to_begin_element' + " We found the opening paren whose closing paren + " starts LTI + let mode = 'align_to_begin_element' + else + " We found the opening pair for a closing paren that + " was already in the stack. + let mode = 'outside' + endif + else + return s:UnexpectedToken(token, stack) + endif + + if mode ==# 'inside' || mode ==# 'align_to_begin_element' + + if last_token_of_line && i != 0 + " Examples: {{{ + " + " mode == 'inside': + " + " my_func( + " LTI + " + " [Variable, { + " LTI + " + " mode == 'align_to_begin_element': + " + " my_func( + " Params + " ) % LTI + " + " [Variable, { + " Terms + " } % LTI + " }}} + let stack = ['prev_term_plus'] + let plus = (mode ==# 'inside' ? 2 : 1) + call s:Log(' "' . token . + \'" token found at end of line -> find previous token') + elseif mode ==# 'align_to_begin_element' + " Examples: {{{ + " + " mode == 'align_to_begin_element' && !last_token_of_line + " + " my_func(stored_vcol + " ) % LTI + " + " [Variable, {stored_vcol + " } % LTI + " + " mode == 'align_to_begin_element' && i == 0 + " + " ( + " stored_vcol + " ) % LTI + " + " { + " stored_vcol + " } % LTI + " }}} + call s:Log(' "' . token . '" token (whose closing token ' . + \'starts LTI) found -> return') + return curr_vcol + elseif stored_vcol ==# -1 + " Examples: {{{ + " + " mode == 'inside' && stored_vcol == -1 && !last_token_of_line + " + " my_func( + " LTI + " [Variable, { + " LTI + " + " mode == 'inside' && stored_vcol == -1 && i == 0 + " + " ( + " LTI + " + " { + " LTI + " }}} + call s:Log(' "' . token . + \'" token (which directly precedes LTI) found -> return') + return curr_vcol + 1 + else + " Examples: {{{ + " + " mode == 'inside' && stored_vcol != -1 && !last_token_of_line + " + " my_func(stored_vcol, + " LTI + " + " [Variable, {stored_vcol, + " LTI + " + " mode == 'inside' && stored_vcol != -1 && i == 0 + " + " (stored_vcol, + " LTI + " + " {stored_vcol, + " LTI + " }}} + call s:Log(' "' . token . + \'" token (whose block contains LTI) found -> return') + return stored_vcol + endif + endif + + elseif index(['end', ')', ']', '}', '>>'], token) != -1 + + " If we can be sure that there is synchronization in the Erlang + " syntax, we use searchpair to make the script quicker. Otherwise we + " just push the token onto the stack and keep parsing. + + " No synchronization -> no searchpair optimization + if !exists('b:erlang_syntax_synced') + call s:Push(stack, token) + + " We don't have searchpair optimization for '>>' + elseif token ==# '>>' + call s:Push(stack, token) + + elseif token ==# 'end' + let [lnum_new, col_new] = s:SearchEndPair(lnum, curr_col) + + if lnum_new ==# 0 + return s:IndentError('Matching token for "end" not found', + \token, stack) + else + if lnum_new != lnum + call s:Log(' Tokenize for "end" <<<<') + let [lnum, indtokens] = s:TokenizeLine(lnum_new, 'up') + call s:Log(' >>>> Tokenize for "end"') + endif + + let [success, i] = s:GetIndtokenAtCol(indtokens, col_new) + if !success | return i | endif + let [token, curr_vcol, curr_col] = indtokens[i] + call s:Log(' Match for "end" in line ' . lnum_new . ': ' . + \string(indtokens[i])) + endif + + else " token is one of the following: ')', ']', '}' + + call s:Push(stack, token) + + " We have to escape '[', because this string will be interpreted as a + " regexp + let open_paren = (token ==# ')' ? '(' : + \token ==# ']' ? '\[' : + \ '{') + + let [lnum_new, col_new] = s:SearchPair(lnum, curr_col, + \open_paren, '', token) + + if lnum_new ==# 0 + return s:IndentError('Matching token not found', + \token, stack) + else + if lnum_new != lnum + call s:Log(' Tokenize the opening paren <<<<') + let [lnum, indtokens] = s:TokenizeLine(lnum_new, 'up') + call s:Log(' >>>>') + endif + + let [success, i] = s:GetIndtokenAtCol(indtokens, col_new) + if !success | return i | endif + let [token, curr_vcol, curr_col] = indtokens[i] + call s:Log(' Match in line ' . lnum_new . ': ' . + \string(indtokens[i])) + + " Go back to the beginning of the loop and handle the opening paren + continue + endif + endif + + elseif token ==# ';' + + if empty(stack) + call s:Push(stack, ';') + elseif index([';', '->', 'when', 'end', 'after', 'catch', 'else'], + \stack[0]) != -1 + " Pass: + " + " - If the stack top is another ';', then one ';' is + " enough. + " - If the stack top is an '->' or a 'when', then we + " should keep that, because they signify the type of the + " LTI (branch, condition or guard). + " - From the indentation point of view, the keyword + " (of/catch/after/else/end) before the LTI is what counts, so + " if the stack already has a catch/after/else/end, we don't + " modify it. This way when we reach case/try/receive/maybe, + " there will be at most one of/catch/after/else/end token in + " the stack. + else + return s:UnexpectedToken(token, stack) + endif + + elseif token ==# '->' + + if empty(stack) && !last_token_of_line + call s:Log(' LTI is in expression after arrow -> return') + return stored_vcol + elseif empty(stack) || stack[0] ==# ';' || stack[0] ==# 'end' + " stack = [';'] -> LTI is either a branch or in a guard + " stack = ['->'] -> LTI is a condition + " stack = ['->', ';'] -> LTI is a branch + call s:Push(stack, '->') + elseif index(['->', 'when', 'end', 'after', 'catch', 'else'], + \stack[0]) != -1 + " Pass: + " + " - If the stack top is another '->', then one '->' is + " enough. + " - If the stack top is a 'when', then we should keep + " that, because this signifies that LTI is a in a guard. + " - From the indentation point of view, the keyword + " (of/catch/after/else/end) before the LTI is what counts, so + " if the stack already has a catch/after/else/end, we don't + " modify it. This way when we reach case/try/receive/maybe, + " there will be at most one of/catch/after/else/end token in + " the stack. + else + return s:UnexpectedToken(token, stack) + endif + + elseif token ==# 'when' + + " Pop all ';' from the top of the stack + while !empty(stack) && stack[0] ==# ';' + call s:Pop(stack) + endwhile + + if empty(stack) + if semicolon_abscol != '' + let stored_vcol = semicolon_abscol + endif + if !last_token_of_line + " Example: + " when A, + " LTI + let [ret, res] = s:BeginElementFoundIfEmpty(stack, token, curr_vcol, + \stored_vcol, shiftwidth()) + if ret | return res | endif + else + " Example: + " when + " LTI + call s:Push(stack, token) + endif + elseif index(['->', 'when', 'end', 'after', 'catch', 'else'], + \stack[0]) != -1 + " Pass: + " - If the stack top is another 'when', then one 'when' is + " enough. + " - If the stack top is an '->' or a 'when', then we + " should keep that, because they signify the type of the + " LTI (branch, condition or guard). + " - From the indentation point of view, the keyword + " (of/catch/after/else/end) before the LTI is what counts, so + " if the stack already has a catch/after/else/end, we don't + " modify it. This way when we reach case/try/receive/maybe, + " there will be at most one of/catch/after/else/end token in + " the stack. + else + return s:UnexpectedToken(token, stack) + endif + + elseif token ==# 'of' || token ==# 'after' || token ==# 'else' || + \ (token ==# 'catch' && !s:IsCatchStandalone(lnum, i)) + + if token ==# 'after' || token ==# 'else' + " If LTI is between an after/else and the corresponding 'end', then + " let's return because calculating the indentation based on + " after/else is enough. + " + " Example: + " receive A after + " LTI + " maybe A else + " LTI + " + " Note about Emacs compatibility {{{ + " + " It would be fine to indent the examples above the following way: + " + " receive A after + " LTI + " maybe A else + " LTI + " + " We intend it the way above because that is how Emacs does it. + " Also, this is a bit faster. + " + " We are still not 100% Emacs compatible because of placing the + " 'end' after the indented blocks. + " + " Emacs example: + " + " receive A after + " LTI + " end, + " maybe A else + " LTI + " end % Yes, it's here (in OTP 25.0, might change + " % later) + " + " vim-erlang example: + " + " receive A after + " LTI + " end, + " maybe A else + " LTI + " end + " }}} + let [ret, res] = s:BeginElementFoundIfEmpty(stack, token, curr_vcol, + \stored_vcol, shiftwidth()) + if ret | return res | endif + endif + + if empty(stack) || stack[0] ==# '->' || stack[0] ==# 'when' + call s:Push(stack, token) + elseif stack[0] ==# 'catch' || stack[0] ==# 'after' || + \stack[0] ==# 'else' || stack[0] ==# 'end' + " Pass: From the indentation point of view, the keyword + " (of/catch/after/end) before the LTI is what counts, so + " if the stack already has a catch/after/end, we don't + " modify it. This way when we reach case/try/receive, + " there will be at most one of/catch/after/end token in + " the stack. + else + return s:UnexpectedToken(token, stack) + endif + + elseif token ==# '||' && empty(stack) && !last_token_of_line + + call s:Log(' LTI is in expression after "||" -> return') + return stored_vcol + + else + call s:Log(' Misc token, stack unchanged = ' . string(stack)) + + endif + + if empty(stack) || stack[0] ==# '->' || stack[0] ==# 'when' + let stored_vcol = curr_vcol + let semicolon_abscol = '' + call s:Log(' Misc token when the stack is empty or has "->" ' . + \'-> setting stored_vcol to ' . stored_vcol) + elseif stack[0] ==# ';' + let semicolon_abscol = curr_vcol + call s:Log(' Setting semicolon-stored_vcol to ' . stored_vcol) + endif + + let i -= 1 + call s:Log(' Token processed. stored_vcol=' . stored_vcol) + + let last_token_of_line = 0 + + endwhile " iteration on tokens in a line + + call s:Log(' Line analyzed. stored_vcol=' . stored_vcol) + + if empty(stack) && stored_vcol != -1 && + \ (!empty(indtokens) && indtokens[0][0] != '<string_end>' && + \ indtokens[0][0] != '<quoted_atom_end>') + call s:Log(' Empty stack at the beginning of the line -> return') + return stored_vcol + endif + + let lnum -= 1 + + endwhile " iteration on lines + +endfunction + +" ErlangIndent function {{{1 +" ===================== + +function! ErlangIndent() + + call s:ClearTokenCacheIfNeeded() + + let currline = getline(v:lnum) + call s:Log('Indenting line ' . v:lnum . ': ' . currline) + + if s:IsLineStringContinuation(v:lnum) || s:IsLineAtomContinuation(v:lnum) + call s:Log('String or atom continuation found -> ' . + \'leaving indentation unchanged') + return -1 + endif + + " If the line starts with the comment, and so is the previous non-blank line + if currline =~# '^\s*%' + let lnum = prevnonblank(v:lnum - 1) + if lnum ==# 0 + call s:Log('First non-empty line of the file -> return 0.') + return 0 + else + let ml = matchlist(getline(lnum), '^\(\s*\)%') + " If the previous line also starts with a comment, then return the same + " indentation that line has. Otherwise exit from this special "if" and + " don't care that the current line is a comment. + if !empty(ml) + let new_col = s:CalcVCol(ml[1], 0, len(ml[1]) - 1, 0, &tabstop) + call s:Log('Comment line after another comment line -> ' . + \'use same indent: ' . new_col) + return new_col + endif + endif + endif + + let ml = matchlist(currline, + \'^\(\s*\)\(\%(end\|of\|catch\|after\|else\)\>\|[)\]}]\|>>\)') + + " If the line has a special beginning, but not a standalone catch + if !empty(ml) && !(ml[2] ==# 'catch' && s:IsCatchStandalone(v:lnum, 0)) + + let curr_col = len(ml[1]) + + " If we can be sure that there is synchronization in the Erlang + " syntax, we use searchpair to make the script quicker. + if ml[2] ==# 'end' && exists('b:erlang_syntax_synced') + + let [lnum, col] = s:SearchEndPair(v:lnum, curr_col) + + if lnum ==# 0 + return s:IndentError('Matching token for "end" not found', + \'end', []) + else + call s:Log(' Tokenize for "end" <<<<') + let [lnum, indtokens] = s:TokenizeLine(lnum, 'up') + call s:Log(' >>>> Tokenize for "end"') + + let [success, i] = s:GetIndtokenAtCol(indtokens, col) + if !success | return i | endif + let [token, curr_vcol, curr_col] = indtokens[i] + call s:Log(' Match for "end" in line ' . lnum . ': ' . + \string(indtokens[i])) + return curr_vcol + endif + + else + + call s:Log(" Line type = 'end'") + let new_col = s:ErlangCalcIndent(v:lnum - 1, + \[ml[2], 'align_to_begin_element']) + endif + else + call s:Log(" Line type = 'normal'") + + let new_col = s:ErlangCalcIndent(v:lnum - 1, []) + if currline =~# '^\s*when\>' + let new_col += 2 + endif + endif + + if new_col < -1 + call s:Log('WARNING: returning new_col == ' . new_col) + return g:erlang_unexpected_token_indent + endif + + return new_col + +endfunction + +" ErlangShowTokensInLine functions {{{1 +" ================================ + +" These functions are useful during development. + +function! ErlangShowTokensInLine(line) + echo "Line: " . a:line + let indtokens = s:GetTokensFromLine(a:line, 0, 0, &tabstop) + echo "Tokens:" + for it in indtokens + echo it + endfor +endfunction + +function! ErlangShowTokensInCurrentLine() + return ErlangShowTokensInLine(getline('.')) +endfunction + +" Cleanup {{{1 +" ======= + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim: sw=2 et fdm=marker diff --git a/runtime/indent/eruby.vim b/runtime/indent/eruby.vim new file mode 100644 index 0000000..6ff15ab --- /dev/null +++ b/runtime/indent/eruby.vim @@ -0,0 +1,111 @@ +" Vim indent file +" Language: eRuby +" Maintainer: Tim Pope <vimNOSPAM@tpope.org> +" URL: https://github.com/vim-ruby/vim-ruby +" Release Coordinator: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2019 Jan 06 + +if exists("b:did_indent") + finish +endif + +runtime! indent/ruby.vim +unlet! b:did_indent +setlocal indentexpr= + +if exists("b:eruby_subtype") && b:eruby_subtype != '' && b:eruby_subtype !=# 'eruby' + exe "runtime! indent/".b:eruby_subtype.".vim" +else + runtime! indent/html.vim +endif +unlet! b:did_indent + +" Force HTML indent to not keep state. +let b:html_indent_usestate = 0 + +if &l:indentexpr == '' + if &l:cindent + let &l:indentexpr = 'cindent(v:lnum)' + else + let &l:indentexpr = 'indent(prevnonblank(v:lnum-1))' + endif +endif +let b:eruby_subtype_indentexpr = &l:indentexpr + +let b:did_indent = 1 + +setlocal indentexpr=GetErubyIndent() +setlocal indentkeys=o,O,*<Return>,<>>,{,},0),0],o,O,!^F,=end,=else,=elsif,=rescue,=ensure,=when + +" Only define the function once. +if exists("*GetErubyIndent") + finish +endif + +" this file uses line continuations +let s:cpo_sav = &cpo +set cpo&vim + +function! GetErubyIndent(...) + " The value of a single shift-width + if exists('*shiftwidth') + let sw = shiftwidth() + else + let sw = &sw + endif + + if a:0 && a:1 == '.' + let v:lnum = line('.') + elseif a:0 && a:1 =~ '^\d' + let v:lnum = a:1 + endif + let vcol = col('.') + call cursor(v:lnum,1) + let inruby = searchpair('<%','','%>','W') + call cursor(v:lnum,vcol) + if inruby && getline(v:lnum) !~ '^<%\|^\s*[-=]\=%>' + let ind = GetRubyIndent(v:lnum) + else + exe "let ind = ".b:eruby_subtype_indentexpr + + " Workaround for Andy Wokula's HTML indent. This should be removed after + " some time, since the newest version is fixed in a different way. + if b:eruby_subtype_indentexpr =~# '^HtmlIndent(' + \ && exists('b:indent') + \ && type(b:indent) == type({}) + \ && has_key(b:indent, 'lnum') + " Force HTML indent to not keep state + let b:indent.lnum = -1 + endif + endif + let lnum = prevnonblank(v:lnum-1) + let line = getline(lnum) + let cline = getline(v:lnum) + if cline =~# '^\s*<%[-=]\=\s*\%(}\|end\|else\|\%(ensure\|rescue\|elsif\|when\).\{-\}\)\s*\%([-=]\=%>\|$\)' + let ind = ind - sw + endif + if line =~# '\S\s*<%[-=]\=\s*\%(}\|end\).\{-\}\s*\%([-=]\=%>\|$\)' + let ind = ind - sw + endif + if line =~# '\%({\|\<do\)\%(\s*|[^|]*|\)\=\s*[-=]\=%>' + let ind = ind + sw + elseif line =~# '<%[-=]\=\s*\%(module\|class\|def\|if\|for\|while\|until\|else\|elsif\|case\|when\|unless\|begin\|ensure\|rescue\)\>.*%>' + let ind = ind + sw + endif + if line =~# '^\s*<%[=#-]\=\s*$' && cline !~# '^\s*end\>' + let ind = ind + sw + endif + if line !~# '^\s*<%' && line =~# '%>\s*$' && line !~# '^\s*end\>' + \ && synID(v:lnum, match(cline, '\S') + 1, 1) != hlID('htmlEndTag') + let ind = ind - sw + endif + if cline =~# '^\s*[-=]\=%>\s*$' + let ind = ind - sw + endif + return ind +endfunction + +let &cpo = s:cpo_sav +unlet! s:cpo_sav + +" vim:set sw=2 sts=2 ts=8 noet: diff --git a/runtime/indent/eterm.vim b/runtime/indent/eterm.vim new file mode 100644 index 0000000..3accf9b --- /dev/null +++ b/runtime/indent/eterm.vim @@ -0,0 +1,39 @@ +" Vim indent file +" Language: Eterm configuration file +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Last Change: 24 Sep 2021 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetEtermIndent() +setlocal indentkeys=!^F,o,O,=end +setlocal nosmartindent + +let b:undo_indent = "setl inde< indk< si<" + +if exists("*GetEtermIndent") + finish +endif + +function GetEtermIndent() + let lnum = prevnonblank(v:lnum - 1) + if lnum == 0 + return 0 + endif + + let ind = indent(lnum) + + if getline(lnum) =~ '^\s*begin\>' + let ind = ind + shiftwidth() + endif + + if getline(v:lnum) =~ '^\s*end\>' + let ind = ind - shiftwidth() + endif + + return ind +endfunction diff --git a/runtime/indent/expect.vim b/runtime/indent/expect.vim new file mode 100644 index 0000000..f2a1f05 --- /dev/null +++ b/runtime/indent/expect.vim @@ -0,0 +1,11 @@ +" Vim indent file +" Language: Expect +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2022 Jul 16 + +if exists("b:did_indent") + finish +endif + +" Syntax is similar to Tcl +runtime! indent/tcl.vim diff --git a/runtime/indent/falcon.vim b/runtime/indent/falcon.vim new file mode 100644 index 0000000..a58ccad --- /dev/null +++ b/runtime/indent/falcon.vim @@ -0,0 +1,454 @@ +" Vim indent file +" Language: Falcon +" Maintainer: Steven Oliver <oliver.steven@gmail.com> +" Website: https://steveno@github.com/steveno/falconpl-vim.git +" Credits: This is, to a great extent, a copy n' paste of ruby.vim. +" 2022 April: b:undo_indent added by Doug Kearns + +" 1. Setup {{{1 +" ============ + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal nosmartindent + +" Setup indent function and when to use it +setlocal indentexpr=FalconGetIndent(v:lnum) +setlocal indentkeys=0{,0},0),0],!^F,o,O,e +setlocal indentkeys+==~case,=~catch,=~default,=~elif,=~else,=~end,=~\" + +let b:undo_indent = "setl inde< indk< si<" + +" Define the appropriate indent function but only once +if exists("*FalconGetIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +" 2. Variables {{{1 +" ============ + +" Regex of syntax group names that are strings AND comments +let s:syng_strcom = '\<falcon\%(String\|StringEscape\|Comment\)\>' + +" Regex of syntax group names that are strings +let s:syng_string = '\<falcon\%(String\|StringEscape\)\>' + +" Regex that defines blocks. +" +" Note that there's a slight problem with this regex and s:continuation_regex. +" Code like this will be matched by both: +" +" method_call do |(a, b)| +" +" The reason is that the pipe matches a hanging "|" operator. +" +let s:block_regex = + \ '\%(\<do:\@!\>\|%\@<!{\)\s*\%(|\s*(*\s*\%([*@&]\=\h\w*,\=\s*\)\%(,\s*(*\s*[*@&]\=\h\w*\s*)*\s*\)*|\)\=\s*\%(#.*\)\=$' + +let s:block_continuation_regex = '^\s*[^])}\t ].*'.s:block_regex + +" Regex that defines continuation lines. +" TODO: this needs to deal with if ...: and so on +let s:continuation_regex = + \ '\%(%\@<![({[\\.,:*/%+]\|\<and\|\<or\|\%(<%\)\@<![=-]\|\W[|&?]\|||\|&&\)\s*\%(#.*\)\=$' + +" Regex that defines bracket continuations +let s:bracket_continuation_regex = '%\@<!\%([({[]\)\s*\%(#.*\)\=$' + +" Regex that defines continuation lines, not including (, {, or [. +let s:non_bracket_continuation_regex = '\%([\\.,:*/%+]\|\<and\|\<or\|\%(<%\)\@<![=-]\|\W[|&?]\|||\|&&\)\s*\%(#.*\)\=$' + +" Keywords to indent on +let s:falcon_indent_keywords = '^\s*\(case\|catch\|class\|enum\|default\|elif\|else' . + \ '\|for\|function\|if.*"[^"]*:.*"\|if \(\(:\)\@!.\)*$\|loop\|object\|select' . + \ '\|switch\|try\|while\|\w*\s*=\s*\w*([$\)' + +" Keywords to deindent on +let s:falcon_deindent_keywords = '^\s*\(case\|catch\|default\|elif\|else\|end\)' + +" 3. Functions {{{1 +" ============ + +" Check if the character at lnum:col is inside a string, comment, or is ascii. +function s:IsInStringOrComment(lnum, col) + return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_strcom +endfunction + +" Check if the character at lnum:col is inside a string. +function s:IsInString(lnum, col) + return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_string +endfunction + +" Check if the character at lnum:col is inside a string delimiter +function s:IsInStringDelimiter(lnum, col) + return synIDattr(synID(a:lnum, a:col, 1), 'name') == 'falconStringDelimiter' +endfunction + +" Find line above 'lnum' that isn't empty, in a comment, or in a string. +function s:PrevNonBlankNonString(lnum) + let in_block = 0 + let lnum = prevnonblank(a:lnum) + while lnum > 0 + " Go in and out of blocks comments as necessary. + " If the line isn't empty (with opt. comment) or in a string, end search. + let line = getline(lnum) + if line =~ '^=begin' + if in_block + let in_block = 0 + else + break + endif + elseif !in_block && line =~ '^=end' + let in_block = 1 + elseif !in_block && line !~ '^\s*#.*$' && !(s:IsInStringOrComment(lnum, 1) + \ && s:IsInStringOrComment(lnum, strlen(line))) + break + endif + let lnum = prevnonblank(lnum - 1) + endwhile + return lnum +endfunction + +" Find line above 'lnum' that started the continuation 'lnum' may be part of. +function s:GetMSL(lnum) + " Start on the line we're at and use its indent. + let msl = a:lnum + let msl_body = getline(msl) + let lnum = s:PrevNonBlankNonString(a:lnum - 1) + while lnum > 0 + " If we have a continuation line, or we're in a string, use line as MSL. + " Otherwise, terminate search as we have found our MSL already. + let line = getline(lnum) + + if s:Match(line, s:non_bracket_continuation_regex) && + \ s:Match(msl, s:non_bracket_continuation_regex) + " If the current line is a non-bracket continuation and so is the + " previous one, keep its indent and continue looking for an MSL. + " + " Example: + " method_call one, + " two, + " three + " + let msl = lnum + elseif s:Match(lnum, s:non_bracket_continuation_regex) && + \ (s:Match(msl, s:bracket_continuation_regex) || s:Match(msl, s:block_continuation_regex)) + " If the current line is a bracket continuation or a block-starter, but + " the previous is a non-bracket one, respect the previous' indentation, + " and stop here. + " + " Example: + " method_call one, + " two { + " three + " + return lnum + elseif s:Match(lnum, s:bracket_continuation_regex) && + \ (s:Match(msl, s:bracket_continuation_regex) || s:Match(msl, s:block_continuation_regex)) + " If both lines are bracket continuations (the current may also be a + " block-starter), use the current one's and stop here + " + " Example: + " method_call( + " other_method_call( + " foo + return msl + elseif s:Match(lnum, s:block_regex) && + \ !s:Match(msl, s:continuation_regex) && + \ !s:Match(msl, s:block_continuation_regex) + " If the previous line is a block-starter and the current one is + " mostly ordinary, use the current one as the MSL. + " + " Example: + " method_call do + " something + " something_else + return msl + else + let col = match(line, s:continuation_regex) + 1 + if (col > 0 && !s:IsInStringOrComment(lnum, col)) + \ || s:IsInString(lnum, strlen(line)) + let msl = lnum + else + break + endif + endif + + let msl_body = getline(msl) + let lnum = s:PrevNonBlankNonString(lnum - 1) + endwhile + return msl +endfunction + +" Check if line 'lnum' has more opening brackets than closing ones. +function s:ExtraBrackets(lnum) + let opening = {'parentheses': [], 'braces': [], 'brackets': []} + let closing = {'parentheses': [], 'braces': [], 'brackets': []} + + let line = getline(a:lnum) + let pos = match(line, '[][(){}]', 0) + + " Save any encountered opening brackets, and remove them once a matching + " closing one has been found. If a closing bracket shows up that doesn't + " close anything, save it for later. + while pos != -1 + if !s:IsInStringOrComment(a:lnum, pos + 1) + if line[pos] == '(' + call add(opening.parentheses, {'type': '(', 'pos': pos}) + elseif line[pos] == ')' + if empty(opening.parentheses) + call add(closing.parentheses, {'type': ')', 'pos': pos}) + else + let opening.parentheses = opening.parentheses[0:-2] + endif + elseif line[pos] == '{' + call add(opening.braces, {'type': '{', 'pos': pos}) + elseif line[pos] == '}' + if empty(opening.braces) + call add(closing.braces, {'type': '}', 'pos': pos}) + else + let opening.braces = opening.braces[0:-2] + endif + elseif line[pos] == '[' + call add(opening.brackets, {'type': '[', 'pos': pos}) + elseif line[pos] == ']' + if empty(opening.brackets) + call add(closing.brackets, {'type': ']', 'pos': pos}) + else + let opening.brackets = opening.brackets[0:-2] + endif + endif + endif + + let pos = match(line, '[][(){}]', pos + 1) + endwhile + + " Find the rightmost brackets, since they're the ones that are important in + " both opening and closing cases + let rightmost_opening = {'type': '(', 'pos': -1} + let rightmost_closing = {'type': ')', 'pos': -1} + + for opening in opening.parentheses + opening.braces + opening.brackets + if opening.pos > rightmost_opening.pos + let rightmost_opening = opening + endif + endfor + + for closing in closing.parentheses + closing.braces + closing.brackets + if closing.pos > rightmost_closing.pos + let rightmost_closing = closing + endif + endfor + + return [rightmost_opening, rightmost_closing] +endfunction + +function s:Match(lnum, regex) + let col = match(getline(a:lnum), '\C'.a:regex) + 1 + return col > 0 && !s:IsInStringOrComment(a:lnum, col) ? col : 0 +endfunction + +function s:MatchLast(lnum, regex) + let line = getline(a:lnum) + let col = match(line, '.*\zs' . a:regex) + while col != -1 && s:IsInStringOrComment(a:lnum, col) + let line = strpart(line, 0, col) + let col = match(line, '.*' . a:regex) + endwhile + return col + 1 +endfunction + +" 4. FalconGetIndent Routine {{{1 +" ============ + +function FalconGetIndent(...) + " For the current line, use the first argument if given, else v:lnum + let clnum = a:0 ? a:1 : v:lnum + + " Use zero indent at the top of the file + if clnum == 0 + return 0 + endif + + let line = getline(clnum) + let ind = -1 + + " If we got a closing bracket on an empty line, find its match and indent + " according to it. For parentheses we indent to its column - 1, for the + " others we indent to the containing line's MSL's level. Return -1 if fail. + let col = matchend(line, '^\s*[]})]') + if col > 0 && !s:IsInStringOrComment(clnum, col) + call cursor(clnum, col) + let bs = strpart('(){}[]', stridx(')}]', line[col - 1]) * 2, 2) + if searchpair(escape(bs[0], '\['), '', bs[1], 'bW', s:skip_expr) > 0 + if line[col-1]==')' && col('.') != col('$') - 1 + let ind = virtcol('.') - 1 + else + let ind = indent(s:GetMSL(line('.'))) + endif + endif + return ind + endif + + " If we have a deindenting keyword, find its match and indent to its level. + " TODO: this is messy + if s:Match(clnum, s:falcon_deindent_keywords) + call cursor(clnum, 1) + if searchpair(s:end_start_regex, s:end_middle_regex, s:end_end_regex, 'bW', + \ s:end_skip_expr) > 0 + let msl = s:GetMSL(line('.')) + let line = getline(line('.')) + + if strpart(line, 0, col('.') - 1) =~ '=\s*$' && + \ strpart(line, col('.') - 1, 2) !~ 'do' + let ind = virtcol('.') - 1 + elseif getline(msl) =~ '=\s*\(#.*\)\=$' + let ind = indent(line('.')) + else + let ind = indent(msl) + endif + endif + return ind + endif + + " If we are in a multi-line string or line-comment, don't do anything to it. + if s:IsInString(clnum, matchend(line, '^\s*') + 1) + return indent('.') + endif + + " Find a non-blank, non-multi-line string line above the current line. + let lnum = s:PrevNonBlankNonString(clnum - 1) + + " If the line is empty and inside a string, use the previous line. + if line =~ '^\s*$' && lnum != prevnonblank(clnum - 1) + return indent(prevnonblank(clnum)) + endif + + " At the start of the file use zero indent. + if lnum == 0 + return 0 + endif + + " Set up variables for the previous line. + let line = getline(lnum) + let ind = indent(lnum) + + " If the previous line ended with a block opening, add a level of indent. + if s:Match(lnum, s:block_regex) + return indent(s:GetMSL(lnum)) + shiftwidth() + endif + + " If it contained hanging closing brackets, find the rightmost one, find its + " match and indent according to that. + if line =~ '[[({]' || line =~ '[])}]\s*\%(#.*\)\=$' + let [opening, closing] = s:ExtraBrackets(lnum) + + if opening.pos != -1 + if opening.type == '(' && searchpair('(', '', ')', 'bW', s:skip_expr) > 0 + if col('.') + 1 == col('$') + return ind + shiftwidth() + else + return virtcol('.') + endif + else + let nonspace = matchend(line, '\S', opening.pos + 1) - 1 + return nonspace > 0 ? nonspace : ind + shiftwidth() + endif + elseif closing.pos != -1 + call cursor(lnum, closing.pos + 1) + normal! % + + if s:Match(line('.'), s:falcon_indent_keywords) + return indent('.') + shiftwidth() + else + return indent('.') + endif + else + call cursor(clnum, 0) " FIXME: column was vcol + end + endif + + " If the previous line ended with an "end", match that "end"s beginning's + " indent. + let col = s:Match(lnum, '\%(^\|[^.:@$]\)\<end\>\s*\%(#.*\)\=$') + if col > 0 + call cursor(lnum, col) + if searchpair(s:end_start_regex, '', s:end_end_regex, 'bW', + \ s:end_skip_expr) > 0 + let n = line('.') + let ind = indent('.') + let msl = s:GetMSL(n) + if msl != n + let ind = indent(msl) + end + return ind + endif + end + + let col = s:Match(lnum, s:falcon_indent_keywords) + if col > 0 + call cursor(lnum, col) + let ind = virtcol('.') - 1 + shiftwidth() + " TODO: make this better (we need to count them) (or, if a searchpair + " fails, we know that something is lacking an end and thus we indent a + " level + if s:Match(lnum, s:end_end_regex) + let ind = indent('.') + endif + return ind + endif + + " Set up variables to use and search for MSL to the previous line. + let p_lnum = lnum + let lnum = s:GetMSL(lnum) + + " If the previous line wasn't a MSL and is continuation return its indent. + " TODO: the || s:IsInString() thing worries me a bit. + if p_lnum != lnum + if s:Match(p_lnum, s:non_bracket_continuation_regex) || s:IsInString(p_lnum,strlen(line)) + return ind + endif + endif + + " Set up more variables, now that we know we wasn't continuation bound. + let line = getline(lnum) + let msl_ind = indent(lnum) + + " If the MSL line had an indenting keyword in it, add a level of indent. + " TODO: this does not take into account contrived things such as + " module Foo; class Bar; end + if s:Match(lnum, s:falcon_indent_keywords) + let ind = msl_ind + shiftwidth() + if s:Match(lnum, s:end_end_regex) + let ind = ind - shiftwidth() + endif + return ind + endif + + " If the previous line ended with [*+/.,-=], but wasn't a block ending or a + " closing bracket, indent one extra level. + if s:Match(lnum, s:non_bracket_continuation_regex) && !s:Match(lnum, '^\s*\([\])}]\|end\)') + if lnum == p_lnum + let ind = msl_ind + shiftwidth() + else + let ind = msl_ind + endif + return ind + endif + + return ind +endfunction + +" }}}1 + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim: set sw=4 sts=4 et tw=80 : diff --git a/runtime/indent/fennel.vim b/runtime/indent/fennel.vim new file mode 100644 index 0000000..e12aa18 --- /dev/null +++ b/runtime/indent/fennel.vim @@ -0,0 +1,12 @@ +" Vim indent file +" Language: Fennel +" Maintainer: Gregory Anders <greg[NOSPAM]@gpanders.com> +" Last Change: 2022 Apr 20 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +" Use the Lisp indenting +runtime! indent/lisp.vim diff --git a/runtime/indent/fish.vim b/runtime/indent/fish.vim new file mode 100644 index 0000000..e7678cb --- /dev/null +++ b/runtime/indent/fish.vim @@ -0,0 +1,85 @@ +" Vim indent file +" Language: fish +" Maintainer: Nicholas Boyle (github.com/nickeb96) +" Repository: https://github.com/nickeb96/fish.vim +" Last Change: February 4, 2023 +" 2023 Aug 28 by Vim Project (undo_indent) + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetFishIndent(v:lnum) +setlocal indentkeys+==end,=else,=case + +let b:undo_indent = "setlocal indentexpr< indentkeys<" + +function s:PrevCmdStart(linenum) + let l:linenum = a:linenum + " look for the first line that isn't a line continuation + while l:linenum > 1 && getline(l:linenum - 1) =~# '\\$' + let l:linenum = l:linenum - 1 + endwhile + return l:linenum +endfunction + +function GetFishIndent(lnum) + let l:shiftwidth = shiftwidth() + + let l:prevlnum = prevnonblank(a:lnum - 1) + if l:prevlnum ==# 0 + return 0 + endif + + " if the previous line ended with a line continuation + if getline(a:lnum - 1) =~# '\\$' + if a:lnum ==# 0 || getline(a:lnum - 2) !~# '\\$' + " this is the first line continuation in a chain, so indent it + return indent(a:lnum - 1) + l:shiftwidth + else + " use the same indentation as the previous continued line + return indent(a:lnum - 1) + endif + endif + + let l:prevlnum = s:PrevCmdStart(l:prevlnum) + + let l:prevline = getline(l:prevlnum) + if l:prevline =~# '^\s*\(begin\|if\|else\|while\|for\|function\|case\|switch\)\>' + let l:indent = l:shiftwidth + else + let l:indent = 0 + endif + + let l:line = getline(a:lnum) + if l:line =~# '^\s*end\>' + " find end's matching start + let l:depth = 1 + let l:currentlnum = a:lnum + while l:depth > 0 && l:currentlnum > 0 + let l:currentlnum = s:PrevCmdStart(prevnonblank(l:currentlnum - 1)) + let l:currentline = getline(l:currentlnum) + if l:currentline =~# '^\s*end\>' + let l:depth = l:depth + 1 + elseif l:currentline =~# '^\s*\(begin\|if\|while\|for\|function\|switch\)\>' + let l:depth = l:depth - 1 + endif + endwhile + if l:currentline =~# '^\s*switch\>' + return indent(l:currentlnum) + else + return indent(l:prevlnum) + l:indent - l:shiftwidth + endif + elseif l:line =~# '^\s*else\>' + return indent(l:prevlnum) + l:indent - l:shiftwidth + elseif l:line =~# '^\s*case\>' + if getline(l:prevlnum) =~# '^\s*switch\>' + return indent(l:prevlnum) + l:indent + else + return indent(l:prevlnum) + l:indent - l:shiftwidth + endif + else + return indent(l:prevlnum) + l:indent + endif +endfunction diff --git a/runtime/indent/fortran.vim b/runtime/indent/fortran.vim new file mode 100644 index 0000000..392b2d2 --- /dev/null +++ b/runtime/indent/fortran.vim @@ -0,0 +1,226 @@ +" Vim indent file +" Language: Fortran 2023 (and Fortran 2018, 2008, 2003, 95, 90, 77, 66) +" Version: (v50) 2023 December 22 +" Maintainers: Ajit J. Thakkar <ajit@unb.ca>; <https://ajit.ext.unb.ca/> +" Joshua Hollett <j.hollett@uwinnipeg.ca> +" Usage: For instructions, do :help fortran-indent from Vim +" Credits: +" Version 0.1 was created in September 2000 by Ajit Thakkar. +" Since then, useful suggestions and contributions have been made, in order, by: +" Albert Oliver Serra, Takuya Fujiwara, Philipp Edelmann, Eisuke Kawashima, +" Louis Cochen, and Doug Kearns. + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +let s:cposet=&cpoptions +set cpoptions&vim +let b:undo_indent = "setl inde< indk<" + +setlocal indentkeys+==~end,=~case,=~if,=~else,=~do,=~where,=~elsewhere,=~select +setlocal indentkeys+==~endif,=~enddo,=~endwhere,=~endselect,=~elseif +setlocal indentkeys+==~interface,=~forall,=~associate,=~block,=~enum,=~critical +setlocal indentkeys+==~endforall,=~endassociate,=~endblock,=~endenum,=~endcritical +if exists("b:fortran_indent_more") || exists("g:fortran_indent_more") + setlocal indentkeys+==~function,=~subroutine,=~module,=~contains,=~program + setlocal indentkeys+==~endfunction,=~endsubroutine,=~endmodule + setlocal indentkeys+==~endprogram +endif + +" Determine whether this is a fixed or free format source file +" if this hasn't been done yet using the priority: +" buffer-local value +" > global value +" > file extension as in Intel ifort, gcc (gfortran), NAG, Pathscale, and Cray compilers +if !exists("b:fortran_fixed_source") + if exists("fortran_free_source") + " User guarantees free source form + let b:fortran_fixed_source = 0 + elseif exists("fortran_fixed_source") + " User guarantees fixed source form + let b:fortran_fixed_source = 1 + elseif expand("%:e") =~? '^f\%(90\|95\|03\|08\)$' + " Free-form file extension defaults as in Intel ifort, gcc(gfortran), NAG, Pathscale, and Cray compilers + let b:fortran_fixed_source = 0 + elseif expand("%:e") =~? '^\%(f\|f77\|for\)$' + " Fixed-form file extension defaults + let b:fortran_fixed_source = 1 + else + " Modern fortran compilers still allow both fixed and free source form + " Assume fixed source form unless signs of free source form + " are detected in the first five columns of the first s:lmax lines. + " Detection becomes more accurate and time-consuming if more lines + " are checked. Increase the limit below if you keep lots of comments at + " the very top of each file and you have a fast computer. + let s:lmax = 500 + if ( s:lmax > line("$") ) + let s:lmax = line("$") + endif + let b:fortran_fixed_source = 1 + let s:ln=1 + while s:ln <= s:lmax + let s:test = strpart(getline(s:ln),0,5) + if s:test !~ '^[Cc*]' && s:test !~ '^ *[!#]' && s:test =~ '[^ 0-9\t]' && s:test !~ '^[ 0-9]*\t' + let b:fortran_fixed_source = 0 + break + endif + let s:ln = s:ln + 1 + endwhile + endif +endif + +" Define the appropriate indent function but only once +if (b:fortran_fixed_source == 1) + setlocal indentexpr=FortranGetFixedIndent() + if exists("*FortranGetFixedIndent") + let &cpoptions = s:cposet + unlet s:cposet + finish + endif +else + setlocal indentexpr=FortranGetFreeIndent() + if exists("*FortranGetFreeIndent") + let &cpoptions = s:cposet + unlet s:cposet + finish + endif +endif + +function FortranGetIndent(lnum) + let ind = indent(a:lnum) + let prevline=getline(a:lnum) + " Strip tail comment + let prevstat=substitute(prevline, '!.*$', '', '') + let prev2line=getline(a:lnum-1) + let prev2stat=substitute(prev2line, '!.*$', '', '') + + "Indent do loops only if they are all guaranteed to be of do/end do type + if exists("b:fortran_do_enddo") || exists("g:fortran_do_enddo") + if prevstat =~? '^\s*\(\d\+\s\)\=\s*\(\a\w*\s*:\)\=\s*do\>' + let ind = ind + shiftwidth() + endif + if getline(v:lnum) =~? '^\s*\(\d\+\s\)\=\s*end\s*do\>' + let ind = ind - shiftwidth() + endif + endif + + "Add a shiftwidth to statements following if, else, else if, case, class, + "where, else where, forall, type, interface and associate statements + if prevstat =~? '^\s*\(case\|class\s\+is\|else\|else\s*if\|else\s*where\)\>' + \ ||prevstat=~? '^\s*\(type\|rank\|interface\|associate\|enum\|critical\)\>' + \ ||prevstat=~? '^\s*change\s\+team\>' + \ ||prevstat=~?'^\s*\(\d\+\s\)\=\s*\(\a\w*\s*:\)\=\s*\(forall\|where\|block\)\>' + \ ||prevstat=~? '^\s*\(\d\+\s\)\=\s*\(\a\w*\s*:\)\=\s*if\>' + let ind = ind + shiftwidth() + " Remove unwanted indent after logical and arithmetic ifs + if prevstat =~? '\<if\>' && prevstat !~? '\<then\>' + let ind = ind - shiftwidth() + endif + " Remove unwanted indent after type( statements + if prevstat =~? '^\s*type\s*(' + let ind = ind - shiftwidth() + endif + endif + + "Indent program units unless instructed otherwise + if !exists("b:fortran_indent_less") && !exists("g:fortran_indent_less") + let prefix='\(\(pure\|impure\|elemental\|recursive\)\s\+\)\{,2}' + let type='\(\(integer\|real\|double\s\+precision\|complex\|logical' + \.'\|character\|type\|class\)\s*\S*\s\+\)\=' + if prevstat =~? '^\s*\(contains\|submodule\|program\)\>' + \ ||prevstat =~? '^\s*'.'module\>\(\s*\procedure\)\@!' + \ ||prevstat =~? '^\s*'.prefix.'subroutine\>' + \ ||prevstat =~? '^\s*'.prefix.type.'function\>' + \ ||prevstat =~? '^\s*'.type.prefix.'function\>' + let ind = ind + shiftwidth() + endif + if getline(v:lnum) =~? '^\s*contains\>' + \ ||getline(v:lnum)=~? '^\s*end\s*' + \ .'\(function\|subroutine\|module\|submodule\|program\)\>' + let ind = ind - shiftwidth() + endif + endif + + "Subtract a shiftwidth from else, else if, elsewhere, case, class, end if, + " end where, end select, end forall, end interface, end associate, + " end enum, end type, end block, end team and end type statements + if getline(v:lnum) =~? '^\s*\(\d\+\s\)\=\s*' + \. '\(else\|else\s*if\|else\s*where\|case\|class\|rank\|type\s\+is\|' + \. 'end\s*\(if\|where\|select\|interface\|critical\|team\|' + \. 'type\|forall\|associate\|enum\|block\)\)\>' + let ind = ind - shiftwidth() + " Fix indent for case statement immediately after select + if prevstat =~? '\<select\s*\(case\|type\)\>' + let ind = ind + shiftwidth() + endif + endif + + "First continuation line + if prevstat =~ '&\s*$' && prev2stat !~ '&\s*$' + let ind = ind + shiftwidth() + endif + "Line after last continuation line + if prevstat !~ '&\s*$' && prev2stat =~ '&\s*$' && prevstat !~? '\<then\>' + let ind = ind - shiftwidth() + endif + + return ind +endfunction + +function FortranGetFreeIndent() + "Find the previous non-blank line + let lnum = prevnonblank(v:lnum - 1) + + "Use zero indent at the top of the file + if lnum == 0 + return 0 + endif + + let ind=FortranGetIndent(lnum) + return ind +endfunction + +function FortranGetFixedIndent() + let currline=getline(v:lnum) + "Don't indent comments, continuation lines and labelled lines + if strpart(currline,0,6) =~ '[^ \t]' + let ind = indent(v:lnum) + return ind + endif + + "Find the previous line which is not blank, not a comment, + "not a continuation line, and does not have a label + let lnum = v:lnum - 1 + while lnum > 0 + let prevline=getline(lnum) + if (prevline =~ "^[C*!]") || (prevline =~ "^\s*$") + \ || (strpart(prevline,5,1) !~ "[ 0]") + " Skip comments, blank lines and continuation lines + let lnum = lnum - 1 + else + let test=strpart(prevline,0,5) + if test =~ "[0-9]" + " Skip lines with statement numbers + let lnum = lnum - 1 + else + break + endif + endif + endwhile + + "First line must begin at column 7 + if lnum == 0 + return 6 + endif + + let ind=FortranGetIndent(lnum) + return ind +endfunction + +let &cpoptions = s:cposet +unlet s:cposet + +" vim:sw=2 tw=130 diff --git a/runtime/indent/framescript.vim b/runtime/indent/framescript.vim new file mode 100644 index 0000000..4611d34 --- /dev/null +++ b/runtime/indent/framescript.vim @@ -0,0 +1,44 @@ +" Vim indent file +" Language: FrameScript +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Last Change: 24 Sep 2021 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetFrameScriptIndent() +setlocal indentkeys=!^F,o,O,0=~Else,0=~EndIf,0=~EndLoop,0=~EndSub +setlocal nosmartindent + +let b:undo_indent = "setl inde< indk< si<" + +if exists("*GetFrameScriptIndent") + finish +endif + +function GetFrameScriptIndent() + let lnum = prevnonblank(v:lnum - 1) + + if lnum == 0 + return 0 + endif + + if getline(v:lnum) =~ '^\s*\*' + return cindent(v:lnum) + endif + + let ind = indent(lnum) + + if getline(lnum) =~? '^\s*\%(If\|Loop\|Sub\)' + let ind = ind + shiftwidth() + endif + + if getline(v:lnum) =~? '^\s*\%(Else\|End\%(If\|Loop\|Sub\)\)' + let ind = ind - shiftwidth() + endif + + return ind +endfunction diff --git a/runtime/indent/freebasic.vim b/runtime/indent/freebasic.vim new file mode 100644 index 0000000..248b928 --- /dev/null +++ b/runtime/indent/freebasic.vim @@ -0,0 +1,11 @@ +" Vim indent file +" Language: FreeBASIC +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2022 Jan 24 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +runtime! indent/vb.vim diff --git a/runtime/indent/gdscript.vim b/runtime/indent/gdscript.vim new file mode 100644 index 0000000..39f68c6 --- /dev/null +++ b/runtime/indent/gdscript.vim @@ -0,0 +1,148 @@ +vim9script + +# Vim indent file +# Language: gdscript (Godot game engine) +# Maintainer: Maxim Kim <habamax@gmail.com> +# Based on python indent file. + +if exists("b:did_indent") + finish +endif +b:did_indent = 1 + +var undo_opts = "setl indentexpr< indentkeys< lisp< autoindent<" + +if exists('b:undo_indent') + b:undo_indent ..= "|" .. undo_opts +else + b:undo_indent = undo_opts +endif + +setlocal nolisp +setlocal autoindent +setlocal indentexpr=GDScriptIndent() +setlocal indentkeys+=<:>,=elif,=except + + +def GDScriptIndent(): number + # If this line is explicitly joined: If the previous line was also joined, + # line it up with that one, otherwise add two 'shiftwidth' + if getline(v:lnum - 1) =~ '\\$' + if v:lnum > 1 && getline(v:lnum - 2) =~ '\\$' + return indent(v:lnum - 1) + endif + return indent(v:lnum - 1) + (shiftwidth() * 2) + endif + + # If the start of the line is in a string don't change the indent. + if has('syntax_items') && synIDattr(synID(v:lnum, 1, 1), "name") =~ "String$" + return -1 + endif + + # Search backwards for the previous non-empty line. + var plnum = prevnonblank(v:lnum - 1) + + if plnum == 0 + # This is the first non-empty line, use zero indent. + return 0 + endif + + var plindent = indent(plnum) + var plnumstart = plnum + + # Get the line and remove a trailing comment. + # Use syntax highlighting attributes when possible. + var pline = getline(plnum) + var pline_len = strlen(pline) + if has('syntax_items') + # If the last character in the line is a comment, do a binary search for + # the start of the comment. synID() is slow, a linear search would take + # too long on a long line. + if synIDattr(synID(plnum, pline_len, 1), "name") =~ "\\(Comment\\|Todo\\)$" + var min = 1 + var max = pline_len + while min < max + var col = (min + max) / 2 + if synIDattr(synID(plnum, col, 1), "name") =~ "\\(Comment\\|Todo\\)$" + max = col + else + min = col + 1 + endif + endwhile + pline = strpart(pline, 0, min - 1) + endif + else + var col = 0 + while col < pline_len + if pline[col] == '#' + pline = strpart(pline, 0, col) + break + endif + col = col + 1 + endwhile + endif + + + # When "inside" parenthesis: If at the first line below the parenthesis add + # one 'shiftwidth' ("inside" is simplified and not really checked) + # my_var = ( + # a + # + b + # + c + # ) + if pline =~ '[({\[]\s*$' + return indent(plnum) + shiftwidth() + endif + + + # If the previous line ended with a colon, indent this line + if pline =~ ':\s*$' + return plindent + shiftwidth() + endif + + # If the previous line was a stop-execution statement... + if getline(plnum) =~ '^\s*\(break\|continue\|raise\|return\|pass\)\>' + # See if the user has already dedented + if indent(v:lnum) > indent(plnum) - shiftwidth() + # If not, recommend one dedent + return indent(plnum) - shiftwidth() + endif + # Otherwise, trust the user + return -1 + endif + + # If the current line begins with a keyword that lines up with "try" + if getline(v:lnum) =~ '^\s*\(except\|finally\)\>' + var lnum = v:lnum - 1 + while lnum >= 1 + if getline(lnum) =~ '^\s*\(try\|except\)\>' + var ind = indent(lnum) + if ind >= indent(v:lnum) + return -1 # indent is already less than this + endif + return ind # line up with previous try or except + endif + lnum = lnum - 1 + endwhile + return -1 # no matching "try"! + endif + + + # If the current line begins with a header keyword, dedent + if getline(v:lnum) =~ '^\s*\(elif\|else\)\>' + + # Unless the previous line was a one-liner + if getline(plnumstart) =~ '^\s*\(for\|if\|try\)\>' + return plindent + endif + + # Or the user has already dedented + if indent(v:lnum) <= plindent - shiftwidth() + return -1 + endif + + return plindent - shiftwidth() + endif + + return -1 +enddef diff --git a/runtime/indent/gitconfig.vim b/runtime/indent/gitconfig.vim new file mode 100644 index 0000000..6a670ee --- /dev/null +++ b/runtime/indent/gitconfig.vim @@ -0,0 +1,38 @@ +" Vim indent file +" Language: git config file +" Maintainer: Tim Pope <vimNOSPAM@tpope.org> +" Last Change: 2017 Jun 13 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal autoindent +setlocal indentexpr=GetGitconfigIndent() +setlocal indentkeys=o,O,*<Return>,0[,],0;,0#,=,!^F + +let b:undo_indent = 'setl ai< inde< indk<' + +" Only define the function once. +if exists("*GetGitconfigIndent") + finish +endif + +function! GetGitconfigIndent() + let sw = shiftwidth() + let line = getline(prevnonblank(v:lnum-1)) + let cline = getline(v:lnum) + if line =~ '\\\@<!\%(\\\\\)*\\$' + " odd number of slashes, in a line continuation + return 2 * sw + elseif cline =~ '^\s*\[' + return 0 + elseif cline =~ '^\s*\a' + return sw + elseif cline == '' && line =~ '^\[' + return sw + else + return -1 + endif +endfunction diff --git a/runtime/indent/gitolite.vim b/runtime/indent/gitolite.vim new file mode 100644 index 0000000..22be687 --- /dev/null +++ b/runtime/indent/gitolite.vim @@ -0,0 +1,51 @@ +" Vim indent file +" Language: gitolite configuration +" URL: https://github.com/sitaramc/gitolite/blob/master/contrib/vim/indent/gitolite.vim +" (https://raw.githubusercontent.com/sitaramc/gitolite/master/contrib/vim/indent/gitolite.vim) +" Maintainer: Sitaram Chamarty <sitaramc@gmail.com> +" (former Maintainer: Teemu Matilainen <teemu.matilainen@iki.fi>) +" Last Change: 2022 Apr 06 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal autoindent +setlocal indentexpr=GetGitoliteIndent() +setlocal indentkeys=o,O,*<Return>,!^F,=repo,\",= + +let b:undo_indent = "setl ai< inde< indk<" + +" Only define the function once. +if exists("*GetGitoliteIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +function! GetGitoliteIndent() + let prevln = prevnonblank(v:lnum-1) + let pline = getline(prevln) + let cline = getline(v:lnum) + + if cline =~ '^\s*\(C\|R\|RW\|RW+\|RWC\|RW+C\|RWD\|RW+D\|RWCD\|RW+CD\|-\)[ \t=]' + return shiftwidth() + elseif cline =~ '^\s*config\s' + return shiftwidth() + elseif cline =~ '^\s*option\s' + return shiftwidth() + elseif pline =~ '^\s*repo\s' && cline =~ '^\s*\(#.*\)\?$' + return shiftwidth() + elseif cline =~ '^\s*#' + return indent(prevln) + elseif cline =~ '^\s*$' + return -1 + else + return 0 + endif +endfunction + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/indent/go.vim b/runtime/indent/go.vim new file mode 100644 index 0000000..a9b1d8d --- /dev/null +++ b/runtime/indent/go.vim @@ -0,0 +1,69 @@ +" Vim indent file +" Language: Go +" Maintainer: David Barnett (https://github.com/google/vim-ft-go) +" Last Change: 2017 Jun 13 +" 2023 Aug 28 by Vim Project (undo_indent) +" +" TODO: +" - function invocations split across lines +" - general line splits (line ends in an operator) + +if exists('b:did_indent') + finish +endif +let b:did_indent = 1 + +" C indentation is too far off useful, mainly due to Go's := operator. +" Let's just define our own. +setlocal nolisp +setlocal autoindent +setlocal indentexpr=GoIndent(v:lnum) +setlocal indentkeys+=<:>,0=},0=) + +let b:undo_indent = "setl ai< inde< indk< lisp<" + +if exists('*GoIndent') + finish +endif + +function! GoIndent(lnum) + let l:prevlnum = prevnonblank(a:lnum-1) + if l:prevlnum == 0 + " top of file + return 0 + endif + + " grab the previous and current line, stripping comments. + let l:prevl = substitute(getline(l:prevlnum), '//.*$', '', '') + let l:thisl = substitute(getline(a:lnum), '//.*$', '', '') + let l:previ = indent(l:prevlnum) + + let l:ind = l:previ + + if l:prevl =~ '[({]\s*$' + " previous line opened a block + let l:ind += shiftwidth() + endif + if l:prevl =~# '^\s*\(case .*\|default\):$' + " previous line is part of a switch statement + let l:ind += shiftwidth() + endif + " TODO: handle if the previous line is a label. + + if l:thisl =~ '^\s*[)}]' + " this line closed a block + let l:ind -= shiftwidth() + endif + + " Colons are tricky. + " We want to outdent if it's part of a switch ("case foo:" or "default:"). + " We ignore trying to deal with jump labels because (a) they're rare, and + " (b) they're hard to disambiguate from a composite literal key. + if l:thisl =~# '^\s*\(case .*\|default\):$' + let l:ind -= shiftwidth() + endif + + return l:ind +endfunction + +" vim: sw=2 sts=2 et diff --git a/runtime/indent/gyp.vim b/runtime/indent/gyp.vim new file mode 100644 index 0000000..c3980ac --- /dev/null +++ b/runtime/indent/gyp.vim @@ -0,0 +1,7 @@ +" Vim indent file +" Language: GYP +" Maintainer: ObserverOfTime <chronobserver@disroot.org> +" Last Change: 2022 Sep 27 + +" JSON indent works well +runtime! indent/json.vim diff --git a/runtime/indent/haml.vim b/runtime/indent/haml.vim new file mode 100644 index 0000000..acd99d9 --- /dev/null +++ b/runtime/indent/haml.vim @@ -0,0 +1,76 @@ +" Vim indent file +" Language: Haml +" Maintainer: Tim Pope <vimNOSPAM@tpope.org> +" Last Change: 2022 Mar 15 + +if exists("b:did_indent") + finish +endif +runtime! indent/ruby.vim +unlet! b:did_indent +let b:did_indent = 1 + +setlocal autoindent +setlocal indentexpr=GetHamlIndent() +setlocal indentkeys=o,O,*<Return>,},],0),!^F,=end,=else,=elsif,=rescue,=ensure,=when + +let b:undo_indent = "setl ai< inde< indk<" + +" Only define the function once. +if exists("*GetHamlIndent") + finish +endif + +let s:attributes = '\%({.\{-\}}\|\[.\{-\}\]\)' +let s:tag = '\%([%.#][[:alnum:]_-]\+\|'.s:attributes.'\)*[<>]*' + +if !exists('g:haml_self_closing_tags') + let g:haml_self_closing_tags = 'base|link|meta|br|hr|img|input' +endif + +function! GetHamlIndent() + let lnum = prevnonblank(v:lnum-1) + if lnum == 0 + return 0 + endif + let line = substitute(getline(lnum),'\s\+$','','') + let cline = substitute(substitute(getline(v:lnum),'\s\+$','',''),'^\s\+','','') + let lastcol = strlen(line) + let line = substitute(line,'^\s\+','','') + let indent = indent(lnum) + let cindent = indent(v:lnum) + let sw = shiftwidth() + if cline =~# '\v^-\s*%(elsif|else|when)>' + let indent = cindent < indent ? cindent : indent - sw + endif + let increase = indent + sw + if indent == indent(lnum) + let indent = cindent <= indent ? -1 : increase + endif + + let group = synIDattr(synID(lnum,lastcol,1),'name') + + if line =~ '^!!!' + return indent + elseif line =~ '^/\%(\[[^]]*\]\)\=$' + return increase + elseif group == 'hamlFilter' + return increase + elseif line =~ '^'.s:tag.'[&!]\=[=~-]\s*\%(\%(if\|else\|elsif\|unless\|case\|when\|while\|until\|for\|begin\|module\|class\|def\)\>\%(.*\<end\>\)\@!\|.*do\%(\s*|[^|]*|\)\=\s*$\)' + return increase + elseif line =~ '^'.s:tag.'[&!]\=[=~-].*,\s*$' + return increase + elseif line == '-#' + return increase + elseif group =~? '\v^(hamlSelfCloser)$' || line =~? '^%\v%('.g:haml_self_closing_tags.')>' + return indent + elseif group =~? '\v^%(hamlTag|hamlAttributesDelimiter|hamlObjectDelimiter|hamlClass|hamlId|htmlTagName|htmlSpecialTagName)$' + return increase + elseif synIDattr(synID(v:lnum,1,1),'name') ==? 'hamlRubyFilter' + return GetRubyIndent() + else + return indent + endif +endfunction + +" vim:set sw=2: diff --git a/runtime/indent/hamster.vim b/runtime/indent/hamster.vim new file mode 100644 index 0000000..ae5c3fd --- /dev/null +++ b/runtime/indent/hamster.vim @@ -0,0 +1,70 @@ +" Vim indent file +" Language: Hamster Script +" Version: 2.0.6.1 +" Last Change: 2021 Oct 11 +" Maintainer: David Fishburn <dfishburn dot vim at gmail dot com> +" Download: https://www.vim.org/scripts/script.php?script_id=1099 +" +" 2.0.6.1 (Oct 2021) +" Added b:undo_indent +" Added cpo check +" + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentkeys+==~if,=~else,=~endif,=~endfor,=~endwhile +setlocal indentkeys+==~do,=~until,=~while,=~repeat,=~for,=~loop +setlocal indentkeys+==~sub,=~endsub + +let b:undo_indent = "setl indentkeys<" + +" Define the appropriate indent function but only once +setlocal indentexpr=HamGetFreeIndent() +if exists("*HamGetFreeIndent") + finish +endif + +let s:keepcpo = &cpo +set cpo&vim + +function HamGetIndent(lnum) + let ind = indent(a:lnum) + let prevline=getline(a:lnum) + + " Add a shiftwidth to statements following if, else, elseif, + " case, select, default, do, until, while, for, start + if prevline =~? '^\s*\<\(if\|else\%(if\)\?\|for\|repeat\|do\|while\|sub\)\>' + let ind = ind + shiftwidth() + endif + + " Subtract a shiftwidth from else, elseif, end(if|while|for), until + let line = getline(v:lnum) + if line =~? '^\s*\(else\|elseif\|loop\|until\|end\%(if\|while\|for\|sub\)\)\>' + let ind = ind - shiftwidth() + endif + + return ind +endfunction + +function HamGetFreeIndent() + " Find the previous non-blank line + let lnum = prevnonblank(v:lnum - 1) + + " Use zero indent at the top of the file + if lnum == 0 + return 0 + endif + + let ind=HamGetIndent(lnum) + return ind +endfunction + +" Restore: +let &cpo = s:keepcpo +unlet s:keepcpo + +" vim:sw=2 tw=80 diff --git a/runtime/indent/hare.vim b/runtime/indent/hare.vim new file mode 100644 index 0000000..0a9d8da --- /dev/null +++ b/runtime/indent/hare.vim @@ -0,0 +1,141 @@ +" Vim indent file +" Language: Hare +" Maintainer: Amelia Clarke <me@rsaihe.dev> +" Last Change: 2022 Sep 22 +" 2023 Aug 28 by Vim Project (undo_indent) + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +if !has("cindent") || !has("eval") + finish +endif + +setlocal cindent + +" L0 -> don't deindent labels +" (s -> use one indent after a trailing ( +" m1 -> if ) starts a line, indent it the same as its matching ( +" ks -> add an extra indent to extra lines in an if expression or for expression +" j1 -> indent code inside {} one level when in parentheses +" J1 -> see j1 +" *0 -> don't search for unclosed block comments +" #1 -> don't deindent lines that begin with # +setlocal cinoptions=L0,(s,m1,ks,j1,J1,*0,#1 + +" Controls which keys reindent the current line. +" 0{ -> { at beginning of line +" 0} -> } at beginning of line +" 0) -> ) at beginning of line +" 0] -> ] at beginning of line +" !^F -> <C-f> (not inserted) +" o -> <CR> or `o` command +" O -> `O` command +" e -> else +" 0=case -> case +setlocal indentkeys=0{,0},0),0],!^F,o,O,e,0=case + +setlocal cinwords=if,else,for,switch,match + +setlocal indentexpr=GetHareIndent() + +let b:undo_indent = "setl cin< cino< cinw< inde< indk<" + +function! FloorCindent(lnum) + return cindent(a:lnum) / shiftwidth() * shiftwidth() +endfunction + +function! GetHareIndent() + let line = getline(v:lnum) + let prevlnum = prevnonblank(v:lnum - 1) + let prevline = getline(prevlnum) + let prevprevline = getline(prevnonblank(prevlnum - 1)) + + " This is all very hacky and imperfect, but it's tough to do much better when + " working with regex-based indenting rules. + + " If the previous line ended with =, indent by one shiftwidth. + if prevline =~# '\v\=\s*(//.*)?$' + return indent(prevlnum) + shiftwidth() + endif + + " If the previous line ended in a semicolon and the line before that ended + " with =, deindent by one shiftwidth. + if prevline =~# '\v;\s*(//.*)?$' && prevprevline =~# '\v\=\s*(//.*)?$' + return indent(prevlnum) - shiftwidth() + endif + + " TODO: The following edge-case is still indented incorrectly: + " case => + " if (foo) { + " bar; + " }; + " | // cursor is incorrectly deindented by one shiftwidth. + " + " This only happens if the {} block is the first statement in the case body. + " If `case` is typed, the case will also be incorrectly deindented by one + " shiftwidth. Are you having fun yet? + + " Deindent cases. + if line =~# '\v^\s*case' + " If the previous line was also a case, don't do any special indenting. + if prevline =~# '\v^\s*case' + return indent(prevlnum) + end + + " If the previous line was a multiline case, deindent by one shiftwidth. + if prevline =~# '\v\=\>\s*(//.*)?$' + return indent(prevlnum) - shiftwidth() + endif + + " If the previous line started a block, deindent by one shiftwidth. + " This handles the first case in a switch/match block. + if prevline =~# '\v\{\s*(//.*)?$' + return FloorCindent(v:lnum) - shiftwidth() + end + + " If the previous line ended in a semicolon and the line before that wasn't + " a case, deindent by one shiftwidth. + if prevline =~# '\v;\s*(//.*)?$' && prevprevline !~# '\v\=\>\s*(//.*)?$' + return FloorCindent(v:lnum) - shiftwidth() + end + + let l:indent = FloorCindent(v:lnum) + + " If a normal cindent would indent the same amount as the previous line, + " deindent by one shiftwidth. This fixes some issues with `case let` blocks. + if l:indent == indent(prevlnum) + return l:indent - shiftwidth() + endif + + " Otherwise, do a normal cindent. + return l:indent + endif + + " Don't indent an extra shiftwidth for cases which span multiple lines. + if prevline =~# '\v\=\>\s*(//.*)?$' && prevline !~# '\v^\s*case\W' + return indent(prevlnum) + endif + + " Indent the body of a case. + " If the previous line ended in a semicolon and the line before that was a + " case, don't do any special indenting. + if prevline =~# '\v;\s*(//.*)?$' && prevprevline =~# '\v\=\>\s*(//.*)?$' && line !~# '\v^\s*}' + return indent(prevlnum) + endif + + let l:indent = FloorCindent(v:lnum) + + " If the previous line was a case and a normal cindent wouldn't indent, indent + " an extra shiftwidth. + if prevline =~# '\v\=\>\s*(//.*)?$' && l:indent == indent(prevlnum) + return l:indent + shiftwidth() + endif + + " If everything above is false, do a normal cindent. + return l:indent +endfunction + +" vim: tabstop=2 shiftwidth=2 expandtab diff --git a/runtime/indent/hog.vim b/runtime/indent/hog.vim new file mode 100644 index 0000000..ece587d --- /dev/null +++ b/runtime/indent/hog.vim @@ -0,0 +1,77 @@ +" Vim indent file +" Language: hog (Snort.conf) +" Maintainer: Victor Roemer, <vroemer@badsec.org> +" Last Change: Mar 7, 2013 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 +let b:undo_indent = 'setlocal smartindent< indentexpr< indentkeys<' + +setlocal nosmartindent +setlocal indentexpr=GetHogIndent() +setlocal indentkeys+=!^F,o,O,0# + +" Only define the function once. +if exists("*GetHogIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +let s:syn_blocks = '\<SnortRuleTypeBody\>' + +function s:IsInBlock(lnum) + return synIDattr(synID(a:lnum, 1, 1), 'name') =~ s:syn_blocks +endfunction + +function GetHogIndent() + let prevlnum = prevnonblank(v:lnum-1) + + " Comment blocks have identical indent + if getline(v:lnum) =~ '^\s*#' && getline(prevlnum) =~ '^\s*#' + return indent(prevlnum) + endif + + " Ignore comment lines when calculating indent + while getline(prevlnum) =~ '^\s*#' + let prevlnum = prevnonblank(prevlnum-1) + if !prevlnum + return previndent + endif + endwhile + + " Continuation of a line that wasn't indented + let prevline = getline(prevlnum) + if prevline =~ '^\k\+.*\\\s*$' + return shiftwidth() + endif + + " Continuation of a line that was indented + if prevline =~ '\k\+.*\\\s*$' + return indent(prevlnum) + endif + + " Indent the next line if previous line contained a start of a block + " definition ('{' or '('). + if prevline =~ '^\k\+[^#]*{}\@!\s*$' " TODO || prevline =~ '^\k\+[^#]*()\@!\s*$' + return shiftwidth() + endif + + " Match inside of a block + if s:IsInBlock(v:lnum) + if prevline =~ "^\k\+.*$" + return shiftwidth() + else + return indent(prevlnum) + endif + endif + + return 0 +endfunction + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/indent/html.vim b/runtime/indent/html.vim new file mode 100644 index 0000000..2fa10cc --- /dev/null +++ b/runtime/indent/html.vim @@ -0,0 +1,1094 @@ +" Vim indent script for HTML +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Original Author: Andy Wokula <anwoku@yahoo.de> +" Last Change: 2023 Aug 13 +" Version: 1.0 "{{{ +" Description: HTML indent script with cached state for faster indenting on a +" range of lines. +" Supports template systems through hooks. +" Supports Closure stylesheets. +" +" Credits: +" indent/html.vim (2006 Jun 05) from J. Zellner +" indent/css.vim (2006 Dec 20) from N. Weibull +" +" History: +" 2014 June (v1.0) overhaul (Bram) +" 2012 Oct 21 (v0.9) added support for shiftwidth() +" 2011 Sep 09 (v0.8) added HTML5 tags (thx to J. Zuckerman) +" 2008 Apr 28 (v0.6) revised customization +" 2008 Mar 09 (v0.5) fixed 'indk' issue (thx to C.J. Robinson) +"}}} + +" Init Folklore, check user settings (2nd time ++) +if exists("b:did_indent") "{{{ + finish +endif + +" Load the Javascript indent script first, it defines GetJavascriptIndent(). +" Undo the rest. +" Load base python indent. +if !exists('*GetJavascriptIndent') + runtime! indent/javascript.vim +endif +let b:did_indent = 1 + +setlocal indentexpr=HtmlIndent() +setlocal indentkeys=o,O,<Return>,<>>,{,},!^F + +" Needed for % to work when finding start/end of a tag. +setlocal matchpairs+=<:> + +let b:undo_indent = "setlocal inde< indk<" + +" b:hi_indent keeps state to speed up indenting consecutive lines. +let b:hi_indent = {"lnum": -1} + +"""""" Code below this is loaded only once. """"" +if exists("*HtmlIndent") && !exists('g:force_reload_html') + call HtmlIndent_CheckUserSettings() + finish +endif + +" Allow for line continuation below. +let s:cpo_save = &cpo +set cpo-=C +"}}} + +" Pattern to match the name of a tag, including custom elements. +let s:tagname = '\w\+\(-\w\+\)*' + +" Check and process settings from b:html_indent and g:html_indent... variables. +" Prefer using buffer-local settings over global settings, so that there can +" be defaults for all HTML files and exceptions for specific types of HTML +" files. +func HtmlIndent_CheckUserSettings() + "{{{ + let inctags = '' + if exists("b:html_indent_inctags") + let inctags = b:html_indent_inctags + elseif exists("g:html_indent_inctags") + let inctags = g:html_indent_inctags + endif + let b:hi_tags = {} + if len(inctags) > 0 + call s:AddITags(b:hi_tags, split(inctags, ",")) + endif + + let autotags = '' + if exists("b:html_indent_autotags") + let autotags = b:html_indent_autotags + elseif exists("g:html_indent_autotags") + let autotags = g:html_indent_autotags + endif + let b:hi_removed_tags = {} + if len(autotags) > 0 + call s:RemoveITags(b:hi_removed_tags, split(autotags, ",")) + endif + + " Syntax names indicating being inside a string of an attribute value. + let string_names = [] + if exists("b:html_indent_string_names") + let string_names = b:html_indent_string_names + elseif exists("g:html_indent_string_names") + let string_names = g:html_indent_string_names + endif + let b:hi_insideStringNames = ['htmlString'] + if len(string_names) > 0 + for s in string_names + call add(b:hi_insideStringNames, s) + endfor + endif + + " Syntax names indicating being inside a tag. + let tag_names = [] + if exists("b:html_indent_tag_names") + let tag_names = b:html_indent_tag_names + elseif exists("g:html_indent_tag_names") + let tag_names = g:html_indent_tag_names + endif + let b:hi_insideTagNames = ['htmlTag', 'htmlScriptTag'] + if len(tag_names) > 0 + for s in tag_names + call add(b:hi_insideTagNames, s) + endfor + endif + + let indone = {"zero": 0 + \,"auto": "indent(prevnonblank(v:lnum-1))" + \,"inc": "b:hi_indent.blocktagind + shiftwidth()"} + + let script1 = '' + if exists("b:html_indent_script1") + let script1 = b:html_indent_script1 + elseif exists("g:html_indent_script1") + let script1 = g:html_indent_script1 + endif + if len(script1) > 0 + let b:hi_js1indent = get(indone, script1, indone.zero) + else + let b:hi_js1indent = 0 + endif + + let style1 = '' + if exists("b:html_indent_style1") + let style1 = b:html_indent_style1 + elseif exists("g:html_indent_style1") + let style1 = g:html_indent_style1 + endif + if len(style1) > 0 + let b:hi_css1indent = get(indone, style1, indone.zero) + else + let b:hi_css1indent = 0 + endif + + if !exists('b:html_indent_line_limit') + if exists('g:html_indent_line_limit') + let b:html_indent_line_limit = g:html_indent_line_limit + else + let b:html_indent_line_limit = 200 + endif + endif + + if exists('b:html_indent_attribute') + let b:hi_attr_indent = b:html_indent_attribute + elseif exists('g:html_indent_attribute') + let b:hi_attr_indent = g:html_indent_attribute + else + let b:hi_attr_indent = 2 + endif + +endfunc "}}} + +" Init Script Vars +"{{{ +let b:hi_lasttick = 0 +let b:hi_newstate = {} +let s:countonly = 0 + "}}} + +" Fill the s:indent_tags dict with known tags. +" The key is "tagname" or "/tagname". {{{ +" The value is: +" 1 opening tag +" 2 "pre" +" 3 "script" +" 4 "style" +" 5 comment start +" 6 conditional comment start +" -1 closing tag +" -2 "/pre" +" -3 "/script" +" -4 "/style" +" -5 comment end +" -6 conditional comment end +let s:indent_tags = {} +let s:endtags = [0,0,0,0,0,0,0] " long enough for the highest index +"}}} + +" Add a list of tag names for a pair of <tag> </tag> to "tags". +func s:AddITags(tags, taglist) + "{{{ + for itag in a:taglist + let a:tags[itag] = 1 + let a:tags['/' . itag] = -1 + endfor +endfunc "}}} + +" Take a list of tag name pairs that are not to be used as tag pairs. +func s:RemoveITags(tags, taglist) + "{{{ + for itag in a:taglist + let a:tags[itag] = 1 + let a:tags['/' . itag] = 1 + endfor +endfunc "}}} + +" Add a block tag, that is a tag with a different kind of indenting. +func s:AddBlockTag(tag, id, ...) + "{{{ + if !(a:id >= 2 && a:id < len(s:endtags)) + echoerr 'AddBlockTag ' . a:id + return + endif + let s:indent_tags[a:tag] = a:id + if a:0 == 0 + let s:indent_tags['/' . a:tag] = -a:id + let s:endtags[a:id] = "</" . a:tag . ">" + else + let s:indent_tags[a:1] = -a:id + let s:endtags[a:id] = a:1 + endif +endfunc "}}} + +" Add known tag pairs. +" Self-closing tags and tags that are sometimes {{{ +" self-closing (e.g., <p>) are not here (when encountering </p> we can find +" the matching <p>, but not the other way around). +" Known self-closing tags: " 'p', 'img', 'source', 'area', 'keygen', 'track', +" 'wbr'. +" Old HTML tags: +call s:AddITags(s:indent_tags, [ + \ 'a', 'abbr', 'acronym', 'address', 'b', 'bdo', 'big', + \ 'blockquote', 'body', 'button', 'caption', 'center', 'cite', 'code', + \ 'colgroup', 'dd', 'del', 'dfn', 'dir', 'div', 'dl', 'dt', 'em', 'fieldset', 'font', + \ 'form', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'html', + \ 'i', 'iframe', 'ins', 'kbd', 'label', 'legend', 'li', + \ 'map', 'menu', 'noframes', 'noscript', 'object', 'ol', + \ 'optgroup', 'q', 's', 'samp', 'select', 'small', 'span', 'strong', 'sub', + \ 'sup', 'table', 'textarea', 'title', 'tt', 'u', 'ul', 'var', 'th', 'td', + \ 'tr', 'tbody', 'tfoot', 'thead']) + +" New HTML5 elements: +call s:AddITags(s:indent_tags, [ + \ 'article', 'aside', 'audio', 'bdi', 'canvas', 'command', 'data', + \ 'datalist', 'details', 'dialog', 'embed', 'figcaption', 'figure', + \ 'footer', 'header', 'hgroup', 'main', 'mark', 'meter', 'nav', 'output', + \ 'picture', 'progress', 'rp', 'rt', 'ruby', 'section', 'summary', + \ 'svg', 'time', 'video']) + +" Tags added for web components: +call s:AddITags(s:indent_tags, [ + \ 'content', 'shadow', 'template']) +"}}} + +" Add Block Tags: these contain alien content +"{{{ +call s:AddBlockTag('pre', 2) +call s:AddBlockTag('script', 3) +call s:AddBlockTag('style', 4) +call s:AddBlockTag('<!--', 5, '-->') +call s:AddBlockTag('<!--[', 6, '![endif]-->') +"}}} + +" Return non-zero when "tagname" is an opening tag, not being a block tag, for +" which there should be a closing tag. Can be used by scripts that include +" HTML indenting. +func HtmlIndent_IsOpenTag(tagname) + "{{{ + if get(s:indent_tags, a:tagname) == 1 + return 1 + endif + return get(b:hi_tags, a:tagname) == 1 +endfunc "}}} + +" Get the value for "tagname", taking care of buffer-local tags. +func s:get_tag(tagname) + "{{{ + let i = get(s:indent_tags, a:tagname) + if (i == 1 || i == -1) && get(b:hi_removed_tags, a:tagname) != 0 + return 0 + endif + if i == 0 + let i = get(b:hi_tags, a:tagname) + endif + return i +endfunc "}}} + +" Count the number of start and end tags in "text". +func s:CountITags(text) + "{{{ + " Store the result in s:curind and s:nextrel. + let s:curind = 0 " relative indent steps for current line [unit &sw]: + let s:nextrel = 0 " relative indent steps for next line [unit &sw]: + let s:block = 0 " assume starting outside of a block + let s:countonly = 1 " don't change state + call substitute(a:text, '<\zs/\=' . s:tagname . '\>\|<!--\[\|\[endif\]-->\|<!--\|-->', '\=s:CheckTag(submatch(0))', 'g') + let s:countonly = 0 +endfunc "}}} + +" Count the number of start and end tags in text. +func s:CountTagsAndState(text) + "{{{ + " Store the result in s:curind and s:nextrel. Update b:hi_newstate.block. + let s:curind = 0 " relative indent steps for current line [unit &sw]: + let s:nextrel = 0 " relative indent steps for next line [unit &sw]: + + let s:block = b:hi_newstate.block + let tmp = substitute(a:text, '<\zs/\=' . s:tagname . '\>\|<!--\[\|\[endif\]-->\|<!--\|-->', '\=s:CheckTag(submatch(0))', 'g') + if s:block == 3 + let b:hi_newstate.scripttype = s:GetScriptType(matchstr(tmp, '\C.*<SCRIPT\>\zs[^>]*')) + endif + let b:hi_newstate.block = s:block +endfunc "}}} + +" Used by s:CountITags() and s:CountTagsAndState(). +func s:CheckTag(itag) + "{{{ + " Returns an empty string or "SCRIPT". + " a:itag can be "tag" or "/tag" or "<!--" or "-->" + if (s:CheckCustomTag(a:itag)) + return "" + endif + let ind = s:get_tag(a:itag) + if ind == -1 + " closing tag + if s:block != 0 + " ignore itag within a block + return "" + endif + if s:nextrel == 0 + let s:curind -= 1 + else + let s:nextrel -= 1 + endif + elseif ind == 1 + " opening tag + if s:block != 0 + return "" + endif + let s:nextrel += 1 + elseif ind != 0 + " block-tag (opening or closing) + return s:CheckBlockTag(a:itag, ind) + " else ind==0 (other tag found): keep indent + endif + return "" +endfunc "}}} + +" Used by s:CheckTag(). Returns an empty string or "SCRIPT". +func s:CheckBlockTag(blocktag, ind) + "{{{ + if a:ind > 0 + " a block starts here + if s:block != 0 + " already in a block (nesting) - ignore + " especially ignore comments after other blocktags + return "" + endif + let s:block = a:ind " block type + if s:countonly + return "" + endif + let b:hi_newstate.blocklnr = v:lnum + " save allover indent for the endtag + let b:hi_newstate.blocktagind = b:hi_indent.baseindent + (s:nextrel + s:curind) * shiftwidth() + if a:ind == 3 + return "SCRIPT" " all except this must be lowercase + " line is to be checked again for the type attribute + endif + else + let s:block = 0 + " we get here if starting and closing a block-tag on the same line + endif + return "" +endfunc "}}} + +" Used by s:CheckTag(). +func s:CheckCustomTag(ctag) + "{{{ + " Returns 1 if ctag is the tag for a custom element, 0 otherwise. + " a:ctag can be "tag" or "/tag" or "<!--" or "-->" + let pattern = '\%\(\w\+-\)\+\w\+' + if match(a:ctag, pattern) == -1 + return 0 + endif + if matchstr(a:ctag, '\/\ze.\+') == "/" + " closing tag + if s:block != 0 + " ignore ctag within a block + return 1 + endif + if s:nextrel == 0 + let s:curind -= 1 + else + let s:nextrel -= 1 + endif + else + " opening tag + if s:block != 0 + return 1 + endif + let s:nextrel += 1 + endif + return 1 +endfunc "}}} + +" Return the <script> type: either "javascript" or "" +func s:GetScriptType(str) + "{{{ + if a:str == "" || a:str =~ "java" + return "javascript" + else + return "" + endif +endfunc "}}} + +" Look back in the file, starting at a:lnum - 1, to compute a state for the +" start of line a:lnum. Return the new state. +func s:FreshState(lnum) + "{{{ + " A state is to know ALL relevant details about the + " lines 1..a:lnum-1, initial calculating (here!) can be slow, but updating is + " fast (incremental). + " TODO: this should be split up in detecting the block type and computing the + " indent for the block type, so that when we do not know the indent we do + " not need to clear the whole state and re-detect the block type again. + " State: + " lnum last indented line == prevnonblank(a:lnum - 1) + " block = 0 a:lnum located within special tag: 0:none, 2:<pre>, + " 3:<script>, 4:<style>, 5:<!--, 6:<!--[ + " baseindent use this indent for line a:lnum as a start - kind of + " autoindent (if block==0) + " scripttype = '' type attribute of a script tag (if block==3) + " blocktagind indent for current opening (get) and closing (set) + " blocktag (if block!=0) + " blocklnr lnum of starting blocktag (if block!=0) + " inattr line {lnum} starts with attributes of a tag + let state = {} + let state.lnum = prevnonblank(a:lnum - 1) + let state.scripttype = "" + let state.blocktagind = -1 + let state.block = 0 + let state.baseindent = 0 + let state.blocklnr = 0 + let state.inattr = 0 + + if state.lnum == 0 + return state + endif + + " Heuristic: + " remember startline state.lnum + " look back for <pre, </pre, <script, </script, <style, </style tags + " remember stopline + " if opening tag found, + " assume a:lnum within block + " else + " look back in result range (stopline, startline) for comment + " \ delimiters (<!--, -->) + " if comment opener found, + " assume a:lnum within comment + " else + " assume usual html for a:lnum + " if a:lnum-1 has a closing comment + " look back to get indent of comment opener + " FI + + " look back for a blocktag + let stopline2 = v:lnum + 1 + if has_key(b:hi_indent, 'block') && b:hi_indent.block > 5 + let [stopline2, stopcol2] = searchpos('<!--', 'bnW') + endif + let [stopline, stopcol] = searchpos('\c<\zs\/\=\%(pre\>\|script\>\|style\>\)', "bnW") + if stopline > 0 && stopline < stopline2 + " ugly ... why isn't there searchstr() + let tagline = tolower(getline(stopline)) + let blocktag = matchstr(tagline, '\/\=\%(pre\>\|script\>\|style\>\)', stopcol - 1) + if blocktag[0] != "/" + " opening tag found, assume a:lnum within block + let state.block = s:indent_tags[blocktag] + if state.block == 3 + let state.scripttype = s:GetScriptType(matchstr(tagline, '\>[^>]*', stopcol)) + endif + let state.blocklnr = stopline + " check preceding tags in the line: + call s:CountITags(tagline[: stopcol-2]) + let state.blocktagind = indent(stopline) + (s:curind + s:nextrel) * shiftwidth() + return state + elseif stopline == state.lnum + " handle special case: previous line (= state.lnum) contains a + " closing blocktag which is preceded by line-noise; + " blocktag == "/..." + let swendtag = match(tagline, '^\s*</') >= 0 + if !swendtag + let [bline, bcol] = searchpos('<'.blocktag[1:].'\>', "bnW") + call s:CountITags(tolower(getline(bline)[: bcol-2])) + let state.baseindent = indent(bline) + (s:curind + s:nextrel) * shiftwidth() + return state + endif + endif + endif + if stopline > stopline2 + let stopline = stopline2 + let stopcol = stopcol2 + endif + + " else look back for comment + let [comlnum, comcol, found] = searchpos('\(<!--\[\)\|\(<!--\)\|-->', 'bpnW', stopline) + if found == 2 || found == 3 + " comment opener found, assume a:lnum within comment + let state.block = (found == 3 ? 5 : 6) + let state.blocklnr = comlnum + " check preceding tags in the line: + call s:CountITags(tolower(getline(comlnum)[: comcol-2])) + if found == 2 + let state.baseindent = b:hi_indent.baseindent + endif + let state.blocktagind = indent(comlnum) + (s:curind + s:nextrel) * shiftwidth() + return state + endif + + " else within usual HTML + let text = tolower(getline(state.lnum)) + + " Check a:lnum-1 for closing comment (we need indent from the opening line). + " Not when other tags follow (might be --> inside a string). + let comcol = stridx(text, '-->') + if comcol >= 0 && match(text, '[<>]', comcol) <= 0 + call cursor(state.lnum, comcol + 1) + let [comlnum, comcol] = searchpos('<!--', 'bW') + if comlnum == state.lnum + let text = text[: comcol-2] + else + let text = tolower(getline(comlnum)[: comcol-2]) + endif + call s:CountITags(text) + let state.baseindent = indent(comlnum) + (s:curind + s:nextrel) * shiftwidth() + " TODO check tags that follow "-->" + return state + endif + + " Check if the previous line starts with end tag. + let swendtag = match(text, '^\s*</') >= 0 + + " If previous line ended in a closing tag, line up with the opening tag. + if !swendtag && text =~ '</' . s:tagname . '\s*>\s*$' + call cursor(state.lnum, 99999) + normal! F< + let start_lnum = HtmlIndent_FindStartTag() + if start_lnum > 0 + let state.baseindent = indent(start_lnum) + if col('.') > 2 + " check for tags before the matching opening tag. + let text = getline(start_lnum) + let swendtag = match(text, '^\s*</') >= 0 + call s:CountITags(text[: col('.') - 2]) + let state.baseindent += s:nextrel * shiftwidth() + if !swendtag + let state.baseindent += s:curind * shiftwidth() + endif + endif + return state + endif + endif + + " Else: no comments. Skip backwards to find the tag we're inside. + let [state.lnum, found] = HtmlIndent_FindTagStart(state.lnum) + " Check if that line starts with end tag. + let text = getline(state.lnum) + let swendtag = match(text, '^\s*</') >= 0 + call s:CountITags(tolower(text)) + let state.baseindent = indent(state.lnum) + s:nextrel * shiftwidth() + if !swendtag + let state.baseindent += s:curind * shiftwidth() + endif + return state +endfunc "}}} + +" Indent inside a <pre> block: Keep indent as-is. +func s:Alien2() + "{{{ + return -1 +endfunc "}}} + +" Return the indent inside a <script> block for javascript. +func s:Alien3() + "{{{ + let lnum = prevnonblank(v:lnum - 1) + while lnum > 1 && getline(lnum) =~ '^\s*/[/*]' + " Skip over comments to avoid that cindent() aligns with the <script> tag + let lnum = prevnonblank(lnum - 1) + endwhile + if lnum < b:hi_indent.blocklnr + " indent for <script> itself + return b:hi_indent.blocktagind + endif + if lnum == b:hi_indent.blocklnr + " indent for the first line after <script> + return eval(b:hi_js1indent) + endif + if b:hi_indent.scripttype == "javascript" + " indent for further lines + return GetJavascriptIndent() + else + return -1 + endif +endfunc "}}} + +" Return the indent inside a <style> block. +func s:Alien4() + "{{{ + if prevnonblank(v:lnum-1) == b:hi_indent.blocklnr + " indent for first content line + return eval(b:hi_css1indent) + endif + return s:CSSIndent() +endfunc "}}} + +" Indending inside a <style> block. Returns the indent. +func s:CSSIndent() + "{{{ + " This handles standard CSS and also Closure stylesheets where special lines + " start with @. + " When the line starts with '*' or the previous line starts with "/*" + " and does not end in "*/", use C indenting to format the comment. + " Adopted $VIMRUNTIME/indent/css.vim + let curtext = getline(v:lnum) + if curtext =~ '^\s*[*]' + \ || (v:lnum > 1 && getline(v:lnum - 1) =~ '\s*/\*' + \ && getline(v:lnum - 1) !~ '\*/\s*$') + return cindent(v:lnum) + endif + + let min_lnum = b:hi_indent.blocklnr + let prev_lnum = s:CssPrevNonComment(v:lnum - 1, min_lnum) + let [prev_lnum, found] = HtmlIndent_FindTagStart(prev_lnum) + if prev_lnum <= min_lnum + " Just below the <style> tag, indent for first content line after comments. + return eval(b:hi_css1indent) + endif + + " If the current line starts with "}" align with its match. + if curtext =~ '^\s*}' + call cursor(v:lnum, 1) + try + normal! % + " Found the matching "{", align with it after skipping unfinished lines. + let align_lnum = s:CssFirstUnfinished(line('.'), min_lnum) + return indent(align_lnum) + catch + " can't find it, try something else, but it's most likely going to be + " wrong + endtry + endif + + " add indent after { + let brace_counts = HtmlIndent_CountBraces(prev_lnum) + let extra = brace_counts.c_open * shiftwidth() + + let prev_text = getline(prev_lnum) + let below_end_brace = prev_text =~ '}\s*$' + + " Search back to align with the first line that's unfinished. + let align_lnum = s:CssFirstUnfinished(prev_lnum, min_lnum) + + " Handle continuation lines if aligning with previous line and not after a + " "}". + if extra == 0 && align_lnum == prev_lnum && !below_end_brace + let prev_hasfield = prev_text =~ '^\s*[a-zA-Z0-9-]\+:' + let prev_special = prev_text =~ '^\s*\(/\*\|@\)' + if curtext =~ '^\s*\(/\*\|@\)' + " if the current line is not a comment or starts with @ (used by template + " systems) reduce indent if previous line is a continuation line + if !prev_hasfield && !prev_special + let extra = -shiftwidth() + endif + else + let cur_hasfield = curtext =~ '^\s*[a-zA-Z0-9-]\+:' + let prev_unfinished = s:CssUnfinished(prev_text) + if prev_unfinished + " Continuation line has extra indent if the previous line was not a + " continuation line. + let extra = shiftwidth() + " Align with @if + if prev_text =~ '^\s*@if ' + let extra = 4 + endif + elseif cur_hasfield && !prev_hasfield && !prev_special + " less indent below a continuation line + let extra = -shiftwidth() + endif + endif + endif + + if below_end_brace + " find matching {, if that line starts with @ it's not the start of a rule + " but something else from a template system + call cursor(prev_lnum, 1) + call search('}\s*$') + try + normal! % + " Found the matching "{", align with it. + let align_lnum = s:CssFirstUnfinished(line('.'), min_lnum) + let special = getline(align_lnum) =~ '^\s*@' + catch + let special = 0 + endtry + if special + " do not reduce indent below @{ ... } + if extra < 0 + let extra += shiftwidth() + endif + else + let extra -= (brace_counts.c_close - (prev_text =~ '^\s*}')) * shiftwidth() + endif + endif + + " if no extra indent yet... + if extra == 0 + if brace_counts.p_open > brace_counts.p_close + " previous line has more ( than ): add a shiftwidth + let extra = shiftwidth() + elseif brace_counts.p_open < brace_counts.p_close + " previous line has more ) than (: subtract a shiftwidth + let extra = -shiftwidth() + endif + endif + + return indent(align_lnum) + extra +endfunc "}}} + +" Inside <style>: Whether a line is unfinished. +" tag: +" tag: blah +" tag: blah && +" tag: blah || +func s:CssUnfinished(text) + "{{{ + return a:text =~ '\(||\|&&\|:\|\k\)\s*$' +endfunc "}}} + +" Search back for the first unfinished line above "lnum". +func s:CssFirstUnfinished(lnum, min_lnum) + "{{{ + let align_lnum = a:lnum + while align_lnum > a:min_lnum && s:CssUnfinished(getline(align_lnum - 1)) + let align_lnum -= 1 + endwhile + return align_lnum +endfunc "}}} + +" Find the non-empty line at or before "lnum" that is not a comment. +func s:CssPrevNonComment(lnum, stopline) + "{{{ + " caller starts from a line a:lnum + 1 that is not a comment + let lnum = prevnonblank(a:lnum) + while 1 + let ccol = match(getline(lnum), '\*/') + if ccol < 0 + " No comment end thus it's something else. + return lnum + endif + call cursor(lnum, ccol + 1) + " Search back for the /* that starts the comment + let lnum = search('/\*', 'bW', a:stopline) + if indent(".") == virtcol(".") - 1 + " The found /* is at the start of the line. Now go back to the line + " above it and again check if it is a comment. + let lnum = prevnonblank(lnum - 1) + else + " /* is after something else, thus it's not a comment line. + return lnum + endif + endwhile +endfunc "}}} + +" Check the number of {} and () in line "lnum". Return a dict with the counts. +func HtmlIndent_CountBraces(lnum) + "{{{ + let brs = substitute(getline(a:lnum), '[''"].\{-}[''"]\|/\*.\{-}\*/\|/\*.*$\|[^{}()]', '', 'g') + let c_open = 0 + let c_close = 0 + let p_open = 0 + let p_close = 0 + for brace in split(brs, '\zs') + if brace == "{" + let c_open += 1 + elseif brace == "}" + if c_open > 0 + let c_open -= 1 + else + let c_close += 1 + endif + elseif brace == '(' + let p_open += 1 + elseif brace == ')' + if p_open > 0 + let p_open -= 1 + else + let p_close += 1 + endif + endif + endfor + return {'c_open': c_open, + \ 'c_close': c_close, + \ 'p_open': p_open, + \ 'p_close': p_close} +endfunc "}}} + +" Return the indent for a comment: <!-- --> +func s:Alien5() + "{{{ + let curtext = getline(v:lnum) + if curtext =~ '^\s*\zs-->' + " current line starts with end of comment, line up with comment start. + call cursor(v:lnum, 0) + let lnum = search('<!--', 'b') + if lnum > 0 + " TODO: what if <!-- is not at the start of the line? + return indent(lnum) + endif + + " Strange, can't find it. + return -1 + endif + + let prevlnum = prevnonblank(v:lnum - 1) + let prevtext = getline(prevlnum) + let idx = match(prevtext, '^\s*\zs<!--') + if idx >= 0 + " just below comment start, add a shiftwidth + return indent(prevlnum) + shiftwidth() + endif + + " Some files add 4 spaces just below a TODO line. It's difficult to detect + " the end of the TODO, so let's not do that. + + " Align with the previous non-blank line. + return indent(prevlnum) +endfunc "}}} + +" Return the indent for conditional comment: <!--[ ![endif]--> +func s:Alien6() + "{{{ + let curtext = getline(v:lnum) + if curtext =~ '\s*\zs<!\[endif\]-->' + " current line starts with end of comment, line up with comment start. + let lnum = search('<!--', 'bn') + if lnum > 0 + return indent(lnum) + endif + endif + return b:hi_indent.baseindent + shiftwidth() +endfunc "}}} + +" When the "lnum" line ends in ">" find the line containing the matching "<". +func HtmlIndent_FindTagStart(lnum) + "{{{ + " Avoids using the indent of a continuation line. + " Moves the cursor. + " Return two values: + " - the matching line number or "lnum". + " - a flag indicating whether we found the end of a tag. + " This method is global so that HTML-like indenters can use it. + " To avoid matching " > " or " < " inside a string require that the opening + " "<" is followed by a word character and the closing ">" comes after a + " non-white character. + let idx = match(getline(a:lnum), '\S>\s*$') + if idx > 0 + call cursor(a:lnum, idx) + let lnum = searchpair('<\w', '' , '\S>', 'bW', '', max([a:lnum - b:html_indent_line_limit, 0])) + if lnum > 0 + return [lnum, 1] + endif + endif + return [a:lnum, 0] +endfunc "}}} + +" Find the unclosed start tag from the current cursor position. +func HtmlIndent_FindStartTag() + "{{{ + " The cursor must be on or before a closing tag. + " If found, positions the cursor at the match and returns the line number. + " Otherwise returns 0. + let tagname = matchstr(getline('.')[col('.') - 1:], '</\zs' . s:tagname . '\ze') + let start_lnum = searchpair('<' . tagname . '\>', '', '</' . tagname . '\>', 'bW') + if start_lnum > 0 + return start_lnum + endif + return 0 +endfunc "}}} + +" Moves the cursor from a "<" to the matching ">". +func HtmlIndent_FindTagEnd() + "{{{ + " Call this with the cursor on the "<" of a start tag. + " This will move the cursor to the ">" of the matching end tag or, when it's + " a self-closing tag, to the matching ">". + " Limited to look up to b:html_indent_line_limit lines away. + let text = getline('.') + let tagname = matchstr(text, s:tagname . '\|!--', col('.')) + if tagname == '!--' + call search('--\zs>') + elseif s:get_tag('/' . tagname) != 0 + " tag with a closing tag, find matching "</tag>" + call searchpair('<' . tagname, '', '</' . tagname . '\zs>', 'W', '', line('.') + b:html_indent_line_limit) + else + " self-closing tag, find the ">" + call search('\S\zs>') + endif +endfunc "}}} + +" Indenting inside a start tag. Return the correct indent or -1 if unknown. +func s:InsideTag(foundHtmlString) + "{{{ + if a:foundHtmlString + " Inside an attribute string. + " Align with the opening quote or use an external function. + let lnum = v:lnum - 1 + if lnum > 1 + if exists('b:html_indent_tag_string_func') + return b:html_indent_tag_string_func(lnum) + endif + " If there is a double quote in the previous line, indent with the + " character after it. + if getline(lnum) =~ '"' + call cursor(lnum, 0) + normal f" + return virtcol('.') + endif + return indent(lnum) + endif + endif + + " Should be another attribute: " attr="val". Align with the previous + " attribute start. + let lnum = v:lnum + while lnum > 1 + let lnum -= 1 + let text = getline(lnum) + " Find a match with one of these, align with "attr": + " attr= + " <tag attr= + " text<tag attr= + " <tag>text</tag>text<tag attr= + " For long lines search for the first match, finding the last match + " gets very slow. + if len(text) < 300 + let idx = match(text, '.*\s\zs[_a-zA-Z0-9-]\+="') + else + let idx = match(text, '\s\zs[_a-zA-Z0-9-]\+="') + endif + if idx == -1 + " try <tag attr + let idx = match(text, '<' . s:tagname . '\s\+\zs\w') + endif + if idx == -1 + " after just "<tag" indent two levels more by default + let idx = match(text, '<' . s:tagname . '$') + if idx >= 0 + call cursor(lnum, idx + 1) + return virtcol('.') - 1 + shiftwidth() * b:hi_attr_indent + endif + endif + if idx > 0 + " Found the attribute to align with. + call cursor(lnum, idx) + return virtcol('.') + endif + endwhile + return -1 +endfunc "}}} + +" THE MAIN INDENT FUNCTION. Return the amount of indent for v:lnum. +func HtmlIndent() + "{{{ + if prevnonblank(v:lnum - 1) < 1 + " First non-blank line has no indent. + return 0 + endif + + let curtext = tolower(getline(v:lnum)) + let indentunit = shiftwidth() + + let b:hi_newstate = {} + let b:hi_newstate.lnum = v:lnum + + " When syntax HL is enabled, detect we are inside a tag. Indenting inside + " a tag works very differently. Do not do this when the line starts with + " "<", it gets the "htmlTag" ID but we are not inside a tag then. + if curtext !~ '^\s*<' + normal! ^ + let stack = synstack(v:lnum, col('.')) " assumes there are no tabs + let foundHtmlString = 0 + for synid in reverse(stack) + let name = synIDattr(synid, "name") + if index(b:hi_insideStringNames, name) >= 0 + let foundHtmlString = 1 + elseif index(b:hi_insideTagNames, name) >= 0 + " Yes, we are inside a tag. + let indent = s:InsideTag(foundHtmlString) + if indent >= 0 + " Do not keep the state. TODO: could keep the block type. + let b:hi_indent.lnum = 0 + return indent + endif + endif + endfor + endif + + " does the line start with a closing tag? + let swendtag = match(curtext, '^\s*</') >= 0 + + if prevnonblank(v:lnum - 1) == b:hi_indent.lnum && b:hi_lasttick == b:changedtick - 1 + " use state (continue from previous line) + else + " start over (know nothing) + let b:hi_indent = s:FreshState(v:lnum) + endif + + if b:hi_indent.block >= 2 + " within block + let endtag = s:endtags[b:hi_indent.block] + let blockend = stridx(curtext, endtag) + if blockend >= 0 + " block ends here + let b:hi_newstate.block = 0 + " calc indent for REST OF LINE (may start more blocks): + call s:CountTagsAndState(strpart(curtext, blockend + strlen(endtag))) + if swendtag && b:hi_indent.block != 5 + let indent = b:hi_indent.blocktagind + s:curind * indentunit + let b:hi_newstate.baseindent = indent + s:nextrel * indentunit + else + let indent = s:Alien{b:hi_indent.block}() + let b:hi_newstate.baseindent = b:hi_indent.blocktagind + s:nextrel * indentunit + endif + else + " block continues + " indent this line with alien method + let indent = s:Alien{b:hi_indent.block}() + endif + else + " not within a block - within usual html + let b:hi_newstate.block = b:hi_indent.block + if swendtag + " The current line starts with an end tag, align with its start tag. + call cursor(v:lnum, 1) + let start_lnum = HtmlIndent_FindStartTag() + if start_lnum > 0 + " check for the line starting with something inside a tag: + " <sometag <- align here + " attr=val><open> not here + let text = getline(start_lnum) + let angle = matchstr(text, '[<>]') + if angle == '>' + call cursor(start_lnum, 1) + normal! f>% + let start_lnum = line('.') + let text = getline(start_lnum) + endif + + let indent = indent(start_lnum) + if col('.') > 2 + let swendtag = match(text, '^\s*</') >= 0 + call s:CountITags(text[: col('.') - 2]) + let indent += s:nextrel * shiftwidth() + if !swendtag + let indent += s:curind * shiftwidth() + endif + endif + else + " not sure what to do + let indent = b:hi_indent.baseindent + endif + let b:hi_newstate.baseindent = indent + else + call s:CountTagsAndState(curtext) + let indent = b:hi_indent.baseindent + let b:hi_newstate.baseindent = indent + (s:curind + s:nextrel) * indentunit + endif + endif + + let b:hi_lasttick = b:changedtick + call extend(b:hi_indent, b:hi_newstate, "force") + return indent +endfunc "}}} + +" Check user settings when loading this script the first time. +call HtmlIndent_CheckUserSettings() + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim: fdm=marker ts=8 sw=2 tw=78 diff --git a/runtime/indent/htmldjango.vim b/runtime/indent/htmldjango.vim new file mode 100644 index 0000000..8da9fe3 --- /dev/null +++ b/runtime/indent/htmldjango.vim @@ -0,0 +1,12 @@ +" Vim indent file +" Language: Django HTML template +" Maintainer: Dave Hodder <dmh@dmh.org.uk> +" Last Change: 2007 Jan 25 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +" Use HTML formatting rules. +runtime! indent/html.vim diff --git a/runtime/indent/idlang.vim b/runtime/indent/idlang.vim new file mode 100644 index 0000000..1519865 --- /dev/null +++ b/runtime/indent/idlang.vim @@ -0,0 +1,65 @@ +" IDL (Interactive Data Language) indent file. +" Language: IDL (ft=idlang) +" Maintainer: Aleksandar Jelenak <ajelenak AT yahoo.com> (Invalid email address) +" Doug Kearns <dougkearns@gmail.com> +" Last change: 2022 Apr 06 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentkeys=o,O,0=endif,0=ENDIF,0=endelse,0=ENDELSE,0=endwhile,0=ENDWHILE,0=endfor,0=ENDFOR,0=endrep,0=ENDREP + +setlocal indentexpr=GetIdlangIndent(v:lnum) + +let b:undo_indent = "setl inde< indk<" + +" Only define the function once. +if exists("*GetIdlangIndent") + finish +endif + +function GetIdlangIndent(lnum) + " First non-empty line above the current line. + let pnum = prevnonblank(v:lnum-1) + " v:lnum is the first non-empty line -- zero indent. + if pnum == 0 + return 0 + endif + " Second non-empty line above the current line. + let pnum2 = prevnonblank(pnum-1) + + " Current indent. + let curind = indent(pnum) + + " Indenting of continued lines. + if getline(pnum) =~ '\$\s*\(;.*\)\=$' + if getline(pnum2) !~ '\$\s*\(;.*\)\=$' + let curind = curind+shiftwidth() + endif + else + if getline(pnum2) =~ '\$\s*\(;.*\)\=$' + let curind = curind-shiftwidth() + endif + endif + + " Indenting blocks of statements. + if getline(v:lnum) =~? '^\s*\(endif\|endelse\|endwhile\|endfor\|endrep\)\>' + if getline(pnum) =~? 'begin\>' + elseif indent(v:lnum) > curind-shiftwidth() + let curind = curind-shiftwidth() + else + return -1 + endif + elseif getline(pnum) =~? 'begin\>' + if indent(v:lnum) < curind+shiftwidth() + let curind = curind+shiftwidth() + else + return -1 + endif + endif + return curind +endfunction + diff --git a/runtime/indent/ishd.vim b/runtime/indent/ishd.vim new file mode 100644 index 0000000..531244b --- /dev/null +++ b/runtime/indent/ishd.vim @@ -0,0 +1,68 @@ +" Description: InstallShield indenter +" Author: Johannes Zellner <johannes@zellner.org> +" Last Change: Tue, 27 Apr 2004 14:54:59 CEST + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal autoindent +setlocal indentexpr=GetIshdIndent(v:lnum) +setlocal indentkeys& +setlocal indentkeys+==else,=elseif,=endif,=end,=begin,<:> +" setlocal indentkeys-=0# + +let b:undo_indent = "setl ai< indentexpr< indentkeys<" + +" Only define the function once. +if exists("*GetIshdIndent") + finish +endif + +fun! GetIshdIndent(lnum) + " labels and preprocessor get zero indent immediately + let this_line = getline(a:lnum) + let LABELS_OR_PREPROC = '^\s*\(\<\k\+\>:\s*$\|#.*\)' + let LABELS_OR_PREPROC_EXCEPT = '^\s*\<default\+\>:' + if this_line =~ LABELS_OR_PREPROC && this_line !~ LABELS_OR_PREPROC_EXCEPT + return 0 + endif + + " Find a non-blank line above the current line. + " Skip over labels and preprocessor directives. + let lnum = a:lnum + while lnum > 0 + let lnum = prevnonblank(lnum - 1) + let previous_line = getline(lnum) + if previous_line !~ LABELS_OR_PREPROC || previous_line =~ LABELS_OR_PREPROC_EXCEPT + break + endif + endwhile + + " Hit the start of the file, use zero indent. + if lnum == 0 + return 0 + endif + + let ind = indent(lnum) + + " Add + if previous_line =~ '^\s*\<\(function\|begin\|switch\|case\|default\|if.\{-}then\|else\|elseif\|while\|repeat\)\>' + let ind = ind + shiftwidth() + endif + + " Subtract + if this_line =~ '^\s*\<endswitch\>' + let ind = ind - 2 * shiftwidth() + elseif this_line =~ '^\s*\<\(begin\|end\|endif\|endwhile\|else\|elseif\|until\)\>' + let ind = ind - shiftwidth() + elseif this_line =~ '^\s*\<\(case\|default\)\>' + if previous_line !~ '^\s*\<switch\>' + let ind = ind - shiftwidth() + endif + endif + + return ind +endfun diff --git a/runtime/indent/j.vim b/runtime/indent/j.vim new file mode 100644 index 0000000..c308512 --- /dev/null +++ b/runtime/indent/j.vim @@ -0,0 +1,50 @@ +" Vim indent file +" Language: J +" Maintainer: David Bürgin <dbuergin@gluet.ch> +" URL: https://gitlab.com/glts/vim-j +" Last Change: 2015-01-11 + +if exists('b:did_indent') + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetJIndent() +setlocal indentkeys-=0{,0},:,0# +setlocal indentkeys+=0),0<:>,=case.,=catch.,=catchd.,=catcht.,=do.,=else.,=elseif.,=end.,=fcase. + +let b:undo_indent = 'setlocal indentkeys< indentexpr<' + +if exists('*GetJIndent') + finish +endif + +" If g:j_indent_definitions is true, the bodies of explicit definitions of +" adverbs, conjunctions, and verbs will be indented. Default is false (0). +if !exists('g:j_indent_definitions') + let g:j_indent_definitions = 0 +endif + +function GetJIndent() abort + let l:prevlnum = prevnonblank(v:lnum - 1) + if l:prevlnum == 0 + return 0 + endif + let l:indent = indent(l:prevlnum) + let l:prevline = getline(l:prevlnum) + if l:prevline =~# '^\s*\%(case\|catch[dt]\=\|do\|else\%(if\)\=\|fcase\|for\%(_\a\k*\)\=\|if\|select\|try\|whil\%(e\|st\)\)\.\%(\%(\<end\.\)\@!.\)*$' + " Increase indentation after an initial control word that starts or + " continues a block and is not terminated by "end." + let l:indent += shiftwidth() + elseif g:j_indent_definitions && (l:prevline =~# '\<\%([1-4]\|13\|adverb\|conjunction\|verb\|monad\|dyad\)\s\+\%(:\s*0\|def\s\+0\|define\)\>' || l:prevline =~# '^\s*:\s*$') + " Increase indentation in explicit definitions of adverbs, conjunctions, + " and verbs + let l:indent += shiftwidth() + endif + " Decrease indentation in lines that start with either control words that + " continue or end a block, or the special items ")" and ":" + if getline(v:lnum) =~# '^\s*\%()\|:\|\%(case\|catch[dt]\=\|do\|else\%(if\)\=\|end\|fcase\)\.\)' + let l:indent -= shiftwidth() + endif + return l:indent +endfunction diff --git a/runtime/indent/java.vim b/runtime/indent/java.vim new file mode 100644 index 0000000..49f8010 --- /dev/null +++ b/runtime/indent/java.vim @@ -0,0 +1,150 @@ +" Vim indent file +" Language: Java +" Previous Maintainer: Toby Allsopp <toby.allsopp@peace.com> +" Current Maintainer: Hong Xu <hong@topbug.net> +" Homepage: http://www.vim.org/scripts/script.php?script_id=3899 +" https://github.com/xuhdev/indent-java.vim +" Last Change: 2016 Mar 7 +" Version: 1.1 +" License: Same as Vim. +" Copyright (c) 2012-2016 Hong Xu +" Before 2012, this file was maintained by Toby Allsopp. + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" Indent Java anonymous classes correctly. +setlocal cindent cinoptions& cinoptions+=j1 + +" The "extends" and "implements" lines start off with the wrong indent. +setlocal indentkeys& indentkeys+=0=extends indentkeys+=0=implements + +" Set the function to do the work. +setlocal indentexpr=GetJavaIndent() + +let b:undo_indent = "set cin< cino< indentkeys< indentexpr<" + +" Only define the function once. +if exists("*GetJavaIndent") + finish +endif + +let s:keepcpo= &cpo +set cpo&vim + +function! SkipJavaBlanksAndComments(startline) + let lnum = a:startline + while lnum > 1 + let lnum = prevnonblank(lnum) + if getline(lnum) =~ '\*/\s*$' + while getline(lnum) !~ '/\*' && lnum > 1 + let lnum = lnum - 1 + endwhile + if getline(lnum) =~ '^\s*/\*' + let lnum = lnum - 1 + else + break + endif + elseif getline(lnum) =~ '^\s*//' + let lnum = lnum - 1 + else + break + endif + endwhile + return lnum +endfunction + +function GetJavaIndent() + + " Java is just like C; use the built-in C indenting and then correct a few + " specific cases. + let theIndent = cindent(v:lnum) + + " If we're in the middle of a comment then just trust cindent + if getline(v:lnum) =~ '^\s*\*' + return theIndent + endif + + " find start of previous line, in case it was a continuation line + let lnum = SkipJavaBlanksAndComments(v:lnum - 1) + + " If the previous line starts with '@', we should have the same indent as + " the previous one + if getline(lnum) =~ '^\s*@.*$' + return indent(lnum) + endif + + let prev = lnum + while prev > 1 + let next_prev = SkipJavaBlanksAndComments(prev - 1) + if getline(next_prev) !~ ',\s*$' + break + endif + let prev = next_prev + endwhile + + " Try to align "throws" lines for methods and "extends" and "implements" for + " classes. + if getline(v:lnum) =~ '^\s*\(throws\|extends\|implements\)\>' + \ && getline(lnum) !~ '^\s*\(throws\|extends\|implements\)\>' + let theIndent = theIndent + shiftwidth() + endif + + " correct for continuation lines of "throws", "implements" and "extends" + let cont_kw = matchstr(getline(prev), + \ '^\s*\zs\(throws\|implements\|extends\)\>\ze.*,\s*$') + if strlen(cont_kw) > 0 + let amount = strlen(cont_kw) + 1 + if getline(lnum) !~ ',\s*$' + let theIndent = theIndent - (amount + shiftwidth()) + if theIndent < 0 + let theIndent = 0 + endif + elseif prev == lnum + let theIndent = theIndent + amount + if cont_kw ==# 'throws' + let theIndent = theIndent + shiftwidth() + endif + endif + elseif getline(prev) =~ '^\s*\(throws\|implements\|extends\)\>' + \ && (getline(prev) =~ '{\s*$' + \ || getline(v:lnum) =~ '^\s*{\s*$') + let theIndent = theIndent - shiftwidth() + endif + + " When the line starts with a }, try aligning it with the matching {, + " skipping over "throws", "extends" and "implements" clauses. + if getline(v:lnum) =~ '^\s*}\s*\(//.*\|/\*.*\)\=$' + call cursor(v:lnum, 1) + silent normal! % + let lnum = line('.') + if lnum < v:lnum + while lnum > 1 + let next_lnum = SkipJavaBlanksAndComments(lnum - 1) + if getline(lnum) !~ '^\s*\(throws\|extends\|implements\)\>' + \ && getline(next_lnum) !~ ',\s*$' + break + endif + let lnum = prevnonblank(next_lnum) + endwhile + return indent(lnum) + endif + endif + + " Below a line starting with "}" never indent more. Needed for a method + " below a method with an indented "throws" clause. + let lnum = SkipJavaBlanksAndComments(v:lnum - 1) + if getline(lnum) =~ '^\s*}\s*\(//.*\|/\*.*\)\=$' && indent(lnum) < theIndent + let theIndent = indent(lnum) + endif + + return theIndent +endfunction + +let &cpo = s:keepcpo +unlet s:keepcpo + +" vi: sw=2 et diff --git a/runtime/indent/javascript.vim b/runtime/indent/javascript.vim new file mode 100644 index 0000000..8077442 --- /dev/null +++ b/runtime/indent/javascript.vim @@ -0,0 +1,486 @@ +" Vim indent file +" Language: Javascript +" Maintainer: Chris Paul ( https://github.com/bounceme ) +" URL: https://github.com/pangloss/vim-javascript +" Last Change: December 4, 2017 + +" Only load this indent file when no other was loaded. +if exists('b:did_indent') + finish +endif +let b:did_indent = 1 + +" Now, set up our indentation expression and keys that trigger it. +setlocal indentexpr=GetJavascriptIndent() +setlocal autoindent nolisp nosmartindent +setlocal indentkeys+=0],0) +" Testable with something like: +" vim -eNs "+filetype plugin indent on" "+syntax on" "+set ft=javascript" \ +" "+norm! gg=G" '+%print' '+:q!' testfile.js \ +" | diff -uBZ testfile.js - + +let b:undo_indent = 'setlocal indentexpr< smartindent< autoindent< indentkeys<' + +" Only define the function once. +if exists('*GetJavascriptIndent') + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +" indent correctly if inside <script> +" vim/vim@690afe1 for the switch from cindent +" overridden with b:html_indent_script1 +call extend(g:,{'html_indent_script1': 'inc'},'keep') + +" Regex of syntax group names that are or delimit string or are comments. +let s:bvars = { + \ 'syng_strcom': 'string\|comment\|regex\|special\|doc\|template\%(braces\)\@!', + \ 'syng_str': 'string\|template\|special' } +" template strings may want to be excluded when editing graphql: +" au! Filetype javascript let b:syng_str = '^\%(.*template\)\@!.*string\|special' +" au! Filetype javascript let b:syng_strcom = '^\%(.*template\)\@!.*string\|comment\|regex\|special\|doc' + +function s:GetVars() + call extend(b:,extend(s:bvars,{'js_cache': [0,0,0]}),'keep') +endfunction + +" Get shiftwidth value +if exists('*shiftwidth') + function s:sw() + return shiftwidth() + endfunction +else + function s:sw() + return &l:shiftwidth ? &l:shiftwidth : &l:tabstop + endfunction +endif + +" Performance for forwards search(): start search at pos rather than masking +" matches before pos. +let s:z = has('patch-7.4.984') ? 'z' : '' + +" Expression used to check whether we should skip a match with searchpair(). +let s:skip_expr = "s:SynAt(line('.'),col('.')) =~? b:syng_strcom" +let s:in_comm = s:skip_expr[:-14] . "'comment\\|doc'" + +let s:rel = has('reltime') +" searchpair() wrapper +if s:rel + function s:GetPair(start,end,flags,skip) + return searchpair('\m'.a:start,'','\m'.a:end,a:flags,a:skip,s:l1,a:skip ==# 's:SkipFunc()' ? 2000 : 200) + endfunction +else + function s:GetPair(start,end,flags,skip) + return searchpair('\m'.a:start,'','\m'.a:end,a:flags,a:skip,s:l1) + endfunction +endif + +function s:SynAt(l,c) + let byte = line2byte(a:l) + a:c - 1 + let pos = index(s:synid_cache[0], byte) + if pos == -1 + let s:synid_cache[:] += [[byte], [synIDattr(synID(a:l, a:c, 0), 'name')]] + endif + return s:synid_cache[1][pos] +endfunction + +function s:ParseCino(f) + let [divider, n, cstr] = [0] + matchlist(&cino, + \ '\%(.*,\)\=\%(\%d'.char2nr(a:f).'\(-\)\=\([.s0-9]*\)\)\=')[1:2] + for c in split(cstr,'\zs') + if c == '.' && !divider + let divider = 1 + elseif c ==# 's' + if n !~ '\d' + return n . s:sw() + 0 + endif + let n = str2nr(n) * s:sw() + break + else + let [n, divider] .= [c, 0] + endif + endfor + return str2nr(n) / max([str2nr(divider),1]) +endfunction + +" Optimized {skip} expr, only callable from the search loop which +" GetJavascriptIndent does to find the containing [[{(] (side-effects) +function s:SkipFunc() + if s:top_col == 1 + throw 'out of bounds' + elseif s:check_in + if eval(s:skip_expr) + return 1 + endif + let s:check_in = 0 + elseif getline('.') =~ '\%<'.col('.').'c\/.\{-}\/\|\%>'.col('.').'c[''"]\|\\$' + if eval(s:skip_expr) + return 1 + endif + elseif search('\m`\|\${\|\*\/','nW'.s:z,s:looksyn) + if eval(s:skip_expr) + let s:check_in = 1 + return 1 + endif + else + let s:synid_cache[:] += [[line2byte('.') + col('.') - 1], ['']] + endif + let [s:looksyn, s:top_col] = getpos('.')[1:2] +endfunction + +function s:AlternatePair() + let [pat, l:for] = ['[][(){};]', 2] + while s:SearchLoop(pat,'bW','s:SkipFunc()') + if s:LookingAt() == ';' + if !l:for + if s:GetPair('{','}','bW','s:SkipFunc()') + return + endif + break + else + let [pat, l:for] = ['[{}();]', l:for - 1] + endif + else + let idx = stridx('])}',s:LookingAt()) + if idx == -1 + return + elseif !s:GetPair(['\[','(','{'][idx],'])}'[idx],'bW','s:SkipFunc()') + break + endif + endif + endwhile + throw 'out of bounds' +endfunction + +function s:Nat(int) + return a:int * (a:int > 0) +endfunction + +function s:LookingAt() + return getline('.')[col('.')-1] +endfunction + +function s:Token() + return s:LookingAt() =~ '\k' ? expand('<cword>') : s:LookingAt() +endfunction + +function s:PreviousToken(...) + let [l:pos, tok] = [getpos('.'), ''] + if search('\m\k\{1,}\|\S','ebW') + if getline('.')[col('.')-2:col('.')-1] == '*/' + if eval(s:in_comm) && !s:SearchLoop('\S\ze\_s*\/[/*]','bW',s:in_comm) + call setpos('.',l:pos) + else + let tok = s:Token() + endif + else + let two = a:0 || line('.') != l:pos[1] ? strridx(getline('.')[:col('.')],'//') + 1 : 0 + if two && eval(s:in_comm) + call cursor(0,two) + let tok = s:PreviousToken(1) + if tok is '' + call setpos('.',l:pos) + endif + else + let tok = s:Token() + endif + endif + endif + return tok +endfunction + +function s:Pure(f,...) + return eval("[call(a:f,a:000),cursor(a:firstline,".col('.').")][0]") +endfunction + +function s:SearchLoop(pat,flags,expr) + return s:GetPair(a:pat,'\_$.',a:flags,a:expr) +endfunction + +function s:ExprCol() + if getline('.')[col('.')-2] == ':' + return 1 + endif + let bal = 0 + while s:SearchLoop('[{}?:]','bW',s:skip_expr) + if s:LookingAt() == ':' + if getline('.')[col('.')-2] == ':' + call cursor(0,col('.')-1) + continue + endif + let bal -= 1 + elseif s:LookingAt() == '?' + if getline('.')[col('.'):col('.')+1] =~ '^\.\d\@!' + continue + elseif !bal + return 1 + endif + let bal += 1 + elseif s:LookingAt() == '{' + return !s:IsBlock() + elseif !s:GetPair('{','}','bW',s:skip_expr) + break + endif + endwhile +endfunction + +" configurable regexes that define continuation lines, not including (, {, or [. +let s:opfirst = '^' . get(g:,'javascript_opfirst', + \ '\C\%([<>=,.?^%|/&]\|\([-:+]\)\1\@!\|\*\+\|!=\|in\%(stanceof\)\=\>\)') +let s:continuation = get(g:,'javascript_continuation', + \ '\C\%([<=,.~!?/*^%|&:]\|+\@<!+\|-\@<!-\|=\@<!>\|\<\%(typeof\|new\|delete\|void\|in\|instanceof\|await\)\)') . '$' + +function s:Continues() + let tok = matchstr(strpart(getline('.'),col('.')-15,15),s:continuation) + if tok =~ '[a-z:]' + return tok == ':' ? s:ExprCol() : s:PreviousToken() != '.' + elseif tok !~ '[/>]' + return tok isnot '' + endif + return s:SynAt(line('.'),col('.')) !~? (tok == '>' ? 'jsflow\|^html' : 'regex') +endfunction + +" Check if line 'lnum' has a balanced amount of parentheses. +function s:Balanced(lnum,line) + let l:open = 0 + let pos = match(a:line, '[][(){}]') + while pos != -1 + if s:SynAt(a:lnum,pos + 1) !~? b:syng_strcom + let l:open += match(' ' . a:line[pos],'[[({]') + if l:open < 0 + return + endif + endif + let pos = match(a:line, !l:open ? '[][(){}]' : '()' =~ a:line[pos] ? + \ '[()]' : '{}' =~ a:line[pos] ? '[{}]' : '[][]', pos + 1) + endwhile + return !l:open +endfunction + +function s:OneScope() + if s:LookingAt() == ')' && s:GetPair('(', ')', 'bW', s:skip_expr) + let tok = s:PreviousToken() + return (count(split('for if let while with'),tok) || + \ tok =~# '^await$\|^each$' && s:PreviousToken() ==# 'for') && + \ s:Pure('s:PreviousToken') != '.' && !(tok == 'while' && s:DoWhile()) + elseif s:Token() =~# '^else$\|^do$' + return s:Pure('s:PreviousToken') != '.' + elseif strpart(getline('.'),col('.')-2,2) == '=>' + call cursor(0,col('.')-1) + if s:PreviousToken() == ')' + return s:GetPair('(', ')', 'bW', s:skip_expr) + endif + return 1 + endif +endfunction + +function s:DoWhile() + let cpos = searchpos('\m\<','cbW') + while s:SearchLoop('\C[{}]\|\<\%(do\|while\)\>','bW',s:skip_expr) + if s:LookingAt() =~ '\a' + if s:Pure('s:IsBlock') + if s:LookingAt() ==# 'd' + return 1 + endif + break + endif + elseif s:LookingAt() != '}' || !s:GetPair('{','}','bW',s:skip_expr) + break + endif + endwhile + call call('cursor',cpos) +endfunction + +" returns total offset from braceless contexts. 'num' is the lineNr which +" encloses the entire context, 'cont' if whether a:firstline is a continued +" expression, which could have started in a braceless context +function s:IsContOne(cont) + let [l:num, b_l] = [b:js_cache[1] + !b:js_cache[1], 0] + let pind = b:js_cache[1] ? indent(b:js_cache[1]) + s:sw() : 0 + let ind = indent('.') + !a:cont + while line('.') > l:num && ind > pind || line('.') == l:num + if indent('.') < ind && s:OneScope() + let b_l += 1 + elseif !a:cont || b_l || ind < indent(a:firstline) + break + else + call cursor(0,1) + endif + let ind = min([ind, indent('.')]) + if s:PreviousToken() is '' + break + endif + endwhile + return b_l +endfunction + +function s:IsSwitch() + call call('cursor',b:js_cache[1:]) + return search('\m\C\%#.\_s*\%(\%(\/\/.*\_$\|\/\*\_.\{-}\*\/\)\@>\_s*\)*\%(case\|default\)\>','nWc'.s:z) +endfunction + +" https://github.com/sweet-js/sweet.js/wiki/design#give-lookbehind-to-the-reader +function s:IsBlock() + let tok = s:PreviousToken() + if join(s:stack) =~? 'xml\|jsx' && s:SynAt(line('.'),col('.')-1) =~? 'xml\|jsx' + let s:in_jsx = 1 + return tok != '{' + elseif tok =~ '\k' + if tok ==# 'type' + return s:Pure('eval',"s:PreviousToken() !~# '^\\%(im\\|ex\\)port$' || s:PreviousToken() == '.'") + elseif tok ==# 'of' + return s:Pure('eval',"!s:GetPair('[[({]','[])}]','bW',s:skip_expr) || s:LookingAt() != '(' ||" + \ ."s:{s:PreviousToken() ==# 'await' ? 'Previous' : ''}Token() !=# 'for' || s:PreviousToken() == '.'") + endif + return index(split('return const let import export extends yield default delete var await void typeof throw case new in instanceof') + \ ,tok) < (line('.') != a:firstline) || s:Pure('s:PreviousToken') == '.' + elseif tok == '>' + return getline('.')[col('.')-2] == '=' || s:SynAt(line('.'),col('.')) =~? 'jsflow\|^html' + elseif tok == '*' + return s:Pure('s:PreviousToken') == ':' + elseif tok == ':' + return s:Pure('eval',"s:PreviousToken() =~ '^\\K\\k*$' && !s:ExprCol()") + elseif tok == '/' + return s:SynAt(line('.'),col('.')) =~? 'regex' + elseif tok !~ '[=~!<,.?^%|&([]' + return tok !~ '[-+]' || line('.') != a:firstline && getline('.')[col('.')-2] == tok + endif +endfunction + +function GetJavascriptIndent() + call s:GetVars() + let s:synid_cache = [[],[]] + let l:line = getline(v:lnum) + " use synstack as it validates syn state and works in an empty line + let s:stack = [''] + map(synstack(v:lnum,1),"synIDattr(v:val,'name')") + + " start with strings,comments,etc. + if s:stack[-1] =~? 'comment\|doc' + if l:line =~ '^\s*\*' + return cindent(v:lnum) + elseif l:line !~ '^\s*\/[/*]' + return -1 + endif + elseif s:stack[-1] =~? b:syng_str + if b:js_cache[0] == v:lnum - 1 && s:Balanced(v:lnum-1,getline(v:lnum-1)) + let b:js_cache[0] = v:lnum + endif + return -1 + endif + + let s:l1 = max([0,prevnonblank(v:lnum) - (s:rel ? 2000 : 1000), + \ get(get(b:,'hi_indent',{}),'blocklnr')]) + call cursor(v:lnum,1) + if s:PreviousToken() is '' + return + endif + let [l:lnum, pline] = [line('.'), getline('.')[:col('.')-1]] + + let l:line = substitute(l:line,'^\s*','','') + let l:line_raw = l:line + if l:line[:1] == '/*' + let l:line = substitute(l:line,'^\%(\/\*.\{-}\*\/\s*\)*','','') + endif + if l:line =~ '^\/[/*]' + let l:line = '' + endif + + " the containing paren, bracket, or curly. Many hacks for performance + call cursor(v:lnum,1) + let idx = index([']',')','}'],l:line[0]) + if b:js_cache[0] > l:lnum && b:js_cache[0] < v:lnum || + \ b:js_cache[0] == l:lnum && s:Balanced(l:lnum,pline) + call call('cursor',b:js_cache[1:]) + else + let [s:looksyn, s:top_col, s:check_in, s:l1] = [v:lnum - 1,0,0, + \ max([s:l1, &smc ? search('\m^.\{'.&smc.',}','nbW',s:l1 + 1) + 1 : 0])] + try + if idx != -1 + call s:GetPair(['\[','(','{'][idx],'])}'[idx],'bW','s:SkipFunc()') + elseif getline(v:lnum) !~ '^\S' && s:stack[-1] =~? 'block\|^jsobject$' + call s:GetPair('{','}','bW','s:SkipFunc()') + else + call s:AlternatePair() + endif + catch /^\Cout of bounds$/ + call cursor(v:lnum,1) + endtry + let b:js_cache[1:] = line('.') == v:lnum ? [0,0] : getpos('.')[1:2] + endif + + let [b:js_cache[0], num] = [v:lnum, b:js_cache[1]] + + let [num_ind, is_op, b_l, l:switch_offset, s:in_jsx] = [s:Nat(indent(num)),0,0,0,0] + if !num || s:LookingAt() == '{' && s:IsBlock() + let ilnum = line('.') + if num && !s:in_jsx && s:LookingAt() == ')' && s:GetPair('(',')','bW',s:skip_expr) + if ilnum == num + let [num, num_ind] = [line('.'), indent('.')] + endif + if idx == -1 && s:PreviousToken() ==# 'switch' && s:IsSwitch() + let l:switch_offset = &cino !~ ':' ? s:sw() : s:ParseCino(':') + if pline[-1:] != '.' && l:line =~# '^\%(default\|case\)\>' + return s:Nat(num_ind + l:switch_offset) + elseif &cino =~ '=' + let l:case_offset = s:ParseCino('=') + endif + endif + endif + if idx == -1 && pline[-1:] !~ '[{;]' + call cursor(l:lnum, len(pline)) + let sol = matchstr(l:line,s:opfirst) + if sol is '' || sol == '/' && s:SynAt(v:lnum, + \ 1 + len(getline(v:lnum)) - len(l:line)) =~? 'regex' + if s:Continues() + let is_op = s:sw() + endif + elseif num && sol =~# '^\%(in\%(stanceof\)\=\|\*\)$' && + \ s:LookingAt() == '}' && s:GetPair('{','}','bW',s:skip_expr) && + \ s:PreviousToken() == ')' && s:GetPair('(',')','bW',s:skip_expr) && + \ (s:PreviousToken() == ']' || s:LookingAt() =~ '\k' && + \ s:{s:PreviousToken() == '*' ? 'Previous' : ''}Token() !=# 'function') + return num_ind + s:sw() + else + let is_op = s:sw() + endif + call cursor(l:lnum, len(pline)) + let b_l = s:Nat(s:IsContOne(is_op) - (!is_op && l:line =~ '^{')) * s:sw() + endif + elseif idx.s:LookingAt().&cino =~ '^-1(.*(' && (search('\m\S','nbW',num) || s:ParseCino('U')) + let pval = s:ParseCino('(') + if !pval + let [Wval, vcol] = [s:ParseCino('W'), virtcol('.')] + if search('\m\S','W',num) + return s:ParseCino('w') ? vcol : virtcol('.')-1 + endif + return Wval ? s:Nat(num_ind + Wval) : vcol + endif + return s:Nat(num_ind + pval + searchpair('\m(','','\m)','nbrmW',s:skip_expr,num) * s:sw()) + endif + + " main return + if l:line =~ '^[])}]\|^|}' + if l:line_raw[0] == ')' + if s:ParseCino('M') + return indent(l:lnum) + elseif num && &cino =~# 'm' && !s:ParseCino('m') + return virtcol('.') - 1 + endif + endif + return num_ind + elseif num + return s:Nat(num_ind + get(l:,'case_offset',s:sw()) + l:switch_offset + b_l + is_op) + endif + + let nest = get(get(b:, 'hi_indent', {}), 'blocklnr') + if nest + return indent(nextnonblank(nest + 1)) + b_l + is_op + endif + + return b_l + is_op +endfunction + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/indent/javascriptreact.vim b/runtime/indent/javascriptreact.vim new file mode 100644 index 0000000..a348209 --- /dev/null +++ b/runtime/indent/javascriptreact.vim @@ -0,0 +1,2 @@ +" Placeholder for backwards compatilibity: .jsx used to stand for JavaScript. +runtime! indent/javascript.vim diff --git a/runtime/indent/json.vim b/runtime/indent/json.vim new file mode 100644 index 0000000..510f7e8 --- /dev/null +++ b/runtime/indent/json.vim @@ -0,0 +1,173 @@ +" Vim indent file +" Language: JSON +" Maintainer: Eli Parra <eli@elzr.com> https://github.com/elzr/vim-json +" Last Change: 2020 Aug 30 +" https://github.com/jakar/vim-json/commit/20b650e22aa750c4ab6a66aa646bdd95d7cd548a#diff-e81fc111b2052e306d126bd9989f7b7c +" 2022 Sep 07: b:undo_indent added by Doug Kearns +" Original Author: Rogerz Zhang <rogerz.zhang at gmail.com> http://github.com/rogerz/vim-json +" Acknowledgement: Based off of vim-javascript maintained by Darrick Wiebe +" http://www.vim.org/scripts/script.php?script_id=2765 + +" 0. Initialization {{{1 +" ================= + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal nosmartindent + +" Now, set up our indentation expression and keys that trigger it. +setlocal indentexpr=GetJSONIndent(v:lnum) +setlocal indentkeys=0{,0},0),0[,0],!^F,o,O,e + +let b:undo_indent = "setl inde< indk< si<" + +" Only define the function once. +if exists("*GetJSONIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +" 1. Variables {{{1 +" ============ + +let s:line_term = '\s*\%(\%(\/\/\).*\)\=$' +" Regex that defines blocks. +let s:block_regex = '\%({\)\s*\%(|\%([*@]\=\h\w*,\=\s*\)\%(,\s*[*@]\=\h\w*\)*|\)\=' . s:line_term + +" 2. Auxiliary Functions {{{1 +" ====================== + +" Check if the character at lnum:col is inside a string. +function s:IsInString(lnum, col) + return synIDattr(synID(a:lnum, a:col, 1), 'name') == 'jsonString' +endfunction + +" Find line above 'lnum' that isn't empty, or in a string. +function s:PrevNonBlankNonString(lnum) + let lnum = prevnonblank(a:lnum) + while lnum > 0 + " If the line isn't empty or in a string, end search. + let line = getline(lnum) + if !(s:IsInString(lnum, 1) && s:IsInString(lnum, strlen(line))) + break + endif + let lnum = prevnonblank(lnum - 1) + endwhile + return lnum +endfunction + +" Check if line 'lnum' has more opening brackets than closing ones. +function s:LineHasOpeningBrackets(lnum) + let open_0 = 0 + let open_2 = 0 + let open_4 = 0 + let line = getline(a:lnum) + let pos = match(line, '[][(){}]', 0) + while pos != -1 + let idx = stridx('(){}[]', line[pos]) + if idx % 2 == 0 + let open_{idx} = open_{idx} + 1 + else + let open_{idx - 1} = open_{idx - 1} - 1 + endif + let pos = match(line, '[][(){}]', pos + 1) + endwhile + return (open_0 > 0) . (open_2 > 0) . (open_4 > 0) +endfunction + +function s:Match(lnum, regex) + let col = match(getline(a:lnum), a:regex) + 1 + return col > 0 && !s:IsInString(a:lnum, col) ? col : 0 +endfunction + +" 3. GetJSONIndent Function {{{1 +" ========================= + +function GetJSONIndent(...) + " 3.1. Setup {{{2 + " ---------- + " For the current line, use the first argument if given, else v:lnum + let clnum = a:0 ? a:1 : v:lnum + + " Set up variables for restoring position in file. Could use clnum here. + let vcol = col('.') + + " 3.2. Work on the current line {{{2 + " ----------------------------- + + " Get the current line. + let line = getline(clnum) + let ind = -1 + + " If we got a closing bracket on an empty line, find its match and indent + " according to it. + let col = matchend(line, '^\s*[]}]') + + if col > 0 && !s:IsInString(clnum, col) + call cursor(clnum, col) + let bs = strpart('{}[]', stridx('}]', line[col - 1]) * 2, 2) + + let pairstart = escape(bs[0], '[') + let pairend = escape(bs[1], ']') + let pairline = searchpair(pairstart, '', pairend, 'bW') + + if pairline > 0 + let ind = indent(pairline) + else + let ind = virtcol('.') - 1 + endif + + return ind + endif + + " If we are in a multi-line string, don't do anything to it. + if s:IsInString(clnum, matchend(line, '^\s*') + 1) + return indent('.') + endif + + " 3.3. Work on the previous line. {{{2 + " ------------------------------- + + let lnum = prevnonblank(clnum - 1) + + if lnum == 0 + return 0 + endif + + " Set up variables for current line. + let line = getline(lnum) + let ind = indent(lnum) + + " If the previous line ended with a block opening, add a level of indent. + " if s:Match(lnum, s:block_regex) + " return indent(lnum) + shiftwidth() + " endif + + " If the previous line contained an opening bracket, and we are still in it, + " add indent depending on the bracket type. + if line =~ '[[({]' + let counts = s:LineHasOpeningBrackets(lnum) + if counts[0] == '1' || counts[1] == '1' || counts[2] == '1' + return ind + shiftwidth() + else + call cursor(clnum, vcol) + end + endif + + " }}}2 + + return ind +endfunction + +" }}}1 + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim:set sw=2 sts=2 ts=8 noet: diff --git a/runtime/indent/jsonc.vim b/runtime/indent/jsonc.vim new file mode 100644 index 0000000..058634a --- /dev/null +++ b/runtime/indent/jsonc.vim @@ -0,0 +1,192 @@ +" Vim indent file +" Language: JSONC (JSON with Comments) +" Original Author: Izhak Jakov <izhak724@gmail.com> +" Acknowledgement: Based off of vim-json maintained by Eli Parra <eli@elzr.com> +" https://github.com/elzr/vim-json +" Last Change: 2021-07-01 +" 2023 Aug 28 by Vim Project (undo_indent) + +" 0. Initialization {{{1 +" ================= + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal nosmartindent + +" Now, set up our indentation expression and keys that trigger it. +setlocal indentexpr=GetJSONCIndent() +setlocal indentkeys=0{,0},0),0[,0],!^F,o,O,e + +let b:undo_indent = "setlocal indentexpr< indentkeys< smartindent<" + +" Only define the function once. +if exists("*GetJSONCIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +" 1. Variables {{{1 +" ============ + +let s:line_term = '\s*\%(\%(\/\/\).*\)\=$' +" Regex that defines blocks. +let s:block_regex = '\%({\)\s*\%(|\%([*@]\=\h\w*,\=\s*\)\%(,\s*[*@]\=\h\w*\)*|\)\=' . s:line_term + +" 2. Auxiliary Functions {{{1 +" ====================== + +" Check if the character at lnum:col is inside a string. +function s:IsInString(lnum, col) + return synIDattr(synID(a:lnum, a:col, 1), 'name') == 'jsonString' +endfunction + +" Find line above 'lnum' that isn't empty, or in a string. +function s:PrevNonBlankNonString(lnum) + let lnum = prevnonblank(a:lnum) + while lnum > 0 + " If the line isn't empty or in a string, end search. + let line = getline(lnum) + if !(s:IsInString(lnum, 1) && s:IsInString(lnum, strlen(line))) + break + endif + let lnum = prevnonblank(lnum - 1) + endwhile + return lnum +endfunction + +" Check if line 'lnum' has more opening brackets than closing ones. +function s:LineHasOpeningBrackets(lnum) + let open_0 = 0 + let open_2 = 0 + let open_4 = 0 + let line = getline(a:lnum) + let pos = match(line, '[][(){}]', 0) + while pos != -1 + let idx = stridx('(){}[]', line[pos]) + if idx % 2 == 0 + let open_{idx} = open_{idx} + 1 + else + let open_{idx - 1} = open_{idx - 1} - 1 + endif + let pos = match(line, '[][(){}]', pos + 1) + endwhile + return (open_0 > 0) . (open_2 > 0) . (open_4 > 0) +endfunction + +function s:Match(lnum, regex) + let col = match(getline(a:lnum), a:regex) + 1 + return col > 0 && !s:IsInString(a:lnum, col) ? col : 0 +endfunction + +" 3. GetJSONCIndent Function {{{1 +" ========================= + +function GetJSONCIndent() + if !exists("s:inside_comment") + let s:inside_comment = 0 + endif + + " 3.1. Setup {{{2 + " ---------- + + " Set up variables for restoring position in file. Could use v:lnum here. + let vcol = col('.') + + " 3.2. Work on the current line {{{2 + " ----------------------------- + + + " Get the current line. + let line = getline(v:lnum) + let ind = -1 + if s:inside_comment == 0 + " TODO iterate through all the matches in a line + let col = matchend(line, '\/\*') + if col > 0 && !s:IsInString(v:lnum, col) + let s:inside_comment = 1 + endif + endif + " If we're in the middle of a comment + if s:inside_comment == 1 + let col = matchend(line, '\*\/') + if col > 0 && !s:IsInString(v:lnum, col) + let s:inside_comment = 0 + endif + return ind + endif + if line =~ '^\s*//' + return ind + endif + + " If we got a closing bracket on an empty line, find its match and indent + " according to it. + let col = matchend(line, '^\s*[]}]') + + if col > 0 && !s:IsInString(v:lnum, col) + call cursor(v:lnum, col) + let bs = strpart('{}[]', stridx('}]', line[col - 1]) * 2, 2) + + let pairstart = escape(bs[0], '[') + let pairend = escape(bs[1], ']') + let pairline = searchpair(pairstart, '', pairend, 'bW') + + if pairline > 0 + let ind = indent(pairline) + else + let ind = virtcol('.') - 1 + endif + + return ind + endif + + " If we are in a multi-line string, don't do anything to it. + if s:IsInString(v:lnum, matchend(line, '^\s*') + 1) + return indent('.') + endif + + " 3.3. Work on the previous line. {{{2 + " ------------------------------- + + let lnum = prevnonblank(v:lnum - 1) + + if lnum == 0 + return 0 + endif + + " Set up variables for current line. + let line = getline(lnum) + let ind = indent(lnum) + + " If the previous line ended with a block opening, add a level of indent. + " if s:Match(lnum, s:block_regex) + " return indent(lnum) + shiftwidth() + " endif + + " If the previous line contained an opening bracket, and we are still in it, + " add indent depending on the bracket type. + if line =~ '[[({]' + let counts = s:LineHasOpeningBrackets(lnum) + if counts[0] == '1' || counts[1] == '1' || counts[2] == '1' + return ind + shiftwidth() + else + call cursor(v:lnum, vcol) + end + endif + + " }}}2 + + return ind +endfunction + +" }}}1 + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim:set sw=2 sts=2 ts=8 noet: diff --git a/runtime/indent/jsp.vim b/runtime/indent/jsp.vim new file mode 100644 index 0000000..6f7069e --- /dev/null +++ b/runtime/indent/jsp.vim @@ -0,0 +1,17 @@ +" Vim filetype indent file +" Language: JSP files +" Maintainer: David Fishburn <fishburn@ianywhere.com> +" Version: 1.0 +" Last Change: Wed Nov 08 2006 11:08:05 AM + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +" If there has been no specific JSP indent script created, +" use the default html indent script which will handle +" html, javascript and most of the JSP constructs. +runtime! indent/html.vim + + diff --git a/runtime/indent/julia.vim b/runtime/indent/julia.vim new file mode 100644 index 0000000..efc98a2 --- /dev/null +++ b/runtime/indent/julia.vim @@ -0,0 +1,500 @@ +" Vim indent file +" Language: Julia +" Maintainer: Carlo Baldassi <carlobaldassi@gmail.com> +" Homepage: https://github.com/JuliaEditorSupport/julia-vim +" Last Change: 2022 Jun 14 +" 2023 Aug 28 by Vim Project (undo_indent) +" Notes: originally based on Bram Moolenaar's indent file for vim + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal autoindent + +setlocal indentexpr=GetJuliaIndent() +setlocal indentkeys+==end,=else,=catch,=finally,),],} +setlocal indentkeys-=0# +setlocal indentkeys-=: +setlocal indentkeys-=0{ +setlocal indentkeys-=0} +setlocal nosmartindent + +let b:undo_indent = "setl ai< inde< indk< si<" + +" Only define the function once. +if exists("*GetJuliaIndent") + finish +endif + +let s:skipPatternsBasic = '\<julia\%(Comment\%([LM]\|Delim\)\)\>' +let s:skipPatterns = '\<julia\%(Comprehension\%(For\|If\)\|RangeKeyword\|Comment\%([LM]\|Delim\)\|\%([bs]\|Shell\|Printf\|Doc\)\?String\|StringPrefixed\|DocStringM\(Raw\)\?\|RegEx\|SymbolS\?\|Macro\|Dotted\)\>' + +function JuliaMatch(lnum, str, regex, st, ...) + let s = a:st + let e = a:0 > 0 ? a:1 : -1 + let basic_skip = a:0 > 1 ? a:2 : 'all' + let skip = basic_skip ==# 'basic' ? s:skipPatternsBasic : s:skipPatterns + while 1 + let f = match(a:str, '\C' . a:regex, s) + if e >= 0 && f >= e + return -1 + endif + if f >= 0 + let attr = synIDattr(synID(a:lnum,f+1,1),"name") + let attrT = synIDattr(synID(a:lnum,f+1,0),"name") + if attr =~# skip || attrT =~# skip + let s = f+1 + continue + endif + endif + break + endwhile + return f +endfunction + +function GetJuliaNestingStruct(lnum, ...) + " Auxiliary function to inspect the block structure of a line + let line = getline(a:lnum) + let s = a:0 > 0 ? a:1 : 0 + let e = a:0 > 1 ? a:2 : -1 + let blocks_stack = [] + let num_closed_blocks = 0 + while 1 + let fb = JuliaMatch(a:lnum, line, '\<\%(if\|else\%(if\)\?\|while\|for\|try\|catch\|finally\|\%(staged\)\?function\|macro\|begin\|mutable\s\+struct\|\%(mutable\s\+\)\@<!struct\|\%(abstract\|primitive\)\s\+type\|let\|\%(bare\)\?module\|quote\|do\)\>', s, e) + let fe = JuliaMatch(a:lnum, line, '\<end\>', s, e) + + if fb < 0 && fe < 0 + " No blocks found + break + end + + if fb >= 0 && (fb < fe || fe < 0) + " The first occurrence is an opening block keyword + " Note: some keywords (elseif,else,catch,finally) are both + " closing blocks and opening new ones + + let i = JuliaMatch(a:lnum, line, '\<if\>', s) + if i >= 0 && i == fb + let s = i+1 + call add(blocks_stack, 'if') + continue + endif + let i = JuliaMatch(a:lnum, line, '\<elseif\>', s) + if i >= 0 && i == fb + let s = i+1 + if len(blocks_stack) > 0 && blocks_stack[-1] == 'if' + let blocks_stack[-1] = 'elseif' + elseif (len(blocks_stack) > 0 && blocks_stack[-1] != 'elseif') || len(blocks_stack) == 0 + call add(blocks_stack, 'elseif') + let num_closed_blocks += 1 + endif + continue + endif + let i = JuliaMatch(a:lnum, line, '\<else\>', s) + if i >= 0 && i == fb + let s = i+1 + if len(blocks_stack) > 0 && blocks_stack[-1] =~# '\<\%(else\)\=if\>' + let blocks_stack[-1] = 'else' + else + call add(blocks_stack, 'else') + let num_closed_blocks += 1 + endif + continue + endif + + let i = JuliaMatch(a:lnum, line, '\<try\>', s) + if i >= 0 && i == fb + let s = i+1 + call add(blocks_stack, 'try') + continue + endif + let i = JuliaMatch(a:lnum, line, '\<catch\>', s) + if i >= 0 && i == fb + let s = i+1 + if len(blocks_stack) > 0 && blocks_stack[-1] == 'try' + let blocks_stack[-1] = 'catch' + else + call add(blocks_stack, 'catch') + let num_closed_blocks += 1 + endif + continue + endif + let i = JuliaMatch(a:lnum, line, '\<finally\>', s) + if i >= 0 && i == fb + let s = i+1 + if len(blocks_stack) > 0 && (blocks_stack[-1] == 'try' || blocks_stack[-1] == 'catch') + let blocks_stack[-1] = 'finally' + else + call add(blocks_stack, 'finally') + let num_closed_blocks += 1 + endif + continue + endif + + let i = JuliaMatch(a:lnum, line, '\<\%(bare\)\?module\>', s) + if i >= 0 && i == fb + let s = i+1 + if i == 0 + call add(blocks_stack, 'col1module') + else + call add(blocks_stack, 'other') + endif + continue + endif + + let i = JuliaMatch(a:lnum, line, '\<\%(while\|for\|function\|macro\|begin\|\%(mutable\s\+\)\?struct\|\%(abstract\|primitive\)\s\+type\|let\|quote\|do\)\>', s) + if i >= 0 && i == fb + if match(line, '\C\<\%(mutable\|abstract\|primitive\)', i) != -1 + let s = i+11 + else + let s = i+1 + endif + call add(blocks_stack, 'other') + continue + endif + + " Note: it should be impossible to get here + break + + else + " The first occurrence is an 'end' + + let s = fe+1 + if len(blocks_stack) == 0 + let num_closed_blocks += 1 + else + call remove(blocks_stack, -1) + endif + continue + + endif + + " Note: it should be impossible to get here + break + endwhile + let num_open_blocks = len(blocks_stack) - count(blocks_stack, 'col1module') + return [num_open_blocks, num_closed_blocks] +endfunction + +function GetJuliaNestingBrackets(lnum, c) + " Auxiliary function to inspect the brackets structure of a line + let line = getline(a:lnum)[0 : (a:c - 1)] + let s = 0 + let brackets_stack = [] + let last_closed_bracket = -1 + while 1 + let fb = JuliaMatch(a:lnum, line, '[([{]', s) + let fe = JuliaMatch(a:lnum, line, '[])}]', s) + + if fb < 0 && fe < 0 + " No brackets found + break + end + + if fb >= 0 && (fb < fe || fe < 0) + " The first occurrence is an opening bracket + + let i = JuliaMatch(a:lnum, line, '(', s) + if i >= 0 && i == fb + let s = i+1 + call add(brackets_stack, ['par',i]) + continue + endif + + let i = JuliaMatch(a:lnum, line, '\[', s) + if i >= 0 && i == fb + let s = i+1 + call add(brackets_stack, ['sqbra',i]) + continue + endif + + let i = JuliaMatch(a:lnum, line, '{', s) + if i >= 0 && i == fb + let s = i+1 + call add(brackets_stack, ['curbra',i]) + continue + endif + + " Note: it should be impossible to get here + break + + else + " The first occurrence is a closing bracket + + let i = JuliaMatch(a:lnum, line, ')', s) + if i >= 0 && i == fe + let s = i+1 + if len(brackets_stack) > 0 && brackets_stack[-1][0] == 'par' + call remove(brackets_stack, -1) + else + let last_closed_bracket = i + 1 + endif + continue + endif + + let i = JuliaMatch(a:lnum, line, ']', s) + if i >= 0 && i == fe + let s = i+1 + if len(brackets_stack) > 0 && brackets_stack[-1][0] == 'sqbra' + call remove(brackets_stack, -1) + else + let last_closed_bracket = i + 1 + endif + continue + endif + + let i = JuliaMatch(a:lnum, line, '}', s) + if i >= 0 && i == fe + let s = i+1 + if len(brackets_stack) > 0 && brackets_stack[-1][0] == 'curbra' + call remove(brackets_stack, -1) + else + let last_closed_bracket = i + 1 + endif + continue + endif + + " Note: it should be impossible to get here + break + + endif + + " Note: it should be impossible to get here + break + endwhile + let first_open_bracket = -1 + let last_open_bracket = -1 + let infuncargs = 0 + if len(brackets_stack) > 0 + let first_open_bracket = brackets_stack[0][1] + let last_open_bracket = brackets_stack[-1][1] + if brackets_stack[-1][0] == 'par' && IsFunctionArgPar(a:lnum, last_open_bracket+1) + let infuncargs = 1 + endif + endif + return [first_open_bracket, last_open_bracket, last_closed_bracket, infuncargs] +endfunction + +let s:bracketBlocks = '\<julia\%(\%(\%(Printf\)\?Par\|SqBra\%(Idx\)\?\|CurBra\)Block\|ParBlockInRange\|StringVars\%(Par\|SqBra\|CurBra\)\|Dollar\%(Par\|SqBra\)\|QuotedParBlockS\?\)\>' + +function IsInBrackets(lnum, c) + let stack = map(synstack(a:lnum, a:c), 'synIDattr(v:val, "name")') + call filter(stack, 'v:val =~# s:bracketBlocks') + return len(stack) > 0 +endfunction + +function IsInDocString(lnum) + let stack = map(synstack(a:lnum, 1), 'synIDattr(v:val, "name")') + call filter(stack, 'v:val =~# "\\<juliaDocString\\(Delim\\|M\\\(Raw\\)\\?\\)\\?\\>"') + return len(stack) > 0 +endfunction + +function IsInContinuationImportLine(lnum) + let stack = map(synstack(a:lnum, 1), 'synIDattr(v:val, "name")') + call filter(stack, 'v:val =~# "\\<juliaImportLine\\>"') + if len(stack) == 0 + return 0 + endif + return JuliaMatch(a:lnum, getline(a:lnum), '\<\%(import\|using\|export\)\>', indent(a:lnum)) == -1 +endfunction + +function IsFunctionArgPar(lnum, c) + if a:c == 0 + return 0 + endif + let stack = map(synstack(a:lnum, a:c-1), 'synIDattr(v:val, "name")') + return len(stack) >= 2 && stack[-2] ==# 'juliaFunctionDef' +endfunction + +function JumpToMatch(lnum, last_closed_bracket) + " we use the % command to skip back (tries to use matchit if possible, + " otherwise resorts to vim's default, which is buggy but better than + " nothing) + call cursor(a:lnum, a:last_closed_bracket) + let percmap = maparg("%", "n") + if exists("g:loaded_matchit") && percmap =~# 'Match\%(it\|_wrapper\)' + normal % + else + normal! % + end +endfunction + +" Auxiliary function to find a line which does not start in the middle of a +" multiline bracketed expression, to be used as reference for block +" indentation. +function LastBlockIndent(lnum) + let lnum = a:lnum + let ind = 0 + while lnum > 0 + let ind = indent(lnum) + if ind == 0 + return [lnum, 0] + endif + if !IsInBrackets(lnum, 1) + break + endif + let lnum = prevnonblank(lnum - 1) + endwhile + return [max([lnum,1]), ind] +endfunction + +function GetJuliaIndent() + " Do not alter doctrings indentation + if IsInDocString(v:lnum) + return -1 + endif + + " Find a non-blank line above the current line. + let lnum = prevnonblank(v:lnum - 1) + + " At the start of the file use zero indent. + if lnum == 0 + return 0 + endif + + let ind = -1 + let st = -1 + let lim = -1 + + " Multiline bracketed expressions take precedence + let align_brackets = get(g:, "julia_indent_align_brackets", 1) + let align_funcargs = get(g:, "julia_indent_align_funcargs", 0) + let c = len(getline(lnum)) + 1 + while IsInBrackets(lnum, c) + let [first_open_bracket, last_open_bracket, last_closed_bracket, infuncargs] = GetJuliaNestingBrackets(lnum, c) + + " First scenario: the previous line has a hanging open bracket: + " set the indentation to match the opening bracket (plus an extra space) + " unless we're in a function arguments list or alignment is disabled, in + " which case we just add an extra indent + if last_open_bracket != -1 + if (!infuncargs && align_brackets) || (infuncargs && align_funcargs) + let st = last_open_bracket + let ind = virtcol([lnum, st + 1]) + else + let ind = indent(lnum) + shiftwidth() + endif + + " Second scenario: some multiline bracketed expression was closed in the + " previous line. But since we know we are still in a bracketed expression, + " we need to find the line where the bracket was opened + elseif last_closed_bracket != -1 + call JumpToMatch(lnum, last_closed_bracket) + if line(".") == lnum + " something wrong here, give up + let ind = indent(lnum) + else + let lnum = line(".") + let c = col(".") - 1 + if c == 0 + " uhm, give up + let ind = 0 + else + " we skipped a bracket set, keep searching for an opening bracket + let lim = c + continue + endif + endif + + " Third scenario: nothing special: keep the indentation + else + let ind = indent(lnum) + endif + + " Does the current line start with a closing bracket? Then depending on + " the situation we align it with the opening one, or we let the rest of + " the code figure it out (the case in which we're closing a function + " argument list is special-cased) + if JuliaMatch(v:lnum, getline(v:lnum), '[])}]', indent(v:lnum)) == indent(v:lnum) && ind > 0 + if !align_brackets && !align_funcargs + call JumpToMatch(v:lnum, indent(v:lnum)) + return indent(line(".")) + elseif (align_brackets && getline(v:lnum)[indent(v:lnum)] != ')') || align_funcargs + return ind - 1 + else " must be a ')' and align_brackets==1 and align_funcargs==0 + call JumpToMatch(v:lnum, indent(v:lnum)) + if IsFunctionArgPar(line("."), col(".")) + let ind = -1 + else + return ind - 1 + endif + endif + endif + + break + endwhile + + if ind == -1 + " We are not in a multiline bracketed expression. Thus we look for a + " previous line to use as a reference + let [lnum,ind] = LastBlockIndent(lnum) + let c = len(getline(lnum)) + 1 + if IsInBrackets(lnum, c) + let [first_open_bracket, last_open_bracket, last_closed_bracket, infuncargs] = GetJuliaNestingBrackets(lnum, c) + let lim = first_open_bracket + endif + end + + " Analyse the reference line + let [num_open_blocks, num_closed_blocks] = GetJuliaNestingStruct(lnum, st, lim) + " Increase indentation for each newly opened block in the reference line + let ind += shiftwidth() * num_open_blocks + + " Analyse the current line + let [num_open_blocks, num_closed_blocks] = GetJuliaNestingStruct(v:lnum) + " Decrease indentation for each closed block in the current line + let ind -= shiftwidth() * num_closed_blocks + + " Additional special case: multiline import/using/export statements + + let prevline = getline(lnum) + " Are we in a multiline import/using/export statement, right below the + " opening line? + if IsInContinuationImportLine(v:lnum) && !IsInContinuationImportLine(lnum) + if get(g:, 'julia_indent_align_import', 1) + " if the opening line has a colon followed by non-comments, use it as + " reference point + let cind = JuliaMatch(lnum, prevline, ':', indent(lnum), lim) + if cind >= 0 + let nonwhiteind = JuliaMatch(lnum, prevline, '\S', cind+1, -1, 'basic') + if nonwhiteind >= 0 + " return match(prevline, '\S', cind+1) " a bit overkill... + return cind + 2 + endif + else + " if the opening line is not a naked import/using/export statement, use + " it as reference + let iind = JuliaMatch(lnum, prevline, '\<import\|using\|export\>', indent(lnum), lim) + if iind >= 0 + " assuming whitespace after using... so no `using(XYZ)` please! + let nonwhiteind = JuliaMatch(lnum, prevline, '\S', iind+6, -1, 'basic') + if nonwhiteind >= 0 + return match(prevline, '\S', iind+6) + endif + endif + endif + endif + let ind += shiftwidth() + + " Or did we just close a multiline import/using/export statement? + elseif !IsInContinuationImportLine(v:lnum) && IsInContinuationImportLine(lnum) + " find the starting line of the statement + let ilnum = 0 + for iln in range(lnum-1, 1, -1) + if !IsInContinuationImportLine(iln) + let ilnum = iln + break + endif + endfor + if ilnum == 0 + " something went horribly wrong, give up + let ind = indent(lnum) + endif + let ind = indent(ilnum) + endif + + return ind +endfunction diff --git a/runtime/indent/kotlin.vim b/runtime/indent/kotlin.vim new file mode 100644 index 0000000..590a507 --- /dev/null +++ b/runtime/indent/kotlin.vim @@ -0,0 +1,60 @@ +" Vim indent file +" Language: Kotlin +" Maintainer: Alexander Udalov +" URL: https://github.com/udalov/kotlin-vim +" Last Change: 7 November 2021 +" 2023 Sep 17 by Vim Project (undo_indent) + +if exists('b:did_indent') + finish +endif +let b:did_indent = 1 + +setlocal cinoptions& cinoptions+=j1,L0 +setlocal indentexpr=GetKotlinIndent() +setlocal indentkeys=0},0),!^F,o,O,e,<CR> +setlocal autoindent " TODO ? + +let b:undo_indent = "setlocal autoindent< cinoptions< indentexpr< indentkeys<" + +" TODO teach it to count bracket balance, etc. +function! GetKotlinIndent() + if v:lnum == 0 + return 0 + endif + + let prev_num = prevnonblank(v:lnum - 1) + let prev = getline(prev_num) + let prev_indent = indent(prev_num) + let cur = getline(v:lnum) + + if cur =~ '^\s*\*' + return cindent(v:lnum) + endif + + if prev =~ '^\s*\*/' + let st = prev + while st > 1 + if getline(st) =~ '^\s*/\*' + break + endif + let st = st - 1 + endwhile + return indent(st) + endif + + let prev_open_paren = prev =~ '^.*(\s*$' + let cur_close_paren = cur =~ '^\s*).*$' + let prev_open_brace = prev =~ '^.*\({\|->\)\s*$' + let cur_close_brace = cur =~ '^\s*}.*$' + + if prev_open_paren && !cur_close_paren || prev_open_brace && !cur_close_brace + return prev_indent + shiftwidth() + endif + + if cur_close_paren && !prev_open_paren || cur_close_brace && !prev_open_brace + return prev_indent - shiftwidth() + endif + + return prev_indent +endfunction diff --git a/runtime/indent/krl.vim b/runtime/indent/krl.vim new file mode 100644 index 0000000..89f4535 --- /dev/null +++ b/runtime/indent/krl.vim @@ -0,0 +1,130 @@ +" Vim indent file +" Language: Kuka Robot Language +" Maintainer: Patrick Meiser-Knosowski <knosowski@graeffrobotics.de> +" Version: 3.0.0 +" Last Change: 15. Apr 2022 +" Credits: Based on indent/vim.vim + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal nolisp +setlocal nocindent +setlocal nosmartindent +setlocal autoindent +setlocal indentexpr=GetKrlIndent() +setlocal indentkeys=!^F,o,O,=~end,0=~else,0=~case,0=~default,0=~until,0=~continue,=~part +let b:undo_indent = "setlocal lisp< cindent< smartindent< autoindent< indentexpr< indentkeys<" + +if get(g:,'krlSpaceIndent',1) + " Use spaces, not tabs, for indention, 2 is enough. + " More or even tabs would waste valuable space on the teach pendant. + setlocal softtabstop=2 + setlocal shiftwidth=2 + setlocal expandtab + setlocal shiftround + let b:undo_indent = b:undo_indent." softtabstop< shiftwidth< expandtab< shiftround<" +endif + +" Only define the function once. +if exists("*GetKrlIndent") + finish +endif +let s:keepcpo = &cpo +set cpo&vim + +function GetKrlIndent() abort + + let currentLine = getline(v:lnum) + if currentLine =~? '\v^;(\s*(end)?fold>)@!' && !get(g:, 'krlCommentIndent', 0) + " If current line has a ; in column 1 and is no fold, keep zero indent. + " This may be useful if code is commented out at the first column. + return 0 + endif + + " Find a non-blank line above the current line. + let preNoneBlankLineNum = s:KrlPreNoneBlank(v:lnum - 1) + if preNoneBlankLineNum == 0 + " At the start of the file use zero indent. + return 0 + endif + + let preNoneBlankLine = getline(preNoneBlankLineNum) + let ind = indent(preNoneBlankLineNum) + + " Define add 'shiftwidth' pattern + let addShiftwidthPattern = '\v^\s*(' + if get(g:, 'krlIndentBetweenDef', 1) + let addShiftwidthPattern ..= '(global\s+)?def(fct|dat)?\s+\$?\w' + let addShiftwidthPattern ..= '|' + endif + let addShiftwidthPattern ..= 'if>|while>|for>|loop>' + let addShiftwidthPattern ..= '|else>' + let addShiftwidthPattern ..= '|case>|default>' + let addShiftwidthPattern ..= '|repeat>' + let addShiftwidthPattern ..= '|skip>|(ptp_)?spline>' + let addShiftwidthPattern ..= '|time_block\s+(start|part)>' + let addShiftwidthPattern ..= '|const_vel\s+start>' + let addShiftwidthPattern ..= ')' + + " Define Subtract 'shiftwidth' pattern + let subtractShiftwidthPattern = '\v^\s*(' + if get(g:, 'krlIndentBetweenDef', 1) + let subtractShiftwidthPattern ..= 'end(fct|dat)?>' + let subtractShiftwidthPattern ..= '|' + endif + let subtractShiftwidthPattern ..= 'end(if|while|for|loop)>' + let subtractShiftwidthPattern ..= '|else>' + let subtractShiftwidthPattern ..= '|case>|default>|endswitch>' + let subtractShiftwidthPattern ..= '|until>' + let subtractShiftwidthPattern ..= '|end(skip|spline)>' + let subtractShiftwidthPattern ..= '|time_block\s+(part|end)>' + let subtractShiftwidthPattern ..= '|const_vel\s+end>' + let subtractShiftwidthPattern ..= ')' + + " Add shiftwidth + if preNoneBlankLine =~? addShiftwidthPattern + let ind += &sw + endif + + " Subtract shiftwidth + if currentLine =~? subtractShiftwidthPattern + let ind = ind - &sw + endif + + " First case after a switch gets the indent of the switch. + if currentLine =~? '\v^\s*case>' + \&& preNoneBlankLine =~? '\v^\s*switch>' + let ind = ind + &sw + endif + + " align continue with the following instruction + if currentLine =~? '\v^\s*continue>' + \&& getline(v:lnum + 1) =~? subtractShiftwidthPattern + let ind = ind - &sw + endif + + return ind +endfunction + +" This function works almost like prevnonblank() but handles &-headers, +" comments and continue instructions like blank lines +function s:KrlPreNoneBlank(lnum) abort + + let nPreNoneBlank = prevnonblank(a:lnum) + + while nPreNoneBlank > 0 && getline(nPreNoneBlank) =~? '\v^\s*(\&\w\+|;|continue>)' + " Previous none blank line irrelevant. Look further aback. + let nPreNoneBlank = prevnonblank(nPreNoneBlank - 1) + endwhile + + return nPreNoneBlank +endfunction + +let &cpo = s:keepcpo +unlet s:keepcpo + +" vim:sw=2 sts=2 et diff --git a/runtime/indent/ld.vim b/runtime/indent/ld.vim new file mode 100644 index 0000000..ddf003e --- /dev/null +++ b/runtime/indent/ld.vim @@ -0,0 +1,87 @@ +" Vim indent file +" Language: ld(1) script +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Last Change: 24 Sep 2021 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetLDIndent() +setlocal indentkeys=0{,0},!^F,o,O +setlocal nosmartindent + +let b:undo_indent = "setl inde< indk< si<" + +if exists("*GetLDIndent") + finish +endif + +function s:prevnonblanknoncomment(lnum) + let lnum = a:lnum + while lnum > 1 + let lnum = prevnonblank(lnum) + let line = getline(lnum) + if line =~ '\*/' + while lnum > 1 && line !~ '/\*' + let lnum -= 1 + endwhile + if line =~ '^\s*/\*' + let lnum -= 1 + else + break + endif + else + break + endif + endwhile + return lnum +endfunction + +function s:count_braces(lnum, count_open) + let n_open = 0 + let n_close = 0 + let line = getline(a:lnum) + let pattern = '[{}]' + let i = match(line, pattern) + while i != -1 + if synIDattr(synID(a:lnum, i + 1, 0), 'name') !~ 'ld\%(Comment\|String\)' + if line[i] == '{' + let n_open += 1 + elseif line[i] == '}' + if n_open > 0 + let n_open -= 1 + else + let n_close += 1 + endif + endif + endif + let i = match(line, pattern, i + 1) + endwhile + return a:count_open ? n_open : n_close +endfunction + +function GetLDIndent() + let line = getline(v:lnum) + if line =~ '^\s*\*' + return cindent(v:lnum) + elseif line =~ '^\s*}' + return indent(v:lnum) - shiftwidth() + endif + + let pnum = s:prevnonblanknoncomment(v:lnum - 1) + if pnum == 0 + return 0 + endif + + let ind = indent(pnum) + s:count_braces(pnum, 1) * shiftwidth() + + let pline = getline(pnum) + if pline =~ '}\s*$' + let ind -= (s:count_braces(pnum, 0) - (pline =~ '^\s*}' ? 1 : 0)) * shiftwidth() + endif + + return ind +endfunction diff --git a/runtime/indent/less.vim b/runtime/indent/less.vim new file mode 100644 index 0000000..82bf2d8 --- /dev/null +++ b/runtime/indent/less.vim @@ -0,0 +1,13 @@ +" Vim indent file +" Language: less +" Maintainer: Alessandro Vioni <jenoma@gmail.com> +" URL: https://github.com/genoma/vim-less +" Last Change: 2014 November 24 + +if exists("b:did_indent") + finish +endif + +runtime! indent/css.vim + +" vim:set sw=2: diff --git a/runtime/indent/lifelines.vim b/runtime/indent/lifelines.vim new file mode 100644 index 0000000..e6d6161 --- /dev/null +++ b/runtime/indent/lifelines.vim @@ -0,0 +1,24 @@ +" Vim indent file +" Language: LifeLines +" Maintainer: Patrick Texier <p.texier@orsennes.com> +" Location: <http://patrick.texier.free.fr/vim/indent/lifelines.vim> +" Last Change: 2010 May 7 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" LifeLines uses cindent without ; line terminator, C functions +" declarations, C keywords, C++ formatting +setlocal cindent +setlocal cinwords="" +setlocal cinoptions+=+0 +setlocal cinoptions+=p0 +setlocal cinoptions+=i0 +setlocal cinoptions+=t0 +setlocal cinoptions+=*500 + +let b:undo_indent = "setl cin< cino< cinw<" +" vim: ts=8 sw=4 diff --git a/runtime/indent/liquid.vim b/runtime/indent/liquid.vim new file mode 100644 index 0000000..6fc9337 --- /dev/null +++ b/runtime/indent/liquid.vim @@ -0,0 +1,66 @@ +" Vim indent file +" Language: Liquid +" Maintainer: Tim Pope <vimNOSPAM@tpope.org> +" Last Change: 2022 Mar 15 + +if exists('b:did_indent') + finish +endif + +set indentexpr= +if exists('b:liquid_subtype') + exe 'runtime! indent/'.b:liquid_subtype.'.vim' +else + runtime! indent/html.vim +endif +unlet! b:did_indent + +if &l:indentexpr == '' + if &l:cindent + let &l:indentexpr = 'cindent(v:lnum)' + else + let &l:indentexpr = 'indent(prevnonblank(v:lnum-1))' + endif +endif +let b:liquid_subtype_indentexpr = &l:indentexpr + +let b:did_indent = 1 + +setlocal indentexpr=GetLiquidIndent() +setlocal indentkeys=o,O,*<Return>,<>>,{,},0),0],o,O,!^F,=end,=endif,=endunless,=endifchanged,=endcase,=endfor,=endtablerow,=endcapture,=else,=elsif,=when,=empty + +let b:undo_indent = "setl inde< indk<" + +" Only define the function once. +if exists('*GetLiquidIndent') + finish +endif + +function! s:count(string, pattern) abort + let string = substitute(a:string,'\C'.a:pattern,"\n",'g') + return strlen(substitute(string,"[^\n]",'','g')) +endfunction + +function! GetLiquidIndent(...) abort + if a:0 && a:1 == '.' + let v:lnum = line('.') + elseif a:0 && a:1 =~ '^\d' + let v:lnum = a:1 + endif + let vcol = col('.') + call cursor(v:lnum,1) + exe "let ind = ".b:liquid_subtype_indentexpr + let lnum = prevnonblank(v:lnum-1) + let line = getline(lnum) + let cline = getline(v:lnum) + let line = substitute(line,'\C^\%(\s*{%-\=\s*end\w*\s*-\=%}\)\+','','') + let line = substitute(line,'\C\%(\s*{%-\=\s*if.\+-\=%}.\+{%-\=\s*endif\s*-\=%}\)\+','','g') + let line .= matchstr(cline,'\C^\%(\s*{%-\=\s*end\w*\s*-\=%}\)\+') + let cline = substitute(cline,'\C^\%(\s*{%-\=\s*end\w*\s*-\=%}\)\+','','') + let sw = shiftwidth() + let ind += sw * s:count(line,'{%-\=\s*\%(if\|elsif\|else\|unless\|ifchanged\|case\|when\|for\|empty\|tablerow\|capture\)\>') + let ind -= sw * s:count(line,'{%-\=\s*end\%(if\|unless\|ifchanged\|case\|for\|tablerow\|capture\)\>') + let ind -= sw * s:count(cline,'{%-\=\s*\%(elsif\|else\|when\|empty\)\>') + let ind -= sw * s:count(cline,'{%-\=\s*end\w*$') + return ind +endfunction diff --git a/runtime/indent/lisp.vim b/runtime/indent/lisp.vim new file mode 100644 index 0000000..1bce395 --- /dev/null +++ b/runtime/indent/lisp.vim @@ -0,0 +1,15 @@ +" Vim indent file +" Language: Lisp +" Maintainer: Sergey Khorev <sergey.khorev@gmail.com> +" URL: http://sites.google.com/site/khorser/opensource/vim +" Last Change: 2012 Jan 10 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal ai nosi + +let b:undo_indent = "setl ai< si<" diff --git a/runtime/indent/livebook.vim b/runtime/indent/livebook.vim new file mode 100644 index 0000000..6311050 --- /dev/null +++ b/runtime/indent/livebook.vim @@ -0,0 +1,9 @@ +" Placeholder livebook indent file. +" This simply uses the markdown indenting. + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +runtime! indent/markdown.vim diff --git a/runtime/indent/logtalk.vim b/runtime/indent/logtalk.vim new file mode 100644 index 0000000..f7a8b03 --- /dev/null +++ b/runtime/indent/logtalk.vim @@ -0,0 +1,67 @@ +" Maintainer: Paulo Moura <pmoura@logtalk.org> +" Revised on: 2018.08.04 +" 2023 Aug 28 by Vim Project (undo_indent) +" Language: Logtalk + +" This Logtalk indent file is a modified version of the Prolog +" indent file written by Gergely Kontra + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +let b:did_indent = 1 + +setlocal indentexpr=GetLogtalkIndent() +setlocal indentkeys-=:,0# +setlocal indentkeys+=0%,-,0;,>,0) + +let b:undo_indent = "setlocal indentexpr< indentkeys<" + +" Only define the function once. +if exists("*GetLogtalkIndent") + finish +endif + +function! GetLogtalkIndent() + " Find a non-blank line above the current line. + let pnum = prevnonblank(v:lnum - 1) + " Hit the start of the file, use zero indent. + if pnum == 0 + return 0 + endif + let line = getline(v:lnum) + let pline = getline(pnum) + + let ind = indent(pnum) + " Previous line was comment -> use previous line's indent + if pline =~ '^\s*%' + retu ind + endif + " Check for entity opening directive on previous line + if pline =~ '^\s*:-\s\(object\|protocol\|category\)\ze(.*,$' + let ind = ind + shiftwidth() + " Check for clause head on previous line + elseif pline =~ ':-\s*\(%.*\)\?$' + let ind = ind + shiftwidth() + " Check for grammar rule head on previous line + elseif pline =~ '-->\s*\(%.*\)\?$' + let ind = ind + shiftwidth() + " Check for entity closing directive on previous line + elseif pline =~ '^\s*:-\send_\(object\|protocol\|category\)\.\(%.*\)\?$' + let ind = ind - shiftwidth() + " Check for end of clause on previous line + elseif pline =~ '\.\s*\(%.*\)\?$' + let ind = ind - shiftwidth() + endif + " Check for opening conditional on previous line + if pline =~ '^\s*\([(;]\|->\)' && pline !~ '\.\s*\(%.*\)\?$' && pline !~ '^.*\([)][,]\s*\(%.*\)\?$\)' + let ind = ind + shiftwidth() + endif + " Check for closing an unclosed paren, or middle ; or -> + if line =~ '^\s*\([);]\|->\)' + let ind = ind - shiftwidth() + endif + return ind +endfunction diff --git a/runtime/indent/lua.vim b/runtime/indent/lua.vim new file mode 100644 index 0000000..35b08d4 --- /dev/null +++ b/runtime/indent/lua.vim @@ -0,0 +1,76 @@ +" Vim indent file +" Language: Lua script +" Maintainer: Marcus Aurelius Farias <marcus.cf 'at' bol.com.br> +" First Author: Max Ischenko <mfi 'at' ukr.net> +" Last Change: 2017 Jun 13 +" 2022 Sep 07: b:undo_indent added by Doug Kearns + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetLuaIndent() + +" To make Vim call GetLuaIndent() when it finds '\s*end' or '\s*until' +" on the current line ('else' is default and includes 'elseif'). +setlocal indentkeys+=0=end,0=until + +setlocal autoindent + +let b:undo_indent = "setlocal autoindent< indentexpr< indentkeys<" + +" Only define the function once. +if exists("*GetLuaIndent") + finish +endif + +function! GetLuaIndent() + let ignorecase_save = &ignorecase + try + let &ignorecase = 0 + return GetLuaIndentIntern() + finally + let &ignorecase = ignorecase_save + endtry +endfunction + +function! GetLuaIndentIntern() + " Find a non-blank line above the current line. + let prevlnum = prevnonblank(v:lnum - 1) + + " Hit the start of the file, use zero indent. + if prevlnum == 0 + return 0 + endif + + " Add a 'shiftwidth' after lines that start a block: + " 'function', 'if', 'for', 'while', 'repeat', 'else', 'elseif', '{' + let ind = indent(prevlnum) + let prevline = getline(prevlnum) + let midx = match(prevline, '^\s*\%(if\>\|for\>\|while\>\|repeat\>\|else\>\|elseif\>\|do\>\|then\>\)') + if midx == -1 + let midx = match(prevline, '{\s*\%(--\%([^[].*\)\?\)\?$') + if midx == -1 + let midx = match(prevline, '\<function\>\s*\%(\k\|[.:]\)\{-}\s*(') + endif + endif + + if midx != -1 + " Add 'shiftwidth' if what we found previously is not in a comment and + " an "end" or "until" is not present on the same line. + if synIDattr(synID(prevlnum, midx + 1, 1), "name") != "luaComment" && prevline !~ '\<end\>\|\<until\>' + let ind = ind + shiftwidth() + endif + endif + + " Subtract a 'shiftwidth' on end, else, elseif, until and '}' + " This is the part that requires 'indentkeys'. + let midx = match(getline(v:lnum), '^\s*\%(end\>\|else\>\|elseif\>\|until\>\|}\)') + if midx != -1 && synIDattr(synID(v:lnum, midx + 1, 1), "name") != "luaComment" + let ind = ind - shiftwidth() + endif + + return ind +endfunction diff --git a/runtime/indent/luau.vim b/runtime/indent/luau.vim new file mode 100644 index 0000000..69893f7 --- /dev/null +++ b/runtime/indent/luau.vim @@ -0,0 +1,14 @@ +" Vim filetype indent file +" Language: Luau +" Maintainer: None yet +" Last Change: 2023 Apr 30 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +" Luau is a superset of Lua +runtime! indent/lua.vim + + diff --git a/runtime/indent/mail.vim b/runtime/indent/mail.vim new file mode 100644 index 0000000..eec9b4e --- /dev/null +++ b/runtime/indent/mail.vim @@ -0,0 +1,15 @@ +" Vim indent file +" Language: Mail +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Last Change: 2023 Aug 13 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" What works best is auto-indenting, disable other indenting. +" For formatting see the ftplugin. +setlocal autoindent nosmartindent nocindent indentexpr= + +let b:undo_indent = "setl ai< cin< inde< si<" diff --git a/runtime/indent/make.vim b/runtime/indent/make.vim new file mode 100644 index 0000000..4d1838b --- /dev/null +++ b/runtime/indent/make.vim @@ -0,0 +1,119 @@ +" Vim indent file +" Language: Makefile +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Last Change: 2022 Apr 06 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetMakeIndent() +setlocal indentkeys=!^F,o,O,<:>,=else,=endif +setlocal nosmartindent + +let b:undo_indent = "setl inde< indk< si<" + +if exists("*GetMakeIndent") + finish +endif + +let s:comment_rx = '^\s*#' +let s:rule_rx = '^[^ \t#:][^#:]*:\{1,2}\%([^=:]\|$\)' +let s:continued_rule_rx = '^[^#:]*:\{1,2}\%([^=:]\|$\)' +let s:continuation_rx = '\\$' +let s:assignment_rx = '^\s*\h\w*\s*[+:?]\==\s*\zs.*\\$' +let s:folded_assignment_rx = '^\s*\h\w*\s*[+:?]\==' +" TODO: This needs to be a lot more restrictive in what it matches. +let s:just_inserted_rule_rx = '^\s*[^#:]\+:\{1,2}$' +let s:conditional_directive_rx = '^ *\%(ifn\=\%(eq\|def\)\|else\)\>' +let s:end_conditional_directive_rx = '^\s*\%(else\|endif\)\>' + +function s:remove_continuation(line) + return substitute(a:line, s:continuation_rx, "", "") +endfunction + +function GetMakeIndent() + " TODO: Should this perhaps be v:lnum -1? +" let prev_lnum = prevnonblank(v:lnum - 1) + let prev_lnum = v:lnum - 1 + if prev_lnum == 0 + return 0 + endif + let prev_line = getline(prev_lnum) + + let prev_prev_lnum = prev_lnum - 1 + let prev_prev_line = prev_prev_lnum != 0 ? getline(prev_prev_lnum) : "" + + " TODO: Deal with comments. In comments, continuations aren't interesting. + if prev_line =~ s:continuation_rx + if prev_prev_line =~ s:continuation_rx + return indent(prev_lnum) + elseif prev_line =~ s:rule_rx + return shiftwidth() + elseif prev_line =~ s:assignment_rx + call cursor(prev_lnum, 1) + if search(s:assignment_rx, 'W') != 0 + return virtcol('.') - 1 + else + " TODO: ? + return shiftwidth() + endif + else + " TODO: OK, this might be a continued shell command, so perhaps indent + " properly here? Leave this out for now, but in the next release this + " should be using indent/sh.vim somehow. + "if prev_line =~ '^\t' " s:rule_command_rx + " if prev_line =~ '^\s\+[@-]\%(if\)\>' + " return indent(prev_lnum) + 2 + " endif + "endif + return indent(prev_lnum) + shiftwidth() + endif + elseif prev_prev_line =~ s:continuation_rx + let folded_line = s:remove_continuation(prev_prev_line) . ' ' . s:remove_continuation(prev_line) + let lnum = prev_prev_lnum - 1 + let line = getline(lnum) + while line =~ s:continuation_rx + let folded_line = s:remove_continuation(line) . ' ' . folded_line + let lnum -= 1 + let line = getline(lnum) + endwhile + let folded_lnum = lnum + 1 + if folded_line =~ s:rule_rx + if getline(v:lnum) =~ s:rule_rx + return 0 + else + return &ts + endif + else +" elseif folded_line =~ s:folded_assignment_rx + if getline(v:lnum) =~ s:rule_rx + return 0 + else + return indent(folded_lnum) + endif +" else +" " TODO: ? +" return indent(prev_lnum) + endif + elseif prev_line =~ s:rule_rx + if getline(v:lnum) =~ s:rule_rx + return 0 + else + return &ts + endif + elseif prev_line =~ s:conditional_directive_rx + return shiftwidth() + else + let line = getline(v:lnum) + if line =~ s:just_inserted_rule_rx + return 0 + elseif line =~ s:end_conditional_directive_rx + return v:lnum - 1 == 0 ? 0 : indent(v:lnum - 1) - shiftwidth() + else + return v:lnum - 1 == 0 ? 0 : indent(v:lnum - 1) + endif + endif +endfunction diff --git a/runtime/indent/matlab.vim b/runtime/indent/matlab.vim new file mode 100644 index 0000000..10d8460 --- /dev/null +++ b/runtime/indent/matlab.vim @@ -0,0 +1,123 @@ +" Vim indent file +" Language: MATLAB +" Maintainer: Axel Forsman <axelsfor@gmail.com> +" Previous maintainer: Christophe Poucet <christophe.poucet@pandora.be> +" Last Update: 2021-10-01 + +" Only load if no other indent file is loaded +if exists('b:did_indent') | finish | endif +let b:did_indent = 1 + +setlocal indentexpr=GetMatlabIndent() +setlocal indentkeys=!,o,O,e,0=end,0=elseif,0=case,0=otherwise,0=catch,0=function,0=elsei +let b:undo_indent = "setlocal indentexpr< indentkeys<" + +" The value of the Function indenting format in +" MATLAB Editor/Debugger Language Preferences. +" The possible values are 0 for Classic, 1 for Indent nested functions +" and 2 for Indent all functions (default). +let b:MATLAB_function_indent = get(g:, 'MATLAB_function_indent', 2) +" The previous value of b:changedtick +let b:MATLAB_lasttick = -1 +" The previously indented line +let b:MATLAB_lastline = -1 +" Whether the line above was a line continuation +let b:MATLAB_waslc = 0 +let b:MATLAB_bracketlevel = 0 + +" Only define the function once +if exists("*GetMatlabIndent") | finish | endif + +let s:keepcpo = &cpo +set cpo&vim + +let s:end = '\<end\>\%([^({]*[)}]\)\@!' " Array indexing heuristic +let s:open_pat = 'for\|if\|parfor\|spmd\|switch\|try\|while\|classdef\|properties\|methods\|events\|enumeration' +let s:dedent_pat = '\C^\s*\zs\<\%(end\|else\|elseif\|catch\|\(case\|otherwise\|function\)\)\>' +let s:start_pat = '\C\<\%(function\|' . s:open_pat . '\)\>' +let s:bracket_pair_pat = '\(\[\|{\)\|\(\]\|}\)' +let s:zflag = has('patch-7.4.984') ? 'z' : '' + +" Returns whether a comment or string envelops the specified column. +function! s:IsCommentOrString(lnum, col) + return synIDattr(synID(a:lnum, a:col, 1), "name") =~# 'matlabComment\|matlabMultilineComment\|matlabCellComment\|matlabString' +endfunction + +" Returns whether the specified line continues on the next line. +function! s:IsLineContinuation(lnum) + let l = getline(a:lnum) | let c = -3 + while 1 + let c = match(l, '\.\{3}', c + 3) + if c == -1 | return 0 + elseif !s:IsCommentOrString(a:lnum, c) | return 1 | endif + endwhile +endfunction + +function! s:SubmatchCount(lnum, pattern, ...) + let endcol = a:0 >= 1 ? a:1 : 1 / 0 | let x = [0, 0, 0, 0] + call cursor(a:lnum, 1) + while 1 + let [lnum, c, submatch] = searchpos(a:pattern, 'cpe' . s:zflag, a:lnum) + if !submatch || c >= endcol | break | endif + if !s:IsCommentOrString(lnum, c) | let x[submatch - 2] += 1 | endif + if cursor(0, c + 1) == -1 || col('.') == c | break | endif + endwhile + return x +endfunction + +function! s:GetOpenCloseCount(lnum, pattern, ...) + let counts = call('s:SubmatchCount', [a:lnum, a:pattern] + a:000) + return counts[0] - counts[1] +endfunction + +function! GetMatlabIndent() + let prevlnum = prevnonblank(v:lnum - 1) + + if b:MATLAB_lasttick != b:changedtick || b:MATLAB_lastline != prevlnum + " Recalculate bracket count (only have to check same block and line above) + let b:MATLAB_bracketlevel = 0 + let previndent = indent(prevlnum) | let l = prevlnum + while 1 + let l = prevnonblank(l - 1) | let indent = indent(l) + if l <= 0 || previndent < indent | break | endif + let b:MATLAB_bracketlevel += s:GetOpenCloseCount(l, s:bracket_pair_pat) + if previndent != indent | break | endif + endwhile + + let b:MATLAB_waslc = s:IsLineContinuation(prevlnum - 1) + endif + " If line above was blank it can impossibly have been a LC + let above_lc = b:MATLAB_lasttick == b:changedtick && prevlnum != v:lnum - 1 && b:MATLAB_lastline == prevlnum ? 0 : s:IsLineContinuation(v:lnum - 1) + + let pair_pat = '\C\<\(' . s:open_pat . '\|' + \ . (b:MATLAB_function_indent == 1 ? '^\@<!' : '') + \ . (b:MATLAB_function_indent >= 1 ? 'function\|' : '') + \ . '\|\%(^\s*\)\@<=\%(else\|elseif\|case\|otherwise\|catch\)\)\>' + \ . '\|\S\s*\zs\(' . s:end . '\)' + let [open, close, b_open, b_close] = prevlnum ? s:SubmatchCount(prevlnum, + \ pair_pat . '\|' . s:bracket_pair_pat) : [0, 0, 0, 0] + let curbracketlevel = b:MATLAB_bracketlevel + b_open - b_close + + call cursor(v:lnum, 1) + let submatch = search(s:dedent_pat, 'cp' . s:zflag, v:lnum) + if submatch && !s:IsCommentOrString(v:lnum, col('.')) + " Align end, et cetera with start of block + let [lnum, col] = searchpairpos(s:start_pat, '', '\C' . s:end, 'bW', 's:IsCommentOrString(line("."), col("."))') + let result = lnum ? indent(lnum) + shiftwidth() * (s:GetOpenCloseCount(lnum, pair_pat, col) + submatch == 2) : 0 + else + " Count how many blocks the previous line opens/closes + " Line continuations/brackets indent once per statement + let result = (prevlnum > 0) * indent(prevlnum) + shiftwidth() * (open - close + \ + (b:MATLAB_bracketlevel ? -!curbracketlevel : !!curbracketlevel) + \ + (curbracketlevel <= 0) * (above_lc - b:MATLAB_waslc)) + endif + + let b:MATLAB_waslc = above_lc + let b:MATLAB_bracketlevel = curbracketlevel + let b:MATLAB_lasttick = b:changedtick + let b:MATLAB_lastline = v:lnum + return result +endfunction + +let &cpo = s:keepcpo +unlet s:keepcpo diff --git a/runtime/indent/meson.vim b/runtime/indent/meson.vim new file mode 100644 index 0000000..09131f5 --- /dev/null +++ b/runtime/indent/meson.vim @@ -0,0 +1,183 @@ +" Vim indent file +" Language: Meson +" License: VIM License +" Maintainer: Nirbheek Chauhan <nirbheek.chauhan@gmail.com> +" Liam Beguin <liambeguin@gmail.com> +" Original Authors: David Bustos <bustos@caltech.edu> +" Bram Moolenaar <Bram@vim.org> +" Last Change: 2019 Oct 18 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" Some preliminary settings +setlocal nolisp " Make sure lisp indenting doesn't supersede us +setlocal autoindent " indentexpr isn't much help otherwise + +setlocal indentexpr=GetMesonIndent(v:lnum) +setlocal indentkeys+==elif,=else,=endforeach,=endif,0) + +let b:undo_indent = "setl ai< inde< indk< lisp<" + +" Only define the function once. +if exists("*GetMesonIndent") + finish +endif +let s:keepcpo= &cpo +set cpo&vim + +" Come here when loading the script the first time. + +let s:maxoff = 50 " maximum number of lines to look backwards for () + +function GetMesonIndent(lnum) + echom getline(line(".")) + + " If this line is explicitly joined: If the previous line was also joined, + " line it up with that one, otherwise add two 'shiftwidth' + if getline(a:lnum - 1) =~ '\\$' + if a:lnum > 1 && getline(a:lnum - 2) =~ '\\$' + return indent(a:lnum - 1) + endif + return indent(a:lnum - 1) + (exists("g:mesonindent_continue") ? eval(g:mesonindent_continue) : (shiftwidth() * 2)) + endif + + " If the start of the line is in a string don't change the indent. + if has('syntax_items') + \ && synIDattr(synID(a:lnum, 1, 1), "name") =~ "String$" + return -1 + endif + + " Search backwards for the previous non-empty line. + let plnum = prevnonblank(v:lnum - 1) + + if plnum == 0 + " This is the first non-empty line, use zero indent. + return 0 + endif + + " If the previous line is inside parenthesis, use the indent of the starting + " line. + " Trick: use the non-existing "dummy" variable to break out of the loop when + " going too far back. + call cursor(plnum, 1) + let parlnum = searchpair('(\|{\|\[', '', ')\|}\|\]', 'nbW', + \ "line('.') < " . (plnum - s:maxoff) . " ? dummy :" + \ . " synIDattr(synID(line('.'), col('.'), 1), 'name')" + \ . " =~ '\\(Comment\\|Todo\\|String\\)$'") + if parlnum > 0 + let plindent = indent(parlnum) + let plnumstart = parlnum + else + let plindent = indent(plnum) + let plnumstart = plnum + endif + + + " When inside parenthesis: If at the first line below the parenthesis add + " a 'shiftwidth', otherwise same as previous line. + " i = (a + " + b + " + c) + call cursor(a:lnum, 1) + let p = searchpair('(\|{\|\[', '', ')\|}\|\]', 'bW', + \ "line('.') < " . (a:lnum - s:maxoff) . " ? dummy :" + \ . " synIDattr(synID(line('.'), col('.'), 1), 'name')" + \ . " =~ '\\(Comment\\|Todo\\|String\\)$'") + if p > 0 + if p == plnum + " When the start is inside parenthesis, only indent one 'shiftwidth'. + let pp = searchpair('(\|{\|\[', '', ')\|}\|\]', 'bW', + \ "line('.') < " . (a:lnum - s:maxoff) . " ? dummy :" + \ . " synIDattr(synID(line('.'), col('.'), 1), 'name')" + \ . " =~ '\\(Comment\\|Todo\\|String\\)$'") + if pp > 0 + return indent(plnum) + (exists("g:pyindent_nested_paren") ? eval(g:pyindent_nested_paren) : shiftwidth()) + endif + return indent(plnum) + (exists("g:pyindent_open_paren") ? eval(g:pyindent_open_paren) : shiftwidth()) + endif + if plnumstart == p + return indent(plnum) + endif + return plindent + endif + + + " Get the line and remove a trailing comment. + " Use syntax highlighting attributes when possible. + let pline = getline(plnum) + let pline_len = strlen(pline) + if has('syntax_items') + " If the last character in the line is a comment, do a binary search for + " the start of the comment. synID() is slow, a linear search would take + " too long on a long line. + if synIDattr(synID(plnum, pline_len, 1), "name") =~ "\\(Comment\\|Todo\\)$" + let min = 1 + let max = pline_len + while min < max + let col = (min + max) / 2 + if synIDattr(synID(plnum, col, 1), "name") =~ "\\(Comment\\|Todo\\)$" + let max = col + else + let min = col + 1 + endif + endwhile + let pline = strpart(pline, 0, min - 1) + endif + else + let col = 0 + while col < pline_len + if pline[col] == '#' + let pline = strpart(pline, 0, col) + break + endif + let col = col + 1 + endwhile + endif + + " If the previous line ended the conditional/loop + if getline(plnum) =~ '^\s*\(endif\|endforeach\)\>\s*' + " Maintain indent + return -1 + endif + + " If the previous line ended with a builtin, indent this line + if pline =~ '^\s*\(foreach\|if\|else\|elif\)\>\s*' + return plindent + shiftwidth() + endif + + " If the current line begins with a header keyword, deindent + if getline(a:lnum) =~ '^\s*\(else\|elif\|endif\|endforeach\)' + + " Unless the previous line was a one-liner + if getline(plnumstart) =~ '^\s*\(foreach\|if\)\>\s*' + return plindent + endif + + " Or the user has already dedented + if indent(a:lnum) <= plindent - shiftwidth() + return -1 + endif + + return plindent - shiftwidth() + endif + + " When after a () construct we probably want to go back to the start line. + " a = (b + " + c) + " here + if parlnum > 0 + return plindent + endif + + return -1 + +endfunction + +let &cpo = s:keepcpo +unlet s:keepcpo + +" vim:sw=2 diff --git a/runtime/indent/mf.vim b/runtime/indent/mf.vim new file mode 100644 index 0000000..893323d --- /dev/null +++ b/runtime/indent/mf.vim @@ -0,0 +1,6 @@ +" METAFONT indent file +" Language: METAFONT +" Maintainer: Nicola Vitacolonna <nvitacolonna@gmail.com> +" Latest Revision: 2022 Aug 12 + +runtime! indent/mp.vim diff --git a/runtime/indent/mma.vim b/runtime/indent/mma.vim new file mode 100644 index 0000000..9dbfd74 --- /dev/null +++ b/runtime/indent/mma.vim @@ -0,0 +1,79 @@ +" Vim indent file +" Language: Mathematica +" Maintainer: Steve Layland <layland@wolfram.com> (Invalid email address) +" Doug Kearns <dougkearns@gmail.com> +" Last Change: Sat May 10 18:56:22 CDT 2005 +" 2022 April: b:undo_indent added by Doug Kearns +" Source: http://vim.sourceforge.net/scripts/script.php?script_id=1274 +" http://members.wolfram.com/layland/vim/indent/mma.vim +" +" NOTE: +" Empty .m files will automatically be presumed to be Matlab files +" unless you have the following in your .vimrc: +" +" let filetype_m="mma" +" +" Credits: +" o steve hacked this out of a random indent file in the Vim 6.1 +" distribution that he no longer remembers...sh.vim? Thanks! + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetMmaIndent() +setlocal indentkeys+=0[,0],0(,0) +setlocal nosi "turn off smart indent so we don't over analyze } blocks + +let b:undo_indent = "setl inde< indk< si<" + +if exists("*GetMmaIndent") + finish +endif + +function GetMmaIndent() + + " Hit the start of the file, use zero indent. + if v:lnum == 0 + return 0 + endif + + " Find a non-blank line above the current line. + let lnum = prevnonblank(v:lnum - 1) + + " use indenting as a base + let ind = indent(v:lnum) + let lnum = v:lnum + + " if previous line has an unmatched bracket, or ( indent. + " doesn't do multiple parens/blocks/etc... + + " also, indent only if this line if this line isn't starting a new + " block... TODO - fix this with indentkeys? + if getline(v:lnum-1) =~ '\\\@<!\%(\[[^\]]*\|([^)]*\|{[^}]*\)$' && getline(v:lnum) !~ '\s\+[\[({]' + let ind = ind+shiftwidth() + endif + + " if this line had unmatched closing block, + " indent to the matching opening block + if getline(v:lnum) =~ '[^[]*]\s*$' + " move to the closing bracket + call search(']','bW') + " and find its partner's indent + let ind = indent(searchpair('\[','',']','bWn')) + " same for ( blocks + elseif getline(v:lnum) =~ '[^(]*)$' + call search(')','bW') + let ind = indent(searchpair('(','',')','bWn')) + + " and finally, close { blocks if si ain't already set + elseif getline(v:lnum) =~ '[^{]*}' + call search('}','bW') + let ind = indent(searchpair('{','','}','bWn')) + endif + + return ind +endfunction + diff --git a/runtime/indent/mp.vim b/runtime/indent/mp.vim new file mode 100644 index 0000000..07873e1 --- /dev/null +++ b/runtime/indent/mp.vim @@ -0,0 +1,320 @@ +vim9script + +# MetaPost indent file +# Language: MetaPost +# Maintainer: Nicola Vitacolonna <nvitacolonna@gmail.com> +# Former Maintainers: Eugene Minkovskii <emin@mccme.ru> +# Latest Revision: 2022 Aug 12 + +if exists("b:did_indent") + finish +endif + +b:did_indent = 1 + +setlocal indentexpr=g:MetaPostIndent() +setlocal indentkeys+==end,=else,=fi,=fill,0),0] +setlocal nolisp +setlocal nosmartindent + +b:undo_indent = "setl indentexpr< indentkeys< lisp< smartindent<" + +# Regexps {{{ +# Expressions starting indented blocks +const MP_OPEN_TAG = [ + '\<if\>', + '\<else\%[if]\>', + '\<for\%(\|ever\|suffixes\)\>', + '\<begingroup\>', + '\<\%(\|var\|primary\|secondary\|tertiary\)def\>', + '^\s*\<begin\%(fig\|graph\|glyph\|char\|logochar\)\>', + '[([{]', + ]->extend(get(g:, "mp_open_tag", []))->join('\|') + +# Expressions ending indented blocks +const MP_CLOSE_TAG = [ + '\<fi\>', + '\<else\%[if]\>', + '\<end\%(\|for\|group\|def\|fig\|char\|glyph\|graph\)\>', + '[)\]}]' + ]->extend(get(g:, "mp_close_tag", []))->join('\|') + +# Statements that may span multiple lines and are ended by a semicolon. To +# keep this list short, statements that are unlikely to be very long or are +# not very common (e.g., keywords like `interim` or `showtoken`) are not +# included. +# +# The regex for assignments and equations (the last branch) is tricky, because +# it must not match things like `for i :=`, `if a=b`, `def...=`, etc... It is +# not perfect, but it works reasonably well. +const MP_STATEMENT = [ + '\<\%(\|un\|cut\)draw\%(dot\)\=\>', + '\<\%(\|un\)fill\%[draw]\>', + '\<draw\%(dbl\)\=arrow\>', + '\<clip\>', + '\<addto\>', + '\<save\>', + '\<setbounds\>', + '\<message\>', + '\<errmessage\>', + '\<errhelp\>', + '\<fontmapline\>', + '\<pickup\>', + '\<show\>', + '\<special\>', + '\<write\>', + '\%(^\|;\)\%([^;=]*\%(' .. MP_OPEN_TAG .. '\)\)\@!.\{-}:\==', + ]->join('\|') + +# A line ends with zero or more spaces, possibly followed by a comment. +const EOL = '\s*\%($\|%\)' +# }}} + +# Auxiliary functions {{{ +# Returns true if (0-based) position immediately preceding `pos` in `line` is +# inside a string or a comment; returns false otherwise. + +# This is the function that is called more often when indenting, so it is +# critical that it is efficient. The method we use is significantly faster +# than using syntax attributes, and more general (it does not require +# syntax_items). It is also faster than using a single regex matching an even +# number of quotes. It helps that MetaPost strings cannot span more than one +# line and cannot contain escaped quotes. +def IsCommentOrString(line: string, pos: number): bool + var in_string = 0 + var q = stridx(line, '"') + var c = stridx(line, '%') + + while q >= 0 && q < pos + if c >= 0 && c < q + if in_string # Find next percent symbol + c = stridx(line, '%', q + 1) + else # Inside comment + return true + endif + endif + in_string = 1 - in_string + q = stridx(line, '"', q + 1) # Find next quote + endwhile + + return in_string || (c >= 0 && c <= pos) +enddef + +# Find the first non-comment non-blank line before the given line. +def PrevNonBlankNonComment(lnum: number): number + var nr = prevnonblank(lnum - 1) + while getline(nr) =~# '^\s*%' + nr = prevnonblank(nr - 1) + endwhile + return nr +enddef + +# Returns true if the last tag appearing in the line is an open tag; returns +# false otherwise. +def LastTagIsOpen(line: string): bool + var o = LastValidMatchEnd(line, MP_OPEN_TAG, 0) + if o == - 1 + return false + endif + return LastValidMatchEnd(line, MP_CLOSE_TAG, o) < 0 +enddef + +# A simple, efficient and quite effective heuristics is used to test whether +# a line should cause the next line to be indented: count the "opening tags" +# (if, for, def, ...) in the line, count the "closing tags" (endif, endfor, +# ...) in the line, and compute the difference. We call the result the +# "weight" of the line. If the weight is positive, then the next line should +# most likely be indented. Note that `else` and `elseif` are both opening and +# closing tags, so they "cancel out" in almost all cases, the only exception +# being a leading `else[if]`, which is counted as an opening tag, but not as +# a closing tag (so that, for instance, a line containing a single `else:` +# will have weight equal to one, not zero). We do not treat a trailing +# `else[if]` in any special way, because lines ending with an open tag are +# dealt with separately before this function is called (see MetaPostIndent()). +# +# Example: +# +# forsuffixes $=a,b: if x.$ = y.$ : draw else: fill fi +# % This line will be indented because |{forsuffixes,if,else}| > |{else,fi}| (3 > 2) +# endfor +def Weight(line: string): number + var o = 0 + var i = ValidMatchEnd(line, MP_OPEN_TAG, 0) + while i > 0 + o += 1 + i = ValidMatchEnd(line, MP_OPEN_TAG, i) + endwhile + var c = 0 + i = matchend(line, '^\s*\<else\%[if]\>') # Skip a leading else[if] + i = ValidMatchEnd(line, MP_CLOSE_TAG, i) + while i > 0 + c += 1 + i = ValidMatchEnd(line, MP_CLOSE_TAG, i) + endwhile + return o - c +enddef + +# Similar to matchend(), but skips strings and comments. +# line: a String +def ValidMatchEnd(line: string, pat: string, start: number): number + var i = matchend(line, pat, start) + while i > 0 && IsCommentOrString(line, i) + i = matchend(line, pat, i) + endwhile + return i +enddef + +# Like s:ValidMatchEnd(), but returns the end position of the last (i.e., +# rightmost) match. +def LastValidMatchEnd(line: string, pat: string, start: number): number + var last_found = -1 + var i = matchend(line, pat, start) + while i > 0 + if !IsCommentOrString(line, i) + last_found = i + endif + i = matchend(line, pat, i) + endwhile + return last_found +enddef + +def DecreaseIndentOnClosingTag(curr_indent: number): number + var cur_text = getline(v:lnum) + if cur_text =~# '^\s*\%(' .. MP_CLOSE_TAG .. '\)' + return max([curr_indent - shiftwidth(), 0]) + endif + return curr_indent +enddef +# }}} + +# Main function {{{ +def g:MetaPostIndent(): number + # Do not touch indentation inside verbatimtex/btex.. etex blocks. + if synIDattr(synID(v:lnum, 1, 1), "name") =~# '^mpTeXinsert$\|^tex\|^Delimiter' + return -1 + endif + + # At the start of a MetaPost block inside ConTeXt, do not touch indentation + if synIDattr(synID(prevnonblank(v:lnum - 1), 1, 1), "name") == "contextBlockDelim" + return -1 + endif + + var lnum = PrevNonBlankNonComment(v:lnum) + + # At the start of the file use zero indent. + if lnum == 0 + return 0 + endif + + var prev_text = getline(lnum) + + # Every rule of indentation in MetaPost is very subjective. We might get + # creative, but things get murky very soon (there are too many corner + # cases). So, we provide a means for the user to decide what to do when this + # script doesn't get it. We use a simple idea: use '%>', '%<', '%=', and + # '%!', to explicitly control indentation. The '<' and '>' symbols may be + # repeated many times (e.g., '%>>' will cause the next line to be indented + # twice). + # + # User-defined overrides take precedence over anything else. + var j = match(prev_text, '%[<>=!]') + if j > 0 + var i = strlen(matchstr(prev_text, '%>\+', j)) - 1 + if i > 0 + return indent(lnum) + i * shiftwidth() + endif + + i = strlen(matchstr(prev_text, '%<\+', j)) - 1 + if i > 0 + return max([indent(lnum) - i * shiftwidth(), 0]) + endif + + if match(prev_text, '%=', j) > -1 + return indent(lnum) + endif + + if match(prev_text, '%!', j) > -1 + return -1 + endif + endif + + # If the reference line ends with an open tag, indent. + # + # Example: + # + # if c: + # 0 + # else: + # 1 + # fi if c2: % Note that this line has weight equal to zero. + # ... % This line will be indented + if LastTagIsOpen(prev_text) + return DecreaseIndentOnClosingTag(indent(lnum) + shiftwidth()) + endif + + # Lines with a positive weight are unbalanced and should likely be indented. + # + # Example: + # + # def f = enddef for i = 1 upto 5: if x[i] > 0: 1 else: 2 fi + # ... % This line will be indented (because of the unterminated `for`) + if Weight(prev_text) > 0 + return DecreaseIndentOnClosingTag(indent(lnum) + shiftwidth()) + endif + + # Unterminated statements cause indentation to kick in. + # + # Example: + # + # draw unitsquare + # withcolor black; % This line is indented because of `draw`. + # x := a + b + c + # + d + e; % This line is indented because of `:=`. + # + var i = LastValidMatchEnd(prev_text, MP_STATEMENT, 0) + if i >= 0 # Does the line contain a statement? + if ValidMatchEnd(prev_text, ';', i) < 0 # Is the statement unterminated? + return indent(lnum) + shiftwidth() + else + return DecreaseIndentOnClosingTag(indent(lnum)) + endif + endif + + # Deal with the special case of a statement spanning multiple lines. If the + # current reference line L ends with a semicolon, search backwards for + # another semicolon or a statement keyword. If the latter is found first, + # its line is used as the reference line for indenting the current line + # instead of L. + # + # Example: + # + # if cond: + # draw if a: z0 else: z1 fi + # shifted S + # scaled T; % L + # + # for i = 1 upto 3: % <-- Current line: this gets the same indent as `draw ...` + # + # NOTE: we get here only if L does not contain a statement (among those + # listed in g:MP_STATEMENT). + if ValidMatchEnd(prev_text, ';' .. EOL, 0) >= 0 # L ends with a semicolon + var stm_lnum = PrevNonBlankNonComment(lnum) + while stm_lnum > 0 + prev_text = getline(stm_lnum) + var sc_pos = LastValidMatchEnd(prev_text, ';', 0) + var stm_pos = ValidMatchEnd(prev_text, MP_STATEMENT, sc_pos) + if stm_pos > sc_pos + lnum = stm_lnum + break + elseif sc_pos > stm_pos + break + endif + stm_lnum = PrevNonBlankNonComment(stm_lnum) + endwhile + endif + + return DecreaseIndentOnClosingTag(indent(lnum)) +enddef +# }}} + +# vim: sw=2 fdm=marker diff --git a/runtime/indent/nginx.vim b/runtime/indent/nginx.vim new file mode 100644 index 0000000..6550609 --- /dev/null +++ b/runtime/indent/nginx.vim @@ -0,0 +1,78 @@ +" Vim indent file +" Language: nginx.conf +" Maintainer: Chris Aumann <me@chr4.org> +" Last Change: 2022 Dec 01 + +" Only load this indent file when no other was loaded. +if exists('b:did_indent') + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetNginxIndent() + +setlocal indentkeys=0{,0},0#,!^F,o,O + +let b:undo_indent = 'setl inde< indk<' + +" Only define the function once. +if exists('*GetNginxIndent') + finish +endif + +function GetNginxIndent() abort + let plnum = s:PrevNotAsBlank(v:lnum - 1) + + " Hit the start of the file, use zero indent. + if plnum == 0 + return 0 + endif + + let ind = indent(plnum) + + " Add a 'shiftwidth' after '{' + if s:AsEndWith(getline(plnum), '{') + let ind = ind + shiftwidth() + end + + " Subtract a 'shiftwidth' on '}' + " This is the part that requires 'indentkeys'. + if getline(v:lnum) =~ '^\s*}' + let ind = ind - shiftwidth() + endif + + let pplnum = s:PrevNotAsBlank(plnum - 1) + + if s:IsLineContinuation(plnum) + if !s:IsLineContinuation(pplnum) + let ind = ind + shiftwidth() + end + else + if s:IsLineContinuation(pplnum) + let ind = ind - shiftwidth() + end + endif + + return ind +endfunction + +" Find the first line at or above {lnum} that is non-blank and not a comment. +function s:PrevNotAsBlank(lnum) abort + let lnum = prevnonblank(a:lnum) + while lnum > 0 + if getline(lnum) !~ '^\s*#' + break + endif + let lnum = prevnonblank(lnum - 1) + endwhile + return lnum +endfunction + +" Check whether {line} ends with {pat}, ignoring trailing comments. +function s:AsEndWith(line, pat) abort + return a:line =~ a:pat . '\m\s*\%(#.*\)\?$' +endfunction + +function s:IsLineContinuation(lnum) abort + return a:lnum > 0 && !s:AsEndWith(getline(a:lnum), '[;{}]') +endfunction diff --git a/runtime/indent/nsis.vim b/runtime/indent/nsis.vim new file mode 100644 index 0000000..3731781 --- /dev/null +++ b/runtime/indent/nsis.vim @@ -0,0 +1,93 @@ +" Vim indent file +" Language: NSIS script +" Maintainer: Ken Takata +" URL: https://github.com/k-takata/vim-nsis +" Last Change: 2021-10-18 +" Filenames: *.nsi +" License: VIM License + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal nosmartindent +setlocal noautoindent +setlocal indentexpr=GetNsisIndent(v:lnum) +setlocal indentkeys=!^F,o,O +setlocal indentkeys+==~${Else,=~${EndIf,=~${EndUnless,=~${AndIf,=~${AndUnless,=~${OrIf,=~${OrUnless,=~${Case,=~${Default,=~${EndSelect,=~${EndSwitch,=~${Loop,=~${Next,=~${MementoSectionEnd,=~FunctionEnd,=~SectionEnd,=~SectionGroupEnd,=~PageExEnd,0=~!macroend,0=~!if,0=~!else,0=~!endif + +let b:undo_indent = "setl ai< inde< indk< si<" + +if exists("*GetNsisIndent") + finish +endif + +function! GetNsisIndent(lnum) + " If this line is explicitly joined: If the previous line was also joined, + " line it up with that one, otherwise add two 'shiftwidth' + if getline(a:lnum - 1) =~ '\\$' + if a:lnum > 1 && getline(a:lnum - 2) =~ '\\$' + return indent(a:lnum - 1) + endif + return indent(a:lnum - 1) + shiftwidth() * 2 + endif + + " Grab the current line, stripping comments. + let l:thisl = substitute(getline(a:lnum), '[;#].*$', '', '') + " Check if this line is a conditional preprocessor line. + let l:preproc = l:thisl =~? '^\s*!\%(if\|else\|endif\)' + + " Grab the previous line, stripping comments. + " Skip preprocessor lines and continued lines. + let l:prevlnum = a:lnum + while 1 + let l:prevlnum = prevnonblank(l:prevlnum - 1) + if l:prevlnum == 0 + " top of file + return 0 + endif + let l:prevl = substitute(getline(l:prevlnum), '[;#].*$', '', '') + let l:prevpreproc = l:prevl =~? '^\s*!\%(if\|else\|endif\)' + if l:preproc == l:prevpreproc && getline(l:prevlnum - 1) !~? '\\$' + break + endif + endwhile + let l:previ = indent(l:prevlnum) + let l:ind = l:previ + + if l:preproc + " conditional preprocessor + if l:prevl =~? '^\s*!\%(if\%(\%(macro\)\?n\?def\)\?\|else\)\>' + let l:ind += shiftwidth() + endif + if l:thisl =~? '^\s*!\%(else\|endif\)\?\>' + let l:ind -= shiftwidth() + endif + return l:ind + endif + + if l:prevl =~? '^\s*\%(\${\%(If\|IfNot\|Unless\|ElseIf\|ElseIfNot\|ElseUnless\|Else\|AndIf\|AndIfNot\|AndUnless\|OrIf\|OrIfNot\|OrUnless\|Select\|Case\|Case[2-5]\|CaseElse\|Default\|Switch\|Do\|DoWhile\|DoUntil\|For\|ForEach\|MementoSection\)}\|Function\>\|Section\>\|SectionGroup\|PageEx\>\|!macro\>\)' + " previous line opened a block + let l:ind += shiftwidth() + endif + if l:thisl =~? '^\s*\%(\${\%(ElseIf\|ElseIfNot\|ElseUnless\|Else\|EndIf\|EndUnless\|AndIf\|AndIfNot\|AndUnless\|OrIf\|OrIfNot\|OrUnless\|Loop\|LoopWhile\|LoopUntil\|Next\|MementoSectionEnd\)\>}\?\|FunctionEnd\>\|SectionEnd\>\|SectionGroupEnd\|PageExEnd\>\|!macroend\>\)' + " this line closed a block + let l:ind -= shiftwidth() + elseif l:thisl =~? '^\s*\${\%(Case\|Case[2-5]\|CaseElse\|Default\)\>}\?' + if l:prevl !~? '^\s*\${\%(Select\|Switch\)}' + let l:ind -= shiftwidth() + endif + elseif l:thisl =~? '^\s*\${\%(EndSelect\|EndSwitch\)\>}\?' + " this line closed a block + if l:prevl =~? '^\s*\${\%(Select\|Switch\)}' + let l:ind -= shiftwidth() + else + let l:ind -= shiftwidth() * 2 + endif + endif + + return l:ind +endfunction + +" vim: ts=8 sw=2 sts=2 diff --git a/runtime/indent/objc.vim b/runtime/indent/objc.vim new file mode 100644 index 0000000..1d10705 --- /dev/null +++ b/runtime/indent/objc.vim @@ -0,0 +1,79 @@ +" Vim indent file +" Language: Objective-C +" Maintainer: Kazunobu Kuriyama <kazunobu.kuriyama@nifty.com> +" Last Change: 2022 Apr 06 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 +setlocal cindent + +" Set the function to do the work. +setlocal indentexpr=GetObjCIndent() + +" To make a colon (:) suggest an indentation other than a goto/switch label, +setlocal indentkeys-=: +setlocal indentkeys+=<:> + +let b:undo_indent = "setl cin< inde< indk<" + +" Only define the function once. +if exists("*GetObjCIndent") + finish +endif + +function s:GetWidth(line, regexp) + let end = matchend(a:line, a:regexp) + let width = 0 + let i = 0 + while i < end + if a:line[i] != "\t" + let width = width + 1 + else + let width = width + &ts - (width % &ts) + endif + let i = i + 1 + endwhile + return width +endfunction + +function s:LeadingWhiteSpace(line) + let end = strlen(a:line) + let width = 0 + let i = 0 + while i < end + let char = a:line[i] + if char != " " && char != "\t" + break + endif + if char != "\t" + let width = width + 1 + else + let width = width + &ts - (width % &ts) + endif + let i = i + 1 + endwhile + return width +endfunction + + +function GetObjCIndent() + let theIndent = cindent(v:lnum) + + let prev_line = getline(v:lnum - 1) + let cur_line = getline(v:lnum) + + if prev_line !~# ":" || cur_line !~# ":" + return theIndent + endif + + if prev_line !~# ";" + let prev_colon_pos = s:GetWidth(prev_line, ":") + let delta = s:GetWidth(cur_line, ":") - s:LeadingWhiteSpace(cur_line) + let theIndent = prev_colon_pos - delta + endif + + return theIndent +endfunction diff --git a/runtime/indent/obse.vim b/runtime/indent/obse.vim new file mode 100644 index 0000000..6603723 --- /dev/null +++ b/runtime/indent/obse.vim @@ -0,0 +1,55 @@ +" Vim indent file +" Language: Oblivion Language (obl) +" Original Creator: Kat <katisntgood@gmail.com> +" Maintainer: Kat <katisntgood@gmail.com> +" Created: 01 November 2021 +" Last Change: 13 November 2022 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 +let b:undo_indent = 'setlocal indentkeys< indentexpr<' + +setlocal indentexpr=GetOblIndent() +setlocal indentkeys+==~endif,=~else,=~loop,=~end + +if exists("*GetOblIndent") + finish +endif +let s:keepcpo = &cpo +set cpo&vim + +let s:SKIP_LINES = '^\s*\(;.*\)' +function! GetOblIndent() + + let lnum = prevnonblank(v:lnum - 1) + let cur_text = getline(v:lnum) + if lnum == 0 + return 0 + endif + let prev_text = getline(lnum) + let found_cont = 0 + let ind = indent(lnum) + + " indent next line on start terms + let i = match(prev_text, '\c^\s*\(\s\+\)\?\(\(if\|while\|foreach\|begin\|else\%[if]\)\>\)') + if i >= 0 + let ind += shiftwidth() + if strpart(prev_text, i, 1) == '|' && has('syntax_items') + \ && synIDattr(synID(lnum, i, 1), "name") =~ '\(Comment\|String\)$' + let ind -= shiftwidth() + endif + endif + " indent current line on end/else terms + if cur_text =~ '\c^\s*\(\s\+\)\?\(\(loop\|endif\|else\%[if]\)\>\)' + let ind = ind - shiftwidth() + " if we are at a begin block just go to column 0 + elseif cur_text =~ '\c^\s*\(\s\+\)\?\(\(begin\|end\)\>\)' + let ind = 0 + endif + return ind +endfunction + +let &cpo = s:keepcpo +unlet s:keepcpo diff --git a/runtime/indent/ocaml.vim b/runtime/indent/ocaml.vim new file mode 100644 index 0000000..c9beb8b --- /dev/null +++ b/runtime/indent/ocaml.vim @@ -0,0 +1,277 @@ +" Vim indent file +" Language: OCaml +" Maintainers: Jean-Francois Yuen <jfyuen@happycoders.org> +" Mike Leary <leary@nwlink.com> +" Markus Mottl <markus.mottl@gmail.com> +" URL: https://github.com/ocaml/vim-ocaml +" Last Change: 2023 Aug 28 - Add undo_indent (Vim Project) +" 2017 Jun 13 +" 2005 Jun 25 - Fixed multiple bugs due to 'else\nreturn ind' working +" 2005 May 09 - Added an option to not indent OCaml-indents specially (MM) +" 2013 June - commented textwidth (Marc Weber) +" +" Marc Weber's comment: This file may contain a lot of (very custom) stuff +" which eventually should be moved somewhere else .. + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal expandtab +setlocal indentexpr=GetOCamlIndent() +setlocal indentkeys+=0=and,0=class,0=constraint,0=done,0=else,0=end,0=exception,0=external,0=if,0=in,0=include,0=inherit,0=initializer,0=let,0=method,0=open,0=then,0=type,0=val,0=with,0;;,0>\],0\|\],0>},0\|,0},0\],0) +setlocal nolisp +setlocal nosmartindent + +let b:undo_indent = "setl et< inde< indk< lisp< si<" + +" At least Marc Weber and Markus Mottl do not like this: +" setlocal textwidth=80 + +" Comment formatting +if !exists("no_ocaml_comments") + if (has("comments")) + setlocal comments=sr:(*\ ,mb:\ ,ex:*) + setlocal comments^=sr:(**,mb:\ \ ,ex:*) + setlocal fo=cqort + endif +endif + +" Only define the function once. +if exists("*GetOCamlIndent") + finish +endif + +" Define some patterns: +let s:beflet = '^\s*\(initializer\|method\|try\)\|\(\<\(begin\|do\|else\|in\|then\|try\)\|->\|<-\|=\|;\|(\)\s*$' +let s:letpat = '^\s*\(let\|type\|module\|class\|open\|exception\|val\|include\|external\)\>' +let s:letlim = '\(\<\(sig\|struct\)\|;;\)\s*$' +let s:lim = '^\s*\(exception\|external\|include\|let\|module\|open\|type\|val\)\>' +let s:module = '\<\%(begin\|sig\|struct\|object\)\>' +let s:obj = '^\s*\(constraint\|inherit\|initializer\|method\|val\)\>\|\<\(object\|object\s*(.*)\)\s*$' +let s:type = '^\s*\%(class\|let\|type\)\>.*=' + +" Skipping pattern, for comments +function! s:GetLineWithoutFullComment(lnum) + let lnum = prevnonblank(a:lnum - 1) + let lline = substitute(getline(lnum), '(\*.*\*)\s*$', '', '') + while lline =~ '^\s*$' && lnum > 0 + let lnum = prevnonblank(lnum - 1) + let lline = substitute(getline(lnum), '(\*.*\*)\s*$', '', '') + endwhile + return lnum +endfunction + +" Indent for ';;' to match multiple 'let' +function! s:GetInd(lnum, pat, lim) + let llet = search(a:pat, 'bW') + let old = indent(a:lnum) + while llet > 0 + let old = indent(llet) + let nb = s:GetLineWithoutFullComment(llet) + if getline(nb) =~ a:lim + return old + endif + let llet = search(a:pat, 'bW') + endwhile + return old +endfunction + +" Indent pairs +function! s:FindPair(pstart, pmid, pend) + call search(a:pend, 'bW') + return indent(searchpair(a:pstart, a:pmid, a:pend, 'bWn', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\\|comment"')) +endfunction + +" Indent 'let' +function! s:FindLet(pstart, pmid, pend) + call search(a:pend, 'bW') + return indent(searchpair(a:pstart, a:pmid, a:pend, 'bWn', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\\|comment" || getline(".") =~ "^\\s*let\\>.*=.*\\<in\\s*$" || getline(prevnonblank(".") - 1) =~ s:beflet')) +endfunction + +function! GetOCamlIndent() + " Find a non-commented line above the current line. + let lnum = s:GetLineWithoutFullComment(v:lnum) + + " At the start of the file use zero indent. + if lnum == 0 + return 0 + endif + + let ind = indent(lnum) + let lline = substitute(getline(lnum), '(\*.*\*)\s*$', '', '') + + " Return double 'shiftwidth' after lines matching: + if lline =~ '^\s*|.*->\s*$' + return ind + 2 * shiftwidth() + endif + + let line = getline(v:lnum) + + " Indent if current line begins with 'end': + if line =~ '^\s*end\>' + return s:FindPair(s:module, '','\<end\>') + + " Indent if current line begins with 'done' for 'do': + elseif line =~ '^\s*done\>' + return s:FindPair('\<do\>', '','\<done\>') + + " Indent if current line begins with '}' or '>}': + elseif line =~ '^\s*\(\|>\)}' + return s:FindPair('{', '','}') + + " Indent if current line begins with ']', '|]' or '>]': + elseif line =~ '^\s*\(\||\|>\)\]' + return s:FindPair('\[', '','\]') + + " Indent if current line begins with ')': + elseif line =~ '^\s*)' + return s:FindPair('(', '',')') + + " Indent if current line begins with 'let': + elseif line =~ '^\s*let\>' + if lline !~ s:lim . '\|' . s:letlim . '\|' . s:beflet + return s:FindLet(s:type, '','\<let\s*$') + endif + + " Indent if current line begins with 'class' or 'type': + elseif line =~ '^\s*\(class\|type\)\>' + if lline !~ s:lim . '\|\<and\s*$\|' . s:letlim + return s:FindLet(s:type, '','\<\(class\|type\)\s*$') + endif + + " Indent for pattern matching: + elseif line =~ '^\s*|' + if lline !~ '^\s*\(|[^\]]\|\(match\|type\|with\)\>\)\|\<\(function\|parser\|private\|with\)\s*$' + call search('|', 'bW') + return indent(searchpair('^\s*\(match\|type\)\>\|\<\(function\|parser\|private\|with\)\s*$', '', '^\s*|', 'bWn', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\\|comment" || getline(".") !~ "^\\s*|.*->"')) + endif + + " Indent if current line begins with ';;': + elseif line =~ '^\s*;;' + if lline !~ ';;\s*$' + return s:GetInd(v:lnum, s:letpat, s:letlim) + endif + + " Indent if current line begins with 'in': + elseif line =~ '^\s*in\>' + if lline !~ '^\s*\(let\|and\)\>' + return s:FindPair('\<let\>', '', '\<in\>') + endif + + " Indent if current line begins with 'else': + elseif line =~ '^\s*else\>' + if lline !~ '^\s*\(if\|then\)\>' + return s:FindPair('\<if\>', '', '\<else\>') + endif + + " Indent if current line begins with 'then': + elseif line =~ '^\s*then\>' + if lline !~ '^\s*\(if\|else\)\>' + return s:FindPair('\<if\>', '', '\<then\>') + endif + + " Indent if current line begins with 'and': + elseif line =~ '^\s*and\>' + if lline !~ '^\s*\(and\|let\|type\)\>\|\<end\s*$' + return ind - shiftwidth() + endif + + " Indent if current line begins with 'with': + elseif line =~ '^\s*with\>' + if lline !~ '^\s*\(match\|try\)\>' + return s:FindPair('\<\%(match\|try\)\>', '','\<with\>') + endif + + " Indent if current line begins with 'exception', 'external', 'include' or + " 'open': + elseif line =~ '^\s*\(exception\|external\|include\|open\)\>' + if lline !~ s:lim . '\|' . s:letlim + call search(line) + return indent(search('^\s*\(\(exception\|external\|include\|open\|type\)\>\|val\>.*:\)', 'bW')) + endif + + " Indent if current line begins with 'val': + elseif line =~ '^\s*val\>' + if lline !~ '^\s*\(exception\|external\|include\|open\)\>\|' . s:obj . '\|' . s:letlim + return indent(search('^\s*\(\(exception\|include\|initializer\|method\|open\|type\|val\)\>\|external\>.*:\)', 'bW')) + endif + + " Indent if current line begins with 'constraint', 'inherit', 'initializer' + " or 'method': + elseif line =~ '^\s*\(constraint\|inherit\|initializer\|method\)\>' + if lline !~ s:obj + return indent(search('\<\(object\|object\s*(.*)\)\s*$', 'bW')) + shiftwidth() + endif + + endif + + " Add a 'shiftwidth' after lines ending with: + if lline =~ '\(:\|=\|->\|<-\|(\|\[\|{\|{<\|\[|\|\[<\|\<\(begin\|do\|else\|fun\|function\|functor\|if\|initializer\|object\|parser\|private\|sig\|struct\|then\|try\)\|\<object\s*(.*)\)\s*$' + let ind = ind + shiftwidth() + + " Back to normal indent after lines ending with ';;': + elseif lline =~ ';;\s*$' && lline !~ '^\s*;;' + let ind = s:GetInd(v:lnum, s:letpat, s:letlim) + + " Back to normal indent after lines ending with 'end': + elseif lline =~ '\<end\s*$' + let ind = s:FindPair(s:module, '','\<end\>') + + " Back to normal indent after lines ending with 'in': + elseif lline =~ '\<in\s*$' && lline !~ '^\s*in\>' + let ind = s:FindPair('\<let\>', '', '\<in\>') + + " Back to normal indent after lines ending with 'done': + elseif lline =~ '\<done\s*$' + let ind = s:FindPair('\<do\>', '','\<done\>') + + " Back to normal indent after lines ending with '}' or '>}': + elseif lline =~ '\(\|>\)}\s*$' + let ind = s:FindPair('{', '','}') + + " Back to normal indent after lines ending with ']', '|]' or '>]': + elseif lline =~ '\(\||\|>\)\]\s*$' + let ind = s:FindPair('\[', '','\]') + + " Back to normal indent after comments: + elseif lline =~ '\*)\s*$' + call search('\*)', 'bW') + let ind = indent(searchpair('(\*', '', '\*)', 'bWn', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string"')) + + " Back to normal indent after lines ending with ')': + elseif lline =~ ')\s*$' + let ind = s:FindPair('(', '',')') + + " If this is a multiline comment then align '*': + elseif lline =~ '^\s*(\*' && line =~ '^\s*\*' + let ind = ind + 1 + + else + " Don't change indentation of this line + " for new lines (indent==0) use indentation of previous line + + " This is for preventing removing indentation of these args: + " let f x = + " let y = x + 1 in + " Printf.printf + " "o" << here + " "oeuth" << don't touch indentation + + let i = indent(v:lnum) + return i == 0 ? ind : i + + endif + + " Subtract a 'shiftwidth' after lines matching 'match ... with parser': + if lline =~ '\<match\>.*\<with\>\s*\<parser\s*$' + let ind = ind - shiftwidth() + endif + + return ind + +endfunction + +" vim:sw=2 diff --git a/runtime/indent/occam.vim b/runtime/indent/occam.vim new file mode 100644 index 0000000..673940a --- /dev/null +++ b/runtime/indent/occam.vim @@ -0,0 +1,190 @@ +" Vim indent file +" Language: occam +" Maintainer: Mario Schweigler <ms44@kent.ac.uk> (Invalid email address) +" Doug Kearns <dougkearns@gmail.com> +" Last Change: 2022 Apr 06 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +"{{{ Settings +" Set the occam indent function +setlocal indentexpr=GetOccamIndent() +" Indent after new line and after initial colon +setlocal indentkeys=o,O,0=: +"}}} + +let b:undo_indent = "setl inde< indk<" + +" Only define the function once +if exists("*GetOccamIndent") + finish +endif +let s:keepcpo= &cpo +set cpo&vim + +"{{{ Indent definitions +" Define carriage return indent +let s:FirstLevelIndent = '^\C\s*\(IF\|ALT\|PRI\s\+ALT\|PAR\|SEQ\|PRI\s\+PAR\|WHILE\|VALOF\|CLAIM\|FORKING\)\>\|\(--.*\)\@<!\(\<PROC\>\|??\|\<CASE\>\s*\(--.*\)\=\_$\)' +let s:FirstLevelNonColonEndIndent = '^\C\s*PROTOCOL\>\|\(--.*\)\@<!\<\(\(CHAN\|DATA\)\s\+TYPE\|FUNCTION\)\>' +let s:SecondLevelIndent = '^\C\s*\(IF\|ALT\|PRI\s\+ALT\)\>\|\(--.*\)\@<!?\s*\<CASE\>\s*\(--.*\)\=\_$' +let s:SecondLevelNonColonEndIndent = '\(--.*\)\@<!\<\(CHAN\|DATA\)\s\+TYPE\>' + +" Define colon indent +let s:ColonIndent = '\(--.*\)\@<!\<PROC\>' +let s:ColonNonColonEndIndent = '^\C\s*PROTOCOL\>\|\(--.*\)\@<!\<\(\(CHAN\|DATA\)\s\+TYPE\|FUNCTION\)\>' + +let s:ColonEnd = '\(--.*\)\@<!:\s*\(--.*\)\=$' +let s:ColonStart = '^\s*:\s*\(--.*\)\=$' + +" Define comment +let s:CommentLine = '^\s*--' +"}}} + +"{{{ function GetOccamIndent() +" Auxiliary function to get the correct indent for a line of occam code +function GetOccamIndent() + + " Ensure magic is on + let save_magic = &magic + setlocal magic + + " Get reference line number + let linenum = prevnonblank(v:lnum - 1) + while linenum > 0 && getline(linenum) =~ s:CommentLine + let linenum = prevnonblank(linenum - 1) + endwhile + + " Get current indent + let curindent = indent(linenum) + + " Get current line + let line = getline(linenum) + + " Get previous line number + let prevlinenum = prevnonblank(linenum - 1) + while prevlinenum > 0 && getline(prevlinenum) =~ s:CommentLine + let prevlinenum = prevnonblank(prevlinenum - 1) + endwhile + + " Get previous line + let prevline = getline(prevlinenum) + + " Colon indent + if getline(v:lnum) =~ s:ColonStart + + let found = 0 + + while found < 1 + + if line =~ s:ColonStart + let found = found - 1 + elseif line =~ s:ColonIndent || (line =~ s:ColonNonColonEndIndent && line !~ s:ColonEnd) + let found = found + 1 + endif + + if found < 1 + let linenum = prevnonblank(linenum - 1) + if linenum > 0 + let line = getline(linenum) + else + let found = 1 + endif + endif + + endwhile + + if linenum > 0 + let curindent = indent(linenum) + else + let colonline = getline(v:lnum) + let tabstr = '' + while strlen(tabstr) < &tabstop + let tabstr = ' ' . tabstr + endwhile + let colonline = substitute(colonline, '\t', tabstr, 'g') + let curindent = match(colonline, ':') + endif + + " Restore magic + if !save_magic|setlocal nomagic|endif + + return curindent + endif + + if getline(v:lnum) =~ '^\s*:' + let colonline = getline(v:lnum) + let tabstr = '' + while strlen(tabstr) < &tabstop + let tabstr = ' ' . tabstr + endwhile + let colonline = substitute(colonline, '\t', tabstr, 'g') + let curindent = match(colonline, ':') + + " Restore magic + if !save_magic|setlocal nomagic|endif + + return curindent + endif + + " Carriage return indenat + if line =~ s:FirstLevelIndent || (line =~ s:FirstLevelNonColonEndIndent && line !~ s:ColonEnd) + \ || (line !~ s:ColonStart && (prevline =~ s:SecondLevelIndent + \ || (prevline =~ s:SecondLevelNonColonEndIndent && prevline !~ s:ColonEnd))) + let curindent = curindent + shiftwidth() + + " Restore magic + if !save_magic|setlocal nomagic|endif + + return curindent + endif + + " Commented line + if getline(prevnonblank(v:lnum - 1)) =~ s:CommentLine + + " Restore magic + if !save_magic|setlocal nomagic|endif + + return indent(prevnonblank(v:lnum - 1)) + endif + + " Look for previous second level IF / ALT / PRI ALT + let found = 0 + + while !found + + if indent(prevlinenum) == curindent - shiftwidth() + let found = 1 + endif + + if !found + let prevlinenum = prevnonblank(prevlinenum - 1) + while prevlinenum > 0 && getline(prevlinenum) =~ s:CommentLine + let prevlinenum = prevnonblank(prevlinenum - 1) + endwhile + if prevlinenum == 0 + let found = 1 + endif + endif + + endwhile + + if prevlinenum > 0 + if getline(prevlinenum) =~ s:SecondLevelIndent + let curindent = curindent + shiftwidth() + endif + endif + + " Restore magic + if !save_magic|setlocal nomagic|endif + + return curindent + +endfunction +"}}} + +let &cpo = s:keepcpo +unlet s:keepcpo diff --git a/runtime/indent/pascal.vim b/runtime/indent/pascal.vim new file mode 100644 index 0000000..b21b725 --- /dev/null +++ b/runtime/indent/pascal.vim @@ -0,0 +1,229 @@ +" Vim indent file +" Language: Pascal +" Maintainer: Neil Carter <n.carter@swansea.ac.uk> +" Created: 2004 Jul 13 +" Last Change: 2021 Sep 22 +" +" For further documentation, see https://psy.swansea.ac.uk/staff/carter/vim/ + + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetPascalIndent(v:lnum) +setlocal indentkeys& +setlocal indentkeys+==end;,==const,==type,==var,==begin,==repeat,==until,==for +setlocal indentkeys+==program,==function,==procedure,==object,==private +setlocal indentkeys+==record,==if,==else,==case + +let b:undo_indent = 'setlocal indentexpr< indentkeys<' + +if exists("*GetPascalIndent") + finish +endif + + +" ________________________________________________________________ +function! s:GetPrevNonCommentLineNum( line_num ) + + " Skip lines starting with a comment + let SKIP_LINES = '^\s*\(\((\*\)\|\(\*\ \)\|\(\*)\)\|{\|}\)' + + let nline = a:line_num + while nline > 0 + let nline = prevnonblank(nline-1) + if getline(nline) !~? SKIP_LINES + break + endif + endwhile + + return nline +endfunction + + +" ________________________________________________________________ +function! s:PurifyCode( line_num ) + " Strip any trailing comments and whitespace + let pureline = 'TODO' + return pureline +endfunction + + +" ________________________________________________________________ +function! GetPascalIndent( line_num ) + + " Line 0 always goes at column 0 + if a:line_num == 0 + return 0 + endif + + let this_codeline = getline( a:line_num ) + + + " SAME INDENT + + " Middle of a three-part comment + if this_codeline =~ '^\s*\*' + return indent( a:line_num - 1) + endif + + + " COLUMN 1 ALWAYS + + " Last line of the program + if this_codeline =~ '^\s*end\.' + return 0 + endif + + " Compiler directives, allowing "(*" and "{" + "if this_codeline =~ '^\s*\({\|(\*\)$\(IFDEF\|IFNDEF\|ELSE\|ENDIF\)' + if this_codeline =~ '^\s*\({\|(\*\)\$' + return 0 + endif + + " section headers + if this_codeline =~ '^\s*\(program\|procedure\|function\|type\)\>' + return 0 + endif + + " Subroutine separators, lines ending with "const" or "var" + if this_codeline =~ '^\s*\((\*\ _\+\ \*)\|\(const\|var\)\)$' + return 0 + endif + + + " OTHERWISE, WE NEED TO LOOK FURTHER BACK... + + let prev_codeline_num = s:GetPrevNonCommentLineNum( a:line_num ) + let prev_codeline = getline( prev_codeline_num ) + let indnt = indent( prev_codeline_num ) + + + " INCREASE INDENT + + " If the PREVIOUS LINE ended in these items, always indent + if prev_codeline =~ '\<\(type\|const\|var\)$' + return indnt + shiftwidth() + endif + + if prev_codeline =~ '\<repeat$' + if this_codeline !~ '^\s*until\>' + return indnt + shiftwidth() + else + return indnt + endif + endif + + if prev_codeline =~ '\<\(begin\|record\)$' + if this_codeline !~ '^\s*end\>' + return indnt + shiftwidth() + else + return indnt + endif + endif + + " If the PREVIOUS LINE ended with these items, indent if not + " followed by "begin" + if prev_codeline =~ '\<\(\|else\|then\|do\)$' || prev_codeline =~ ':$' + if this_codeline !~ '^\s*begin\>' + return indnt + shiftwidth() + else + " If it does start with "begin" then keep the same indent + "return indnt + shiftwidth() + return indnt + endif + endif + + " Inside a parameter list (i.e. a "(" without a ")"). ???? Considers + " only the line before the current one. TODO: Get it working for + " parameter lists longer than two lines. + if prev_codeline =~ '([^)]\+$' + return indnt + shiftwidth() + endif + + + " DECREASE INDENT + + " Lines starting with "else", but not following line ending with + " "end". + if this_codeline =~ '^\s*else\>' && prev_codeline !~ '\<end$' + return indnt - shiftwidth() + endif + + " Lines after a single-statement branch/loop. + " Two lines before ended in "then", "else", or "do" + " Previous line didn't end in "begin" + let prev2_codeline_num = s:GetPrevNonCommentLineNum( prev_codeline_num ) + let prev2_codeline = getline( prev2_codeline_num ) + if prev2_codeline =~ '\<\(then\|else\|do\)$' && prev_codeline !~ '\<begin$' + " If the next code line after a single statement branch/loop + " starts with "end", "except" or "finally", we need an + " additional unindentation. + if this_codeline =~ '^\s*\(end;\|except\|finally\|\)$' + " Note that we don't return from here. + return indnt - 2 * shiftwidth() + endif + return indnt - shiftwidth() + endif + + " Lines starting with "until" or "end". This rule must be overridden + " by the one for "end" after a single-statement branch/loop. In + " other words that rule should come before this one. + if this_codeline =~ '^\s*\(end\|until\)\>' + return indnt - shiftwidth() + endif + + + " MISCELLANEOUS THINGS TO CATCH + + " Most "begin"s will have been handled by now. Any remaining + " "begin"s on their own line should go in column 1. + if this_codeline =~ '^\s*begin$' + return 0 + endif + + +" ________________________________________________________________ +" Object/Borland Pascal/Delphi Extensions +" +" Note that extended-pascal is handled here, unless it is simpler to +" handle them in the standard-pascal section above. + + + " COLUMN 1 ALWAYS + + " section headers at start of line. + if this_codeline =~ '^\s*\(interface\|implementation\|uses\|unit\)\>' + return 0 + endif + + + " INDENT ONCE + + " If the PREVIOUS LINE ended in these items, always indent. + if prev_codeline =~ '^\s*\(unit\|uses\|try\|except\|finally\|private\|protected\|public\|published\)$' + return indnt + shiftwidth() + endif + + " ???? Indent "procedure" and "functions" if they appear within an + " class/object definition. But that means overriding standard-pascal + " rule where these words always go in column 1. + + + " UNINDENT ONCE + + if this_codeline =~ '^\s*\(except\|finally\)$' + return indnt - shiftwidth() + endif + + if this_codeline =~ '^\s*\(private\|protected\|public\|published\)$' + return indnt - shiftwidth() + endif + + + " If nothing changed, return same indent. + return indnt +endfunction + diff --git a/runtime/indent/perl.vim b/runtime/indent/perl.vim new file mode 100644 index 0000000..a97c34d --- /dev/null +++ b/runtime/indent/perl.vim @@ -0,0 +1,184 @@ +" Vim indent file +" Language: Perl +" Maintainer: vim-perl <vim-perl@googlegroups.com> +" Homepage: https://github.com/vim-perl/vim-perl +" Bugs/requests: https://github.com/vim-perl/vim-perl/issues +" License: Vim License (see :help license) +" Last Change: 2022 Jun 14 + +" Suggestions and improvements by : +" Aaron J. Sherman (use syntax for hints) +" Artem Chuprina (play nice with folding) + +" TODO things that are not or not properly indented (yet) : +" - Continued statements +" print "foo", +" "bar"; +" print "foo" +" if bar(); +" - Multiline regular expressions (m//x) +" (The following probably needs modifying the perl syntax file) +" - qw() lists +" - Heredocs with terminators that don't match \I\i* + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" Is syntax highlighting active ? +let b:indent_use_syntax = has("syntax") + +setlocal indentexpr=GetPerlIndent() +setlocal indentkeys+=0=,0),0],0=or,0=and +if !b:indent_use_syntax + setlocal indentkeys+=0=EO +endif + +let b:undo_indent = "setl inde< indk<" + +let s:cpo_save = &cpo +set cpo-=C + +function! GetPerlIndent() + + " Get the line to be indented + let cline = getline(v:lnum) + + " Indent POD markers to column 0 + if cline =~ '^\s*=\L\@!' + return 0 + endif + + " Get current syntax item at the line's first char + let csynid = '' + if b:indent_use_syntax + let csynid = synIDattr(synID(v:lnum,1,0),"name") + endif + + " Don't reindent POD and heredocs + if csynid == "perlPOD" || csynid == "perlHereDoc" || csynid =~ "^pod" + return indent(v:lnum) + endif + + " Indent end-of-heredocs markers to column 0 + if b:indent_use_syntax + " Assumes that an end-of-heredoc marker matches \I\i* to avoid + " confusion with other types of strings + if csynid == "perlStringStartEnd" && cline =~ '^\I\i*$' + return 0 + endif + else + " Without syntax hints, assume that end-of-heredocs markers begin with EO + if cline =~ '^\s*EO' + return 0 + endif + endif + + " Now get the indent of the previous perl line. + + " Find a non-blank line above the current line. + let lnum = prevnonblank(v:lnum - 1) + " Hit the start of the file, use zero indent. + if lnum == 0 + return 0 + endif + let line = getline(lnum) + let ind = indent(lnum) + " Skip heredocs, POD, and comments on 1st column + if b:indent_use_syntax + let skippin = 2 + while skippin + let synid = synIDattr(synID(lnum,1,0),"name") + if (synid == "perlStringStartEnd" && line =~ '^\I\i*$') + \ || (skippin != 2 && synid == "perlPOD") + \ || (skippin != 2 && synid == "perlHereDoc") + \ || synid == "perlComment" + \ || synid =~ "^pod" + let lnum = prevnonblank(lnum - 1) + if lnum == 0 + return 0 + endif + let line = getline(lnum) + let ind = indent(lnum) + let skippin = 1 + else + let skippin = 0 + endif + endwhile + else + if line =~ "^EO" + let lnum = search("<<[\"']\\=EO", "bW") + let line = getline(lnum) + let ind = indent(lnum) + endif + endif + + " Indent blocks enclosed by {}, (), or [] + if b:indent_use_syntax + " Find a real opening brace + " NOTE: Unlike Perl character classes, we do NOT need to escape the + " closing brackets with a backslash. Doing so just puts a backslash + " in the character class and causes sorrow. Instead, put the closing + " bracket as the first character in the class. + let braceclass = '[][(){}]' + let bracepos = match(line, braceclass, matchend(line, '^\s*[])}]')) + while bracepos != -1 + let synid = synIDattr(synID(lnum, bracepos + 1, 0), "name") + " If the brace is highlighted in one of those groups, indent it. + " 'perlHereDoc' is here only to handle the case '&foo(<<EOF)'. + if synid == "" + \ || synid == "perlMatchStartEnd" + \ || synid == "perlHereDoc" + \ || synid == "perlBraces" + \ || synid == "perlStatementIndirObj" + \ || synid == "perlSubDeclaration" + \ || synid =~ "^perlFiledescStatement" + \ || synid =~ '^perl\(Sub\|Block\|Package\)Fold' + let brace = strpart(line, bracepos, 1) + if brace == '(' || brace == '{' || brace == '[' + let ind = ind + shiftwidth() + else + let ind = ind - shiftwidth() + endif + endif + let bracepos = match(line, braceclass, bracepos + 1) + endwhile + let bracepos = matchend(cline, '^\s*[])}]') + if bracepos != -1 + let synid = synIDattr(synID(v:lnum, bracepos, 0), "name") + if synid == "" + \ || synid == "perlMatchStartEnd" + \ || synid == "perlBraces" + \ || synid == "perlStatementIndirObj" + \ || synid =~ '^perl\(Sub\|Block\|Package\)Fold' + let ind = ind - shiftwidth() + endif + endif + else + if line =~ '[{[(]\s*\(#[^])}]*\)\=$' + let ind = ind + shiftwidth() + endif + if cline =~ '^\s*[])}]' + let ind = ind - shiftwidth() + endif + endif + + " Indent lines that begin with 'or' or 'and' + if cline =~ '^\s*\(or\|and\)\>' + if line !~ '^\s*\(or\|and\)\>' + let ind = ind + shiftwidth() + endif + elseif line =~ '^\s*\(or\|and\)\>' + let ind = ind - shiftwidth() + endif + + return ind + +endfunction + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim:ts=8:sts=4:sw=4:expandtab:ft=vim diff --git a/runtime/indent/php.vim b/runtime/indent/php.vim new file mode 100644 index 0000000..377ffd5 --- /dev/null +++ b/runtime/indent/php.vim @@ -0,0 +1,965 @@ +" Vim indent file +" Language: PHP +" Author: John Wellesz <John.wellesz (AT) gmail (DOT) com> +" URL: https://www.2072productions.com/vim/indent/php.vim +" Home: https://github.com/2072/PHP-Indenting-for-VIm +" Last Change: 2023 August 18th +" Version: 1.75 +" +" +" Type :help php-indent for available options +" +" A fully commented version of this file is available on github +" +" +" If you find a bug, please open a ticket on github.com +" ( https://github.com/2072/PHP-Indenting-for-VIm/issues ) with an example of +" code that breaks the algorithm. +" + +" NOTE: This script must be used with PHP syntax ON and with the php syntax +" script by Lutz Eymers (http://www.isp.de/data/php.vim ) or with the +" script by Peter Hodge (https://www.vim.org/scripts/script.php?script_id=1571 ) +" the later is bunbdled by default with Vim 7. +" +" +" In the case you have syntax errors in your script such as HereDoc end +" identifiers not at col 1 you'll have to indent your file 2 times (This +" script will automatically put HereDoc end identifiers at col 1 if +" they are followed by a ';'). +" + +" NOTE: If you are editing files in Unix file format and that (by accident) +" there are '\r' before new lines, this script won't be able to proceed +" correctly and will make many mistakes because it won't be able to match +" '\s*$' correctly. +" So you have to remove those useless characters first with a command like: +" +" :%s /\r$//g +" +" or simply 'let' the option PHP_removeCRwhenUnix to 1 and the script will +" silently remove them when VIM load this script (at each bufread). + + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + + +let g:php_sync_method = 0 + + +if exists("PHP_default_indenting") + let b:PHP_default_indenting = PHP_default_indenting * shiftwidth() +else + let b:PHP_default_indenting = 0 +endif + +if exists("PHP_outdentSLComments") + let b:PHP_outdentSLComments = PHP_outdentSLComments * shiftwidth() +else + let b:PHP_outdentSLComments = 0 +endif + +if exists("PHP_BracesAtCodeLevel") + let b:PHP_BracesAtCodeLevel = PHP_BracesAtCodeLevel +else + let b:PHP_BracesAtCodeLevel = 0 +endif + + +if exists("PHP_autoformatcomment") + let b:PHP_autoformatcomment = PHP_autoformatcomment +else + let b:PHP_autoformatcomment = 1 +endif + +if exists("PHP_outdentphpescape") + let b:PHP_outdentphpescape = PHP_outdentphpescape +else + let b:PHP_outdentphpescape = 1 +endif + +if exists("PHP_noArrowMatching") + let b:PHP_noArrowMatching = PHP_noArrowMatching +else + let b:PHP_noArrowMatching = 0 +endif + + +if exists("PHP_vintage_case_default_indent") && PHP_vintage_case_default_indent + let b:PHP_vintage_case_default_indent = 1 +else + let b:PHP_vintage_case_default_indent = 0 +endif + +if exists("PHP_IndentFunctionCallParameters") + let b:PHP_IndentFunctionCallParameters = PHP_IndentFunctionCallParameters +else + let b:PHP_IndentFunctionCallParameters = 0 +endif + +if exists("PHP_IndentFunctionDeclarationParameters") + let b:PHP_IndentFunctionDeclarationParameters = PHP_IndentFunctionDeclarationParameters +else + let b:PHP_IndentFunctionDeclarationParameters = 0 +endif + +let b:PHP_lastindented = 0 +let b:PHP_indentbeforelast = 0 +let b:PHP_indentinghuge = 0 +let b:PHP_CurrentIndentLevel = b:PHP_default_indenting +let b:PHP_LastIndentedWasComment = 0 +let b:PHP_InsideMultilineComment = 0 +let b:InPHPcode = 0 +let b:InPHPcode_checked = 0 +let b:InPHPcode_and_script = 0 +let b:InPHPcode_tofind = "" +let b:PHP_oldchangetick = b:changedtick +let b:UserIsTypingComment = 0 +let b:optionsset = 0 + +setlocal nosmartindent +setlocal noautoindent +setlocal nocindent +setlocal nolisp + +setlocal indentexpr=GetPhpIndent() +setlocal indentkeys=0{,0},0),0],:,!^F,o,O,e,*<Return>,=?>,=<?,=*/ + +let b:undo_indent = "setl ai< cin< inde< indk< lisp< si<" + +let s:searchpairflags = 'bWr' + +if &fileformat == "unix" && exists("PHP_removeCRwhenUnix") && PHP_removeCRwhenUnix + silent! %s/\r$//g +endif + +if exists("*GetPhpIndent") + call ResetPhpOptions() + finish " XXX -- comment this line for easy dev +endif + + +let s:endline = '\s*\%(//.*\|#\[\@!.*\|/\*.*\*/\s*\)\=$' +let s:PHP_validVariable = '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*' +let s:notPhpHereDoc = '\<\%(break\|return\|continue\|exit\|die\|true\|false\|elseif\|else\|end\%(if\|while\|for\|foreach\|match\|switch\)\)\>' +let s:blockstart = '\%(\%(\%(}\s*\)\=else\%(\s\+\)\=\)\=if\>\|\%(}\s*\)\?else\>\|do\>\|while\>\|match\>\|switch\>\|case\>\|default\>\|for\%(each\)\=\>\|declare\>\|class\>\|trait\>\|\%()\s*\)\=use\>\|interface\>\|abstract\>\|final\>\|try\>\|\%(}\s*\)\=catch\>\|\%(}\s*\)\=finally\>\)' +let s:functionDeclPrefix = '\<function\>\%(\s\+&\='.s:PHP_validVariable.'\)\=\s*(' +let s:functionDecl = s:functionDeclPrefix.'.*' +let s:multilineFunctionDecl = s:functionDeclPrefix.s:endline +let s:arrayDecl = '\<array\>\s*(.*' +let s:multilineFunctionCall = s:PHP_validVariable.'\s*('.s:endline +let s:unstated = '\%(^\s*'.s:blockstart.'.*)\|\%(//.*\)\@<!\<e'.'lse\>\)'.s:endline + + +let s:terminated = '\%(\%(;\%(\s*\%(?>\|}\)\)\=\|<<<\s*[''"]\=\a\w*[''"]\=$\|^\s*}\|^\s*'.s:PHP_validVariable.':\)'.s:endline.'\)' +let s:PHP_startindenttag = '<?\%(.*?>\)\@!\|<script[^>]*>\%(.*<\/script>\)\@!' +let s:matchStart = 'match\s*(\s*\$\?'.s:PHP_validVariable.'\s*)\s*{'. s:endline +let s:structureHead = '^\s*\%(' . s:blockstart . '\)\|'. s:functionDecl . s:endline . '\|\<new\s\+class\>\|' . s:matchStart + + +let s:escapeDebugStops = 0 +function! DebugPrintReturn(scriptLine) + + if ! s:escapeDebugStops + echo "debug:" . a:scriptLine + let c = getchar() + if c == "\<Del>" + let s:escapeDebugStops = 1 + end + endif + +endfunction + +function! GetLastRealCodeLNum(startline) " {{{ + + let lnum = a:startline + + if b:GetLastRealCodeLNum_ADD && b:GetLastRealCodeLNum_ADD == lnum + 1 + let lnum = b:GetLastRealCodeLNum_ADD + endif + + while lnum > 1 + let lnum = prevnonblank(lnum) + let lastline = getline(lnum) + + if b:InPHPcode_and_script && lastline =~ '?>\s*$' + let lnum = lnum - 1 + elseif lastline =~ '^\s*?>.*<?\%(php\)\=\s*$' + let lnum = lnum - 1 + elseif lastline =~ '^\s*\%(//\|#\|/\*.*\*/\s*$\)' + let lnum = lnum - 1 + elseif lastline =~ '\*/\s*$' + call cursor(lnum, 1) + if lastline !~ '^\*/' + call search('\*/', 'W') + endif + let lnum = searchpair('/\*', '', '\*/', s:searchpairflags, 'Skippmatch2()') + + let lastline = getline(lnum) + if lastline =~ '^\s*/\*' + let lnum = lnum - 1 + else + break + endif + + + elseif lastline =~? '\%(//\s*\|?>.*\)\@<!<?\%(php\)\=\s*$\|^\s*<script\>' + + while lastline !~ '\(<?.*\)\@<!?>' && lnum > 1 + let lnum = lnum - 1 + let lastline = getline(lnum) + endwhile + if lastline =~ '^\s*?>' + let lnum = lnum - 1 + else + break + endif + + + elseif lastline =~? '^\a\w*;\=$' && lastline !~? s:notPhpHereDoc + let tofind=substitute( lastline, '\(\a\w*\);\=', '<<<\\s*[''"]\\=\1[''"]\\=$', '') + while getline(lnum) !~? tofind && lnum > 1 + let lnum = lnum - 1 + endwhile + elseif lastline =~ '^\s*[''"`][;,]'.s:endline || (lastline =~ '^[^''"`]*[''"`][;,]'.s:endline && IslinePHP(lnum, "") == "SpecStringEntrails") + + let tofind=substitute( lastline, '^.*\([''"`]\)[;,].*$', '^[^\1]\\+[\1]$\\|^[^\1]\\+[=([]\\s*[\1]', '') + let trylnum = lnum + while getline(trylnum) !~? tofind && trylnum > 1 + let trylnum = trylnum - 1 + endwhile + + if trylnum == 1 + break + else + if lastline =~ ';'.s:endline + while getline(trylnum) !~? s:terminated && getline(trylnum) !~? '{'.s:endline && trylnum > 1 + let trylnum = prevnonblank(trylnum - 1) + endwhile + + + if trylnum == 1 + break + end + end + let lnum = trylnum + end + else + break + endif + endwhile + + if lnum==1 && getline(lnum) !~ '<?' + let lnum=0 + endif + + if b:InPHPcode_and_script && 1 > b:InPHPcode + let b:InPHPcode_and_script = 0 + endif + + return lnum +endfunction " }}} + +function! Skippmatch2() + + let line = getline(".") + + if line =~ "\\([\"']\\).*/\\*.*\\1" || line =~ '\%(//\|#\[\@!\).*/\*' + return 1 + else + return 0 + endif +endfun + +function! Skippmatch() " {{{ + let synname = synIDattr(synID(line("."), col("."), 0), "name") + if synname ==? "Delimiter" || synname ==? "phpRegionDelimiter" || synname =~? "^phpParent" || synname ==? "phpArrayParens" || synname =~? '^php\%(Block\|Brace\)' || synname ==? "javaScriptBraces" || synname =~? '^php\%(Doc\)\?Comment' && b:UserIsTypingComment + return 0 + else + return 1 + endif +endfun " }}} + +function! FindOpenBracket(lnum, blockStarter) " {{{ + call cursor(a:lnum, 1) + let line = searchpair('{', '', '}', 'bW', 'Skippmatch()') + + if a:blockStarter == 1 + while line > 1 + let linec = getline(line) + + if linec =~ s:terminated || linec =~ s:structureHead + break + endif + + let line = GetLastRealCodeLNum(line - 1) + endwhile + endif + + return line +endfun " }}} + +let s:blockChars = {'{':1, '[': 1, '(': 1, ')':-1, ']':-1, '}':-1} +let s:blockCharsLUT = {'{':'{', '}':'{', '[':'[', ']':'[', '(':'(', ')':'('} +function! BalanceDirection (str) + + let balance = {'{':0, '[': 0, '(': 0, 'none':0} + let director = 'none' + + for c in split(a:str, '\zs') + if has_key(s:blockChars, c) + let balance[s:blockCharsLUT[c]] += s:blockChars[c] + + if balance[s:blockCharsLUT[c]] + let director = s:blockCharsLUT[c] + endif + endif + endfor + + return balance[director] +endfun + +function! StripEndlineComments (line) + + let cleaned = substitute(a:line,'\v(//|#\[\@!)((([^"'']*(["''])[^"'']*\5)+[^"'']*$)|([^"'']*$))','','') + if cleaned != a:line + endif + return cleaned +endfun + +function! FindArrowIndent (lnum) " {{{ + + let parentArrowPos = -1 + let cursorPos = -1 + let lnum = a:lnum + while lnum > 1 + let last_line = getline(lnum) + if last_line =~ '^\s*->' + let parentArrowPos = indent(a:lnum) + break + else + + if b:PHP_noArrowMatching + break + endif + + let cleanedLnum = StripEndlineComments(last_line) + + if cleanedLnum =~ ')'.s:endline + if BalanceDirection(cleanedLnum) <= 0 + call cursor(lnum, 1) + call searchpos(')'.s:endline, 'cW', lnum) + let openedparent = searchpair('(', '', ')', 'bW', 'Skippmatch()') + let cursorPos = col(".") + if openedparent != lnum + let lnum = openedparent + continue + else + endif + else + let parentArrowPos = -1 + break + end + endif + + if cleanedLnum =~ '->' + call cursor(lnum, cursorPos == -1 ? strwidth(cleanedLnum) : cursorPos) + let parentArrowPos = searchpos('->', 'cWb', lnum)[1] - 1 + + break + else + let parentArrowPos = -1 + break + endif + endif + endwhile + + if parentArrowPos == -1 + let parentArrowPos = indent(lnum) + shiftwidth() + end + + return parentArrowPos +endfun "}}} + +function! FindTheIfOfAnElse (lnum, StopAfterFirstPrevElse) " {{{ + + if getline(a:lnum) =~# '^\s*}\s*else\%(if\)\=\>' + let beforeelse = a:lnum + else + let beforeelse = GetLastRealCodeLNum(a:lnum - 1) + endif + + if !s:level + let s:iftoskip = 0 + endif + + if getline(beforeelse) =~# '^\s*\%(}\s*\)\=else\%(\s*if\)\@!\>' + let s:iftoskip = s:iftoskip + 1 + endif + + if getline(beforeelse) =~ '^\s*}' + let beforeelse = FindOpenBracket(beforeelse, 0) + + if getline(beforeelse) =~ '^\s*{' + let beforeelse = GetLastRealCodeLNum(beforeelse - 1) + endif + endif + + + if !s:iftoskip && a:StopAfterFirstPrevElse && getline(beforeelse) =~# '^\s*\%([}]\s*\)\=else\%(if\)\=\>' + return beforeelse + endif + + if getline(beforeelse) !~# '^\s*if\>' && beforeelse>1 || s:iftoskip && beforeelse>1 + + if s:iftoskip && getline(beforeelse) =~# '^\s*if\>' + let s:iftoskip = s:iftoskip - 1 + endif + + let s:level = s:level + 1 + let beforeelse = FindTheIfOfAnElse(beforeelse, a:StopAfterFirstPrevElse) + endif + + return beforeelse + +endfunction " }}} + +let s:defaultORcase = '^\s*\%(default\|case\).*:' + +function! FindTheSwitchIndent (lnum) " {{{ + + let test = GetLastRealCodeLNum(a:lnum - 1) + + if test <= 1 + return indent(1) - shiftwidth() * b:PHP_vintage_case_default_indent + end + + while getline(test) =~ '^\s*}' && test > 1 + let test = GetLastRealCodeLNum(FindOpenBracket(test, 0) - 1) + + if getline(test) =~ '^\s*switch\>' + let test = GetLastRealCodeLNum(test - 1) + endif + endwhile + + if getline(test) =~# '^\s*switch\>' + return indent(test) + elseif getline(test) =~# s:defaultORcase + return indent(test) - shiftwidth() * b:PHP_vintage_case_default_indent + else + return FindTheSwitchIndent(test) + endif + +endfunction "}}} + +let s:SynPHPMatchGroups = {'phpparent':1, 'delimiter':1, 'define':1, 'storageclass':1, 'structure':1, 'exception':1} +function! IslinePHP (lnum, tofind) " {{{ + let cline = getline(a:lnum) + + if a:tofind=="" + let tofind = "^\\s*[\"'`]*\\s*\\zs\\S" + else + let tofind = a:tofind + endif + + let tofind = tofind . '\c' + + let coltotest = match (cline, tofind) + 1 + + let synname = synIDattr(synID(a:lnum, coltotest, 0), "name") + + if synname ==? 'phpStringSingle' || synname ==? 'phpStringDouble' || synname ==? 'phpBacktick' + if cline !~ '^\s*[''"`]' " ??? XXX + return "SpecStringEntrails" + else + return synname + end + end + + if get(s:SynPHPMatchGroups, tolower(synname)) || synname =~ '^php' || synname =~? '^javaScript' + return synname + else + return "" + endif +endfunction " }}} + +let s:autoresetoptions = 0 +if ! s:autoresetoptions + let s:autoresetoptions = 1 +endif + +function! ResetPhpOptions() + if ! b:optionsset && &filetype =~ "php" + if b:PHP_autoformatcomment + + setlocal comments=s1:/*,mb:*,ex:*/,://,f:#[,:# + + setlocal formatoptions-=t + setlocal formatoptions+=q + setlocal formatoptions+=r + setlocal formatoptions+=o + setlocal formatoptions+=c + setlocal formatoptions+=b + endif + let b:optionsset = 1 + endif +endfunc + +call ResetPhpOptions() + +function! GetPhpIndentVersion() + return "1.75" +endfun + +function! GetPhpIndent() + + let b:GetLastRealCodeLNum_ADD = 0 + + let UserIsEditing=0 + if b:PHP_oldchangetick != b:changedtick + let b:PHP_oldchangetick = b:changedtick + let UserIsEditing=1 + endif + + if b:PHP_default_indenting + let b:PHP_default_indenting = g:PHP_default_indenting * shiftwidth() + endif + + let cline = getline(v:lnum) + + if !b:PHP_indentinghuge && b:PHP_lastindented > b:PHP_indentbeforelast + if b:PHP_indentbeforelast + let b:PHP_indentinghuge = 1 + endif + let b:PHP_indentbeforelast = b:PHP_lastindented + endif + + if b:InPHPcode_checked && prevnonblank(v:lnum - 1) != b:PHP_lastindented + if b:PHP_indentinghuge + let b:PHP_indentinghuge = 0 + let b:PHP_CurrentIndentLevel = b:PHP_default_indenting + endif + let real_PHP_lastindented = v:lnum + let b:PHP_LastIndentedWasComment=0 + let b:PHP_InsideMultilineComment=0 + let b:PHP_indentbeforelast = 0 + + let b:InPHPcode = 0 + let b:InPHPcode_checked = 0 + let b:InPHPcode_and_script = 0 + let b:InPHPcode_tofind = "" + + elseif v:lnum > b:PHP_lastindented + let real_PHP_lastindented = b:PHP_lastindented + else + let real_PHP_lastindented = v:lnum + endif + + let b:PHP_lastindented = v:lnum + + + if !b:InPHPcode_checked " {{{ One time check + let b:InPHPcode_checked = 1 + let b:UserIsTypingComment = 0 + + let synname = "" + if cline !~ '<?.*?>' + let synname = IslinePHP (prevnonblank(v:lnum), "") + endif + + if synname!="" + if synname ==? "SpecStringEntrails" + let b:InPHPcode = -1 " thumb down + let b:InPHPcode_tofind = "" + elseif synname !=? "phpHereDoc" && synname !=? "phpHereDocDelimiter" + let b:InPHPcode = 1 + let b:InPHPcode_tofind = "" + + if synname =~? '^php\%(Doc\)\?Comment' + let b:UserIsTypingComment = 1 + let b:InPHPcode_checked = 0 + endif + + if synname =~? '^javaScript' + let b:InPHPcode_and_script = 1 + endif + + else + let b:InPHPcode = 0 + + let lnum = v:lnum - 1 + while getline(lnum) !~? '<<<\s*[''"]\=\a\w*[''"]\=$' && lnum > 1 + let lnum = lnum - 1 + endwhile + + let b:InPHPcode_tofind = substitute( getline(lnum), '^.*<<<\s*[''"]\=\(\a\w*\)[''"]\=$', '^\\s*\1;\\=$', '') + endif + else + let b:InPHPcode = 0 + let b:InPHPcode_tofind = s:PHP_startindenttag + endif + endif "!b:InPHPcode_checked }}} + + + " Test if we are indenting PHP code {{{ + let lnum = prevnonblank(v:lnum - 1) + let last_line = getline(lnum) + let endline= s:endline + + if b:InPHPcode_tofind!="" + if cline =~? b:InPHPcode_tofind + let b:InPHPcode_tofind = "" + let b:UserIsTypingComment = 0 + + if b:InPHPcode == -1 + let b:InPHPcode = 1 + return -1 + end + + let b:InPHPcode = 1 + + if cline =~ '\*/' + call cursor(v:lnum, 1) + if cline !~ '^\*/' + call search('\*/', 'W') + endif + let lnum = searchpair('/\*', '', '\*/', s:searchpairflags, 'Skippmatch2()') + + let b:PHP_CurrentIndentLevel = b:PHP_default_indenting + + let b:PHP_LastIndentedWasComment = 0 + + if cline =~ '^\s*\*/' + return indent(lnum) + 1 + else + return indent(lnum) + endif + + elseif cline =~? '<script\>' + let b:InPHPcode_and_script = 1 + let b:GetLastRealCodeLNum_ADD = v:lnum + endif + endif + endif + + if 1 == b:InPHPcode + + if !b:InPHPcode_and_script && last_line =~ '\%(<?.*\)\@<!?>\%(.*<?\)\@!' && IslinePHP(lnum, '?>')=~?"Delimiter" + if cline !~? s:PHP_startindenttag + let b:InPHPcode = 0 + let b:InPHPcode_tofind = s:PHP_startindenttag + elseif cline =~? '<script\>' + let b:InPHPcode_and_script = 1 + endif + + elseif last_line =~ '^[^''"`]\+[''"`]$' && last_line !~ '^\s*\%(//\|#\[\@!\|/\*.*\*/\s*$\)' " a string identifier with nothing after it and no other string identifier before + let b:InPHPcode = -1 + let b:InPHPcode_tofind = substitute( last_line, '^.*\([''"`]\).*$', '^[^\1]*\1[;,]$', '') + elseif last_line =~? '<<<\s*[''"]\=\a\w*[''"]\=$' + let b:InPHPcode = 0 + let b:InPHPcode_tofind = substitute( last_line, '^.*<<<\s*[''"]\=\(\a\w*\)[''"]\=$', '^\\s*\1;\\=$', '') + + elseif !UserIsEditing && cline =~ '^\s*/\*\%(.*\*/\)\@!' && getline(v:lnum + 1) !~ '^\s*\*' + let b:InPHPcode = 0 + let b:InPHPcode_tofind = '\*/' + + elseif cline =~? '^\s*</script>' + let b:InPHPcode = 0 + let b:InPHPcode_tofind = s:PHP_startindenttag + endif + endif " }}} + + + if 1 > b:InPHPcode && !b:InPHPcode_and_script + return -1 + endif + + " Indent successive // or # comment the same way the first is {{{ + let addSpecial = 0 + if cline =~ '^\s*\%(//\|#\[\@!\|/\*.*\*/\s*$\)' + let addSpecial = b:PHP_outdentSLComments + if b:PHP_LastIndentedWasComment == 1 + return indent(real_PHP_lastindented) + endif + let b:PHP_LastIndentedWasComment = 1 + else + let b:PHP_LastIndentedWasComment = 0 + endif " }}} + + " Indent multiline /* comments correctly {{{ + + if b:PHP_InsideMultilineComment || b:UserIsTypingComment + if cline =~ '^\s*\*\%(\/\)\@!' + if last_line =~ '^\s*/\*' + return indent(lnum) + 1 + else + return indent(lnum) + endif + else + let b:PHP_InsideMultilineComment = 0 + endif + endif + + if !b:PHP_InsideMultilineComment && cline =~ '^\s*/\*\%(.*\*/\)\@!' + if getline(v:lnum + 1) !~ '^\s*\*' + return -1 + endif + let b:PHP_InsideMultilineComment = 1 + endif " }}} + + + " Things always indented at col 1 (PHP delimiter: <?, ?>, Heredoc end) {{{ + if cline =~# '^\s*<?' && cline !~ '?>' && b:PHP_outdentphpescape + return 0 + endif + + if cline =~ '^\s*?>' && cline !~# '<?' && b:PHP_outdentphpescape + return 0 + endif + + if (cline =~? '^\s*\a\w*;$\|^\a\w*$' || (cline =~? '^\s*[''"`][;,]' && IslinePHP(v:lnum, '[;,]') !~? '^\(phpString[SD]\|phpBacktick\)') ) && cline !~? s:notPhpHereDoc + return 0 + endif " }}} + + let s:level = 0 + + let lnum = GetLastRealCodeLNum(v:lnum - 1) + + let last_line = getline(lnum) + let ind = indent(lnum) + + if ind==0 && b:PHP_default_indenting + let ind = b:PHP_default_indenting + endif + + if lnum == 0 + return b:PHP_default_indenting + addSpecial + endif + + + if cline =~ '^\s*}\%(}}\)\@!' + let ind = indent(FindOpenBracket(v:lnum, 1)) + let b:PHP_CurrentIndentLevel = b:PHP_default_indenting + if b:PHP_BracesAtCodeLevel + let ind = ind + shiftwidth() + endif + return ind + endif + + if cline =~ '^\s*\*/' + call cursor(v:lnum, 1) + if cline !~ '^\*/' + call search('\*/', 'W') + endif + let lnum = searchpair('/\*', '', '\*/', s:searchpairflags, 'Skippmatch2()') + + let b:PHP_CurrentIndentLevel = b:PHP_default_indenting + + if cline =~ '^\s*\*/' + return indent(lnum) + 1 + else + return indent(lnum) + endif + endif + + + if last_line =~ '[;}]'.endline && last_line !~ '^[)\]]' && last_line !~# s:defaultORcase && last_line !~ '^\s*[''"`][;,]' + if ind==b:PHP_default_indenting + return b:PHP_default_indenting + addSpecial + elseif b:PHP_indentinghuge && ind==b:PHP_CurrentIndentLevel && cline !~# '^\s*\%(else\|\%(case\|default\).*:\|[})];\=\)' && last_line !~# '^\s*\%(\%(}\s*\)\=else\)\|^\(\s*\S\+\s*\)\+}'.endline && getline(GetLastRealCodeLNum(lnum - 1))=~';'.endline + return b:PHP_CurrentIndentLevel + addSpecial + endif + endif + + let LastLineClosed = 0 + + let terminated = s:terminated + + let unstated = s:unstated + + + if ind != b:PHP_default_indenting && cline =~# '^\s*else\%(if\)\=\>' + let b:PHP_CurrentIndentLevel = b:PHP_default_indenting + return indent(FindTheIfOfAnElse(v:lnum, 1)) + elseif cline =~# s:defaultORcase + return FindTheSwitchIndent(v:lnum) + shiftwidth() * b:PHP_vintage_case_default_indent + elseif cline =~ '^\s*)\=\s*{' + let previous_line = last_line + let last_line_num = lnum + + while last_line_num > 1 + + if previous_line =~ terminated || previous_line =~ s:structureHead + + let ind = indent(last_line_num) + + if b:PHP_BracesAtCodeLevel + let ind = ind + shiftwidth() + endif + + return ind + endif + + let last_line_num = GetLastRealCodeLNum(last_line_num - 1) + let previous_line = getline(last_line_num) + endwhile + elseif cline =~ '^\s*->' + return FindArrowIndent(lnum) + elseif last_line =~# unstated && cline !~ '^\s*);\='.endline + let ind = ind + shiftwidth() " we indent one level further when the preceding line is not stated + return ind + addSpecial + + elseif (ind != b:PHP_default_indenting || last_line =~ '^[)\]]' ) && last_line =~ terminated + let previous_line = last_line + let last_line_num = lnum + let LastLineClosed = 1 + + let isSingleLineBlock = 0 + while 1 + if ! isSingleLineBlock && previous_line =~ '^\s*}\|;\s*}'.endline + + call cursor(last_line_num, 1) + if previous_line !~ '^}' + call search('}\|;\s*}'.endline, 'W') + end + let oldLastLine = last_line_num + let last_line_num = searchpair('{', '', '}', 'bW', 'Skippmatch()') + + if getline(last_line_num) =~ '^\s*{' + let last_line_num = GetLastRealCodeLNum(last_line_num - 1) + elseif oldLastLine == last_line_num + let isSingleLineBlock = 1 + continue + endif + + let previous_line = getline(last_line_num) + + continue + else + let isSingleLineBlock = 0 + + if getline(last_line_num) =~# '^\s*else\%(if\)\=\>' + let last_line_num = FindTheIfOfAnElse(last_line_num, 0) + continue + endif + + + let last_match = last_line_num + + let one_ahead_indent = indent(last_line_num) + let last_line_num = GetLastRealCodeLNum(last_line_num - 1) + let two_ahead_indent = indent(last_line_num) + let after_previous_line = previous_line + let previous_line = getline(last_line_num) + + + if previous_line =~# s:defaultORcase.'\|{'.endline + break + endif + + if after_previous_line=~# '^\s*'.s:blockstart.'.*)'.endline && previous_line =~# '[;}]'.endline + break + endif + + if one_ahead_indent == two_ahead_indent || last_line_num < 1 + if previous_line =~# '\%(;\|^\s*}\)'.endline || last_line_num < 1 + break + endif + endif + endif + endwhile + + if indent(last_match) != ind + let ind = indent(last_match) + let b:PHP_CurrentIndentLevel = b:PHP_default_indenting + + return ind + addSpecial + endif + endif + + if (last_line !~ '^\s*}\%(}}\)\@!') + let plinnum = GetLastRealCodeLNum(lnum - 1) + else + let plinnum = GetLastRealCodeLNum(FindOpenBracket(lnum, 1) - 1) + endif + + let AntepenultimateLine = getline(plinnum) + + let last_line = StripEndlineComments(last_line) + + if ind == b:PHP_default_indenting + if last_line =~ terminated && last_line !~# s:defaultORcase + let LastLineClosed = 1 + endif + endif + + if !LastLineClosed + + let openedparent = -1 + + + if last_line =~# '[{(\[]'.endline || last_line =~? '\h\w*\s*(.*,$' && AntepenultimateLine !~ '[,(\[]'.endline && BalanceDirection(last_line) > 0 + + let dontIndent = 0 + if last_line =~ '\S\+\s*{'.endline && last_line !~ '^\s*[)\]]\+\(\s*:\s*'.s:PHP_validVariable.'\)\=\s*{'.endline && last_line !~ s:structureHead + let dontIndent = 1 + endif + + if !dontIndent && (!b:PHP_BracesAtCodeLevel || last_line !~# '^\s*{') + let ind = ind + shiftwidth() + endif + + if b:PHP_IndentFunctionCallParameters && last_line =~ s:multilineFunctionCall && last_line !~ s:structureHead && last_line !~ s:arrayDecl + let ind = ind + b:PHP_IndentFunctionCallParameters * shiftwidth() + endif + + if b:PHP_IndentFunctionDeclarationParameters && last_line =~ s:multilineFunctionDecl + let ind = ind + b:PHP_IndentFunctionDeclarationParameters * shiftwidth() + endif + + if b:PHP_BracesAtCodeLevel || b:PHP_vintage_case_default_indent == 1 + let b:PHP_CurrentIndentLevel = ind + + endif + + elseif last_line =~ '),'.endline && BalanceDirection(last_line) < 0 + call cursor(lnum, 1) + call searchpos('),'.endline, 'cW') + let openedparent = searchpair('(', '', ')', 'bW', 'Skippmatch()') + if openedparent != lnum + let ind = indent(openedparent) + endif + + elseif last_line =~ s:structureHead + let ind = ind + shiftwidth() + + + elseif AntepenultimateLine =~ '{'.endline && AntepenultimateLine !~? '^\s*use\>' && AntepenultimateLine !~? s:matchStart || AntepenultimateLine =~ terminated || AntepenultimateLine =~# s:defaultORcase + let ind = ind + shiftwidth() + endif + + + if openedparent >= 0 + let last_line = StripEndlineComments(getline(openedparent)) + endif + endif + + if cline =~ '^\s*[)\]];\=' + call cursor(v:lnum, 1) + call searchpos('[)\]]', 'cW') + let matchedBlockChar = cline[col('.')-1] + let openedparent = searchpair('\M'.s:blockCharsLUT[matchedBlockChar], '', '\M'.matchedBlockChar, 'bW', 'Skippmatch()') + if openedparent != v:lnum + let ind = indent(openedparent) + endif + + elseif last_line =~ '^\s*->' && last_line !~? s:structureHead && BalanceDirection(last_line) <= 0 + let ind = ind - shiftwidth() + endif + + let b:PHP_CurrentIndentLevel = ind + return ind + addSpecial +endfunction diff --git a/runtime/indent/postscr.vim b/runtime/indent/postscr.vim new file mode 100644 index 0000000..8430ccf --- /dev/null +++ b/runtime/indent/postscr.vim @@ -0,0 +1,69 @@ +" PostScript indent file +" Language: PostScript +" Maintainer: Mike Williams <mrw@eandem.co.uk> +" Last Change: 2022 Apr 06 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=PostscrIndentGet(v:lnum) +setlocal indentkeys+=0],0=>>,0=%%,0=end,0=restore,0=grestore indentkeys-=:,0#,e + +let b:undo_indent = "setl inde< indk<" + +" Catch multiple instantiations +if exists("*PostscrIndentGet") + finish +endif + +function! PostscrIndentGet(lnum) + " Find a non-empty non-comment line above the current line. + " Note: ignores DSC comments as well! + let lnum = a:lnum - 1 + while lnum != 0 + let lnum = prevnonblank(lnum) + if getline(lnum) !~ '^\s*%.*$' + break + endif + let lnum = lnum - 1 + endwhile + + " Hit the start of the file, use user indent. + if lnum == 0 + return -1 + endif + + " Start with the indent of the previous line + let ind = indent(lnum) + let pline = getline(lnum) + + " Indent for dicts, arrays, and saves with possible trailing comment + if pline =~ '\(begin\|<<\|g\=save\|{\|[\)\s*\(%.*\)\=$' + let ind = ind + shiftwidth() + endif + + " Remove indent for popped dicts, and restores. + if pline =~ '\(end\|g\=restore\)\s*$' + let ind = ind - shiftwidth() + + " Else handle immediate dedents of dicts, restores, and arrays. + elseif getline(a:lnum) =~ '\(end\|>>\|g\=restore\|}\|]\)' + let ind = ind - shiftwidth() + + " Else handle DSC comments - always start of line. + elseif getline(a:lnum) =~ '^\s*%%' + let ind = 0 + endif + + " For now catch excessive left indents if they occur. + if ind < 0 + let ind = -1 + endif + + return ind +endfunction + +" vim:sw=2 diff --git a/runtime/indent/pov.vim b/runtime/indent/pov.vim new file mode 100644 index 0000000..60077ff --- /dev/null +++ b/runtime/indent/pov.vim @@ -0,0 +1,87 @@ +" Vim indent file +" Language: PoV-Ray Scene Description Language +" Maintainer: David Necas (Yeti) <yeti@physics.muni.cz> +" Last Change: 2017 Jun 13 +" 2022 April: b:undo_indent added by Doug Kearns +" URI: http://trific.ath.cx/Ftp/vim/indent/pov.vim + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" Some preliminary settings. +setlocal nolisp " Make sure lisp indenting doesn't supersede us. + +setlocal indentexpr=GetPoVRayIndent() +setlocal indentkeys+==else,=end,0] + +let b:undo_indent = "setl inde< indk< lisp<" + +" Only define the function once. +if exists("*GetPoVRayIndent") + finish +endif + +" Counts matches of a regexp <rexp> in line number <line>. +" Doesn't count matches inside strings and comments (as defined by current +" syntax). +function! s:MatchCount(line, rexp) + let str = getline(a:line) + let i = 0 + let n = 0 + while i >= 0 + let i = matchend(str, a:rexp, i) + if i >= 0 && synIDattr(synID(a:line, i, 0), "name") !~? "string\|comment" + let n = n + 1 + endif + endwhile + return n +endfunction + +" The main function. Returns indent amount. +function GetPoVRayIndent() + " If we are inside a comment (may be nested in obscure ways), give up + if synIDattr(synID(v:lnum, indent(v:lnum)+1, 0), "name") =~? "string\|comment" + return -1 + endif + + " Search backwards for the first non-empty, non-comment line. + let plnum = prevnonblank(v:lnum - 1) + let plind = indent(plnum) + while plnum > 0 && synIDattr(synID(plnum, plind+1, 0), "name") =~? "comment" + let plnum = prevnonblank(plnum - 1) + let plind = indent(plnum) + endwhile + + " Start indenting from zero + if plnum == 0 + return 0 + endif + + " Analyse previous nonempty line. + let chg = 0 + let chg = chg + s:MatchCount(plnum, '[[{(]') + let chg = chg + s:MatchCount(plnum, '#\s*\%(if\|ifdef\|ifndef\|switch\|while\|macro\|else\)\>') + let chg = chg - s:MatchCount(plnum, '#\s*end\>') + let chg = chg - s:MatchCount(plnum, '[]})]') + " Dirty hack for people writing #if and #else on the same line. + let chg = chg - s:MatchCount(plnum, '#\s*\%(if\|ifdef\|ifndef\|switch\)\>.*#\s*else\>') + " When chg > 0, then we opened groups and we should indent more, but when + " chg < 0, we closed groups and this already affected the previous line, + " so we should not dedent. And when everything else fails, scream. + let chg = chg > 0 ? chg : 0 + + " Analyse current line + " FIXME: If we have to dedent, we should try to find the indentation of the + " opening line. + let cur = s:MatchCount(v:lnum, '^\s*\%(#\s*\%(end\|else\)\>\|[]})]\)') + if cur > 0 + let final = plind + (chg - cur) * shiftwidth() + else + let final = plind + chg * shiftwidth() + endif + + return final < 0 ? 0 : final +endfunction diff --git a/runtime/indent/prolog.vim b/runtime/indent/prolog.vim new file mode 100644 index 0000000..0c4fd54 --- /dev/null +++ b/runtime/indent/prolog.vim @@ -0,0 +1,71 @@ +" vim: set sw=4 sts=4: +" Language: Prolog +" Maintainer: Gergely Kontra <kgergely@mcl.hu> (Invalid email address) +" Doug Kearns <dougkearns@gmail.com> +" Revised on: 2002.02.18. 23:34:05 +" Last change by: Takuya Fujiwara, 2018 Sep 23 +" 2022 April: b:undo_indent added by Doug Kearns + +" TODO: +" checking with respect to syntax highlighting +" ignoring multiline comments +" detecting multiline strings + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +let b:did_indent = 1 + +setlocal indentexpr=GetPrologIndent() +setlocal indentkeys-=:,0# +setlocal indentkeys+=0%,-,0;,>,0) + +let b:undo_indent = "setl inde< indk<" + +" Only define the function once. +"if exists("*GetPrologIndent") +" finish +"endif + +function! GetPrologIndent() + " Find a non-blank line above the current line. + let pnum = prevnonblank(v:lnum - 1) + " Hit the start of the file, use zero indent. + if pnum == 0 + return 0 + endif + let line = getline(v:lnum) + let pline = getline(pnum) + + let ind = indent(pnum) + " Previous line was comment -> use previous line's indent + if pline =~ '^\s*%' + return ind + endif + " Previous line was the start of block comment -> +1 after '/*' comment + if pline =~ '^\s*/\*' + return ind + 1 + endif + " Previous line was the end of block comment -> -1 after '*/' comment + if pline =~ '^\s*\*/' + return ind - 1 + endif + " Check for clause head on previous line + if pline =~ '\%(:-\|-->\)\s*\(%.*\)\?$' + let ind = ind + shiftwidth() + " Check for end of clause on previous line + elseif pline =~ '\.\s*\(%.*\)\?$' + let ind = ind - shiftwidth() + endif + " Check for opening conditional on previous line + if pline =~ '^\s*\([(;]\|->\)' + let ind = ind + shiftwidth() + endif + " Check for closing an unclosed paren, or middle ; or -> + if line =~ '^\s*\([);]\|->\)' + let ind = ind - shiftwidth() + endif + return ind +endfunction diff --git a/runtime/indent/ps1.vim b/runtime/indent/ps1.vim new file mode 100644 index 0000000..0f794db --- /dev/null +++ b/runtime/indent/ps1.vim @@ -0,0 +1,17 @@ +" Vim indent file +" Language: Windows PowerShell +" URL: https://github.com/PProvost/vim-ps1 +" Last Change: 2017 Oct 19 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" smartindent is good enough for powershell +setlocal smartindent +" disable the indent removal for # marks +inoremap <buffer> # X# + +let b:undo_indent = "setl si<" diff --git a/runtime/indent/pyrex.vim b/runtime/indent/pyrex.vim new file mode 100644 index 0000000..a1a1746 --- /dev/null +++ b/runtime/indent/pyrex.vim @@ -0,0 +1,13 @@ +" Vim indent file +" Language: Pyrex +" Maintainer: Marco Barisione <marco.bari@people.it> +" URL: http://marcobari.altervista.org/pyrex_vim.html +" Last Change: 2005 Jun 24 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +" Use Python formatting rules +runtime! indent/python.vim diff --git a/runtime/indent/python.vim b/runtime/indent/python.vim new file mode 100644 index 0000000..42ab4f3 --- /dev/null +++ b/runtime/indent/python.vim @@ -0,0 +1,34 @@ +" Vim indent file +" Language: Python +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Last Change: 2023 Aug 10 +" Former Maintainer: Bram Moolenaar <Bram@vim.org> +" Original Author: David Bustos <bustos@caltech.edu> + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" Some preliminary settings +setlocal nolisp " Make sure lisp indenting doesn't supersede us +setlocal autoindent " indentexpr isn't much help otherwise + +setlocal indentexpr=python#GetIndent(v:lnum) +setlocal indentkeys+=<:>,=elif,=except + +let b:undo_indent = "setl ai< inde< indk< lisp<" + +" Only define the function once. +if exists("*GetPythonIndent") + finish +endif + +" Keep this for backward compatibility, new scripts should use +" python#GetIndent() +function GetPythonIndent(lnum) + return python#GetIndent(a:lnum) +endfunction + +" vim:sw=2 diff --git a/runtime/indent/qb64.vim b/runtime/indent/qb64.vim new file mode 100644 index 0000000..09f815c --- /dev/null +++ b/runtime/indent/qb64.vim @@ -0,0 +1,11 @@ +" Vim indent file +" Language: QB64 +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2022 Jan 24 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +runtime! indent/vb.vim diff --git a/runtime/indent/qml.vim b/runtime/indent/qml.vim new file mode 100644 index 0000000..8c9fa91 --- /dev/null +++ b/runtime/indent/qml.vim @@ -0,0 +1,59 @@ +" Vim indent file +" Language: QML +" Maintainer: Chase Knowlden <haroldknowlden@gmail.com> +" Last Change: 2023 Aug 16 +" +" Improved JavaScript indent script. + +" Indent script in place for this already? +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 +let b:undo_indent = "setlocal indentexpr< indentkeys<" + +setlocal indentexpr=s:GetQmlIndent() +setlocal indentkeys=0{,0},0),0],:,!^F,o,O,e,*<Return>,=*/ + +" Only define functions once per session +if exists("*s:GetQmlIndent") + finish +endif + +" Clean up a line of code by removing trailing '//' and '/* */' comments, and trimming +" whitespace +function! s:Trim(line) + return substitute(substitute(substitute(a:line, '// .*', '', ''), '/\* .* \*/', '', ''), '^\s*\|\s*$', '', 'g') +endfunction + +function! s:GetQmlIndent() + let num = v:lnum + let line = s:Trim(getline(num)) + + let pnum = prevnonblank(num - 1) + if pnum == 0 + return 0 + endif + let pline = s:Trim(getline(pnum)) + + let ind = indent(pnum) + + " bracket/brace/paren blocks + if pline =~ '[{[(]$' + let ind += &sw + endif + if line =~ '^[}\])]' + let ind -= &sw + endif + + " '/*' comments + if pline =~ '^/\*.*\*/' + " no indent for single-line form + elseif pline =~ '^/\*' + let ind += 1 + elseif pline =~ '^\*/' + let ind -= 1 + endif + + return ind +endfunction diff --git a/runtime/indent/quarto.vim b/runtime/indent/quarto.vim new file mode 100644 index 0000000..586d232 --- /dev/null +++ b/runtime/indent/quarto.vim @@ -0,0 +1 @@ +runtime indent/rmd.vim diff --git a/runtime/indent/r.vim b/runtime/indent/r.vim new file mode 100644 index 0000000..f7956e4 --- /dev/null +++ b/runtime/indent/r.vim @@ -0,0 +1,521 @@ +" Vim indent file +" Language: R +" Author: Jakson Alves de Aquino <jalvesaq@gmail.com> +" Homepage: https://github.com/jalvesaq/R-Vim-runtime +" Last Change: Sun Oct 08, 2023 10:45AM + + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentkeys=0{,0},:,!^F,o,O,e +setlocal indentexpr=GetRIndent() +setlocal autoindent + +let b:undo_indent = "setl inde< indk<" + +" Only define the function once. +if exists("*GetRIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +" Options to make the indentation more similar to Emacs/ESS: +let g:r_indent_align_args = get(g:, 'r_indent_align_args', 1) +let g:r_indent_ess_comments = get(g:, 'r_indent_ess_comments', 0) +let g:r_indent_comment_column = get(g:, 'r_indent_comment_column', 40) +let g:r_indent_ess_compatible = get(g:, 'r_indent_ess_compatible', 0) +let g:r_indent_op_pattern = get(g:, 'r_indent_op_pattern', + \ '\(&\||\|+\|-\|\*\|/\|=\|\~\|%\|->\||>\)\s*$') + +function s:RDelete_quotes(line) + let i = 0 + let j = 0 + let line1 = "" + let llen = strlen(a:line) + while i < llen + if a:line[i] == '"' + let i += 1 + let line1 = line1 . 's' + while !(a:line[i] == '"' && ((i > 1 && a:line[i-1] == '\' && a:line[i-2] == '\') || a:line[i-1] != '\')) && i < llen + let i += 1 + endwhile + if a:line[i] == '"' + let i += 1 + endif + elseif a:line[i] == "'" + let i += 1 + let line1 = line1 . 's' + while !(a:line[i] == "'" && ((i > 1 && a:line[i-1] == '\' && a:line[i-2] == '\') || a:line[i-1] != '\')) && i < llen + let i += 1 + endwhile + if a:line[i] == "'" + let i += 1 + endif + elseif a:line[i] == "`" + let i += 1 + let line1 = line1 . 's' + while a:line[i] != "`" && i < llen + let i += 1 + endwhile + if a:line[i] == "`" + let i += 1 + endif + endif + if i == llen + break + endif + let line1 = line1 . a:line[i] + let j += 1 + let i += 1 + endwhile + return line1 +endfunction + +" Convert foo(bar()) int foo() +function s:RDelete_parens(line) + if s:Get_paren_balance(a:line, "(", ")") != 0 + return a:line + endif + let i = 0 + let j = 0 + let line1 = "" + let llen = strlen(a:line) + while i < llen + let line1 = line1 . a:line[i] + if a:line[i] == '(' + let nop = 1 + while nop > 0 && i < llen + let i += 1 + if a:line[i] == ')' + let nop -= 1 + elseif a:line[i] == '(' + let nop += 1 + endif + endwhile + let line1 = line1 . a:line[i] + endif + let i += 1 + endwhile + return line1 +endfunction + +function s:Get_paren_balance(line, o, c) + let line2 = substitute(a:line, a:o, "", "g") + let openp = strlen(a:line) - strlen(line2) + let line3 = substitute(line2, a:c, "", "g") + let closep = strlen(line2) - strlen(line3) + return openp - closep +endfunction + +function s:Get_matching_brace(linenr, o, c, delbrace) + let line = SanitizeRLine(getline(a:linenr)) + if a:delbrace == 1 + let line = substitute(line, '{$', "", "") + endif + let pb = s:Get_paren_balance(line, a:o, a:c) + let i = a:linenr + while pb != 0 && i > 1 + let i -= 1 + let pb += s:Get_paren_balance(SanitizeRLine(getline(i)), a:o, a:c) + endwhile + return i +endfunction + +" This function is buggy because there 'if's without 'else' +" It must be rewritten relying more on indentation +function s:Get_matching_if(linenr, delif) + let line = SanitizeRLine(getline(a:linenr)) + if a:delif + let line = substitute(line, "if", "", "g") + endif + let elsenr = 0 + let i = a:linenr + let ifhere = 0 + while i > 0 + let line2 = substitute(line, '\<else\>', "xxx", "g") + let elsenr += strlen(line) - strlen(line2) + if line =~ '.*\s*if\s*()' || line =~ '.*\s*if\s*()' + let elsenr -= 1 + if elsenr == 0 + let ifhere = i + break + endif + endif + let i -= 1 + let line = SanitizeRLine(getline(i)) + endwhile + if ifhere + return ifhere + else + return a:linenr + endif +endfunction + +function s:Get_last_paren_idx(line, o, c, pb) + let blc = a:pb + let line = substitute(a:line, '\t', s:curtabstop, "g") + let theidx = -1 + let llen = strlen(line) + let idx = 0 + while idx < llen + if line[idx] == a:o + let blc -= 1 + if blc == 0 + let theidx = idx + endif + elseif line[idx] == a:c + let blc += 1 + endif + let idx += 1 + endwhile + return theidx + 1 +endfunction + +" Get previous relevant line. Search back until getting a line that isn't +" comment or blank +function s:Get_prev_line(lineno) + let lnum = a:lineno - 1 + let data = getline( lnum ) + while lnum > 0 && (data =~ '^\s*#' || data =~ '^\s*$') + let lnum = lnum - 1 + let data = getline( lnum ) + endwhile + return lnum +endfunction + +" This function is also used by r-plugin/common_global.vim +" Delete from '#' to the end of the line, unless the '#' is inside a string. +function SanitizeRLine(line) + let newline = s:RDelete_quotes(a:line) + let newline = s:RDelete_parens(newline) + let newline = substitute(newline, '#.*', "", "") + let newline = substitute(newline, '\s*$', "", "") + if &filetype == "rhelp" && newline =~ '^\\method{.*}{.*}(.*' + let newline = substitute(newline, '^\\method{\(.*\)}{.*}', '\1', "") + endif + return newline +endfunction + +function GetRIndent() + + let clnum = line(".") " current line + + let cline = getline(clnum) + if cline =~ '^\s*#' + if g:r_indent_ess_comments == 1 + if cline =~ '^\s*###' + return 0 + endif + if cline !~ '^\s*##' + return g:r_indent_comment_column + endif + endif + endif + + let cline = SanitizeRLine(cline) + + if cline =~ '^\s*}' + let indline = s:Get_matching_brace(clnum, '{', '}', 1) + if indline > 0 && indline != clnum + let iline = SanitizeRLine(getline(indline)) + if s:Get_paren_balance(iline, "(", ")") == 0 || iline =~ '(\s*{$' + return indent(indline) + else + let indline = s:Get_matching_brace(indline, '(', ')', 1) + return indent(indline) + endif + endif + endif + + if cline =~ '^\s*)$' + let indline = s:Get_matching_brace(clnum, '(', ')', 1) + return indent(indline) + endif + + " Find the first non blank line above the current line + let lnum = s:Get_prev_line(clnum) + " Hit the start of the file, use zero indent. + if lnum == 0 + return 0 + endif + + let line = SanitizeRLine(getline(lnum)) + + if &filetype == "rhelp" + if cline =~ '^\\dontshow{' || cline =~ '^\\dontrun{' || cline =~ '^\\donttest{' || cline =~ '^\\testonly{' + return 0 + endif + if line =~ '^\\examples{' || line =~ '^\\usage{' || line =~ '^\\dontshow{' || line =~ '^\\dontrun{' || line =~ '^\\donttest{' || line =~ '^\\testonly{' + return 0 + endif + endif + + if &filetype == "rnoweb" && line =~ "^<<.*>>=" + return 0 + endif + + if cline =~ '^\s*{' && s:Get_paren_balance(cline, '{', '}') > 0 + if g:r_indent_ess_compatible && line =~ ')$' + let nlnum = lnum + let nline = line + while s:Get_paren_balance(nline, '(', ')') < 0 + let nlnum = s:Get_prev_line(nlnum) + let nline = SanitizeRLine(getline(nlnum)) . nline + endwhile + if nline =~ '^\s*function\s*(' && indent(nlnum) == shiftwidth() + return 0 + endif + endif + if s:Get_paren_balance(line, "(", ")") == 0 + return indent(lnum) + endif + endif + + " line is an incomplete command: + if line =~ '\<\(if\|while\|for\|function\)\s*()$' || line =~ '\<else$' || line =~ '<-$' || line =~ '->$' + return indent(lnum) + shiftwidth() + endif + + " Deal with () and [] + + let pb = s:Get_paren_balance(line, '(', ')') + + if line =~ '^\s*{$' || line =~ '(\s*{' || (pb == 0 && (line =~ '{$' || line =~ '(\s*{$')) + return indent(lnum) + shiftwidth() + endif + + let s:curtabstop = repeat(' ', &tabstop) + + if g:r_indent_align_args == 1 + if pb > 0 && line =~ '{$' + return s:Get_last_paren_idx(line, '(', ')', pb) + shiftwidth() + endif + + let bb = s:Get_paren_balance(line, '[', ']') + + if pb > 0 + if &filetype == "rhelp" + let ind = s:Get_last_paren_idx(line, '(', ')', pb) + else + let ind = s:Get_last_paren_idx(getline(lnum), '(', ')', pb) + endif + return ind + endif + + if pb < 0 && line =~ '.*[,&|\-\*+<>]$' + if line =~ '.*[\-\*+>]$' + let is_op = v:true + else + let is_op = v:false + endif + let lnum = s:Get_prev_line(lnum) + while pb < 1 && lnum > 0 + let line = SanitizeRLine(getline(lnum)) + let line = substitute(line, '\t', s:curtabstop, "g") + let ind = strlen(line) + while ind > 0 + if line[ind] == ')' + let pb -= 1 + elseif line[ind] == '(' + let pb += 1 + if is_op && pb == 0 + return indent(lnum) + endif + endif + if pb == 1 + return ind + 1 + endif + let ind -= 1 + endwhile + let lnum -= 1 + endwhile + return 0 + endif + + if bb > 0 + let ind = s:Get_last_paren_idx(getline(lnum), '[', ']', bb) + return ind + endif + endif + + let post_block = 0 + if line =~ '}$' && s:Get_paren_balance(line, '{', '}') < 0 + let lnum = s:Get_matching_brace(lnum, '{', '}', 0) + let line = SanitizeRLine(getline(lnum)) + if lnum > 0 && line =~ '^\s*{' + let lnum = s:Get_prev_line(lnum) + let line = SanitizeRLine(getline(lnum)) + endif + let pb = s:Get_paren_balance(line, '(', ')') + let post_block = 1 + endif + + " Indent after operator pattern + let olnum = s:Get_prev_line(lnum) + let oline = getline(olnum) + if olnum > 0 + if substitute(line, '#.*', '', '') =~ g:r_indent_op_pattern && s:Get_paren_balance(line, "(", ")") == 0 + if substitute(oline, '#.*', '', '') =~ g:r_indent_op_pattern && s:Get_paren_balance(line, "(", ")") == 0 + return indent(lnum) + else + return indent(lnum) + shiftwidth() + endif + elseif substitute(oline, '#.*', '', '') =~ g:r_indent_op_pattern && s:Get_paren_balance(line, "(", ")") == 0 + return indent(lnum) - shiftwidth() + endif + elseif substitute(line, '#.*', '', '') =~ g:r_indent_op_pattern && s:Get_paren_balance(line, "(", ")") == 0 + return indent(lnum) + shiftwidth() + endif + + let post_fun = 0 + if pb < 0 && line !~ ')\s*[,&|\-\*+<>]$' + let post_fun = 1 + while pb < 0 && lnum > 0 + let lnum -= 1 + let linepiece = SanitizeRLine(getline(lnum)) + let pb += s:Get_paren_balance(linepiece, "(", ")") + let line = linepiece . line + endwhile + if line =~ '{$' && post_block == 0 + return indent(lnum) + shiftwidth() + endif + + " Now we can do some tests again + if cline =~ '^\s*{' + return indent(lnum) + endif + if post_block == 0 + let newl = SanitizeRLine(line) + if newl =~ '\<\(if\|while\|for\|function\)\s*()$' || newl =~ '\<else$' || newl =~ '<-$' + return indent(lnum) + shiftwidth() + endif + endif + endif + + if cline =~ '^\s*else' + if line =~ '<-\s*if\s*()' + return indent(lnum) + shiftwidth() + elseif line =~ '\<if\s*()' + return indent(lnum) + else + return indent(lnum) - shiftwidth() + endif + endif + + let bb = s:Get_paren_balance(line, '[', ']') + if bb < 0 && line =~ '.*]' + while bb < 0 && lnum > 0 + let lnum -= 1 + let linepiece = SanitizeRLine(getline(lnum)) + let bb += s:Get_paren_balance(linepiece, "[", "]") + let line = linepiece . line + endwhile + let line = s:RDelete_parens(line) + endif + + let plnum = s:Get_prev_line(lnum) + let ppost_else = 0 + if plnum > 0 + let pline = SanitizeRLine(getline(plnum)) + let ppost_block = 0 + if pline =~ '}$' + let ppost_block = 1 + let plnum = s:Get_matching_brace(plnum, '{', '}', 0) + let pline = SanitizeRLine(getline(plnum)) + if pline =~ '^\s*{$' && plnum > 0 + let plnum = s:Get_prev_line(plnum) + let pline = SanitizeRLine(getline(plnum)) + endif + endif + + if pline =~ 'else$' + let ppost_else = 1 + let plnum = s:Get_matching_if(plnum, 0) + let pline = SanitizeRLine(getline(plnum)) + endif + + if pline =~ '^\s*else\s*if\s*(' + let pplnum = s:Get_prev_line(plnum) + let ppline = SanitizeRLine(getline(pplnum)) + while ppline =~ '^\s*else\s*if\s*(' || ppline =~ '^\s*if\s*()\s*\S$' + let plnum = pplnum + let pline = ppline + let pplnum = s:Get_prev_line(plnum) + let ppline = SanitizeRLine(getline(pplnum)) + endwhile + while ppline =~ '\<\(if\|while\|for\|function\)\s*()$' || ppline =~ '\<else$' || ppline =~ '<-$' + let plnum = pplnum + let pline = ppline + let pplnum = s:Get_prev_line(plnum) + let ppline = SanitizeRLine(getline(pplnum)) + endwhile + endif + + let ppb = s:Get_paren_balance(pline, '(', ')') + if ppb < 0 && (pline =~ ')\s*{$' || pline =~ ')$') + while ppb < 0 && plnum > 0 + let plnum -= 1 + let linepiece = SanitizeRLine(getline(plnum)) + let ppb += s:Get_paren_balance(linepiece, "(", ")") + let pline = linepiece . pline + endwhile + let pline = s:RDelete_parens(pline) + endif + endif + + let ind = indent(lnum) + + if g:r_indent_align_args == 0 && pb != 0 + let ind += pb * shiftwidth() + return ind + endif + + if g:r_indent_align_args == 0 && bb != 0 + let ind += bb * shiftwidth() + return ind + endif + + if plnum > 0 + let pind = indent(plnum) + else + let pind = 0 + endif + + if ind == pind || (ind == (pind + shiftwidth()) && pline =~ '{$' && ppost_else == 0) + return ind + endif + + let pline = getline(plnum) + let pbb = s:Get_paren_balance(pline, '[', ']') + + while pind < ind && plnum > 0 && ppb == 0 && pbb == 0 + let ind = pind + let plnum = s:Get_prev_line(plnum) + let pline = getline(plnum) + let ppb = s:Get_paren_balance(pline, '(', ')') + let pbb = s:Get_paren_balance(pline, '[', ']') + while pline =~ '^\s*else' + let plnum = s:Get_matching_if(plnum, 1) + let pline = getline(plnum) + let ppb = s:Get_paren_balance(pline, '(', ')') + let pbb = s:Get_paren_balance(pline, '[', ']') + endwhile + let pind = indent(plnum) + if ind == (pind + shiftwidth()) && pline =~ '{$' + return ind + endif + endwhile + + return ind +endfunction + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim: sw=2 diff --git a/runtime/indent/racket.vim b/runtime/indent/racket.vim new file mode 100644 index 0000000..2d45d89 --- /dev/null +++ b/runtime/indent/racket.vim @@ -0,0 +1,69 @@ +" Vim indent file +" Language: Racket +" Maintainer: D. Ben Knoble <ben.knoble+github@gmail.com> +" Previous Maintainer: Will Langstroth <will@langstroth.com> +" URL: https://github.com/benknoble/vim-racket +" Last Change: 2023 Jul 17 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal lisp autoindent nosmartindent +if has('vim9script') + setlocal indentexpr=racket#Indent() lispoptions+=expr:1 +endif + +setlocal lispwords+=module,module*,module+,parameterize,parameterize*,let-values,let*-values,letrec-values,local +setlocal lispwords+=define/contract +setlocal lispwords+=λ +setlocal lispwords+=with-handlers +setlocal lispwords+=define-values,opt-lambda,case-lambda,syntax-rules,with-syntax,syntax-case,syntax-parse +setlocal lispwords+=define-for-syntax,define-syntax-parser,define-syntax-parse-rule,define-syntax-class,define-splicing-syntax-class +setlocal lispwords+=define-syntax-parameter,syntax-parameterize +setlocal lispwords+=define-signature,unit,unit/sig,compund-unit/sig,define-values/invoke-unit/sig +setlocal lispwords+=define-opt/c,define-syntax-rule +setlocal lispwords+=define-test-suite,test-case +setlocal lispwords+=struct +setlocal lispwords+=with-input-from-file,with-output-to-file +setlocal lispwords+=begin,begin0 +setlocal lispwords+=place +setlocal lispwords+=cond + +" Racket OOP +" TODO missing a lot of define-like forms here (e.g., define/augment, etc.) +setlocal lispwords+=class,class*,mixin,interface,class/derived +setlocal lispwords+=define/public,define/pubment,define/public-final +setlocal lispwords+=define/override,define/overment,define/override-final +setlocal lispwords+=define/augment,define/augride,define/augment-final +setlocal lispwords+=define/private + +" kanren +setlocal lispwords+=fresh,run,run*,project,conde,condu + +" loops +setlocal lispwords+=for,for/list,for/fold,for*,for*/list,for*/fold,for/or,for/and,for*/or,for*/and +setlocal lispwords+=for/hash,for/hasheq,for/hasheqv,for/sum,for/flvector,for*/flvector,for/vector,for*/vector,for*/sum,for*/hash,for*/hasheq,for*/hasheqv +setlocal lispwords+=for/async +setlocal lispwords+=for/set,for*/set +setlocal lispwords+=for/first,for*/first +setlocal lispwords+=for/last,for*/last +setlocal lispwords+=for/stream,for*/stream + +setlocal lispwords+=match,match*,match/values,define/match,match-lambda,match-lambda*,match-lambda** +setlocal lispwords+=match-let,match-let*,match-let-values,match-let*-values +setlocal lispwords+=match-letrec,match-define,match-define-values + +setlocal lispwords+=let/cc,let/ec + +" qi +setlocal lispwords+=define-flow,define-switch,flow-lambda,switch-lambda,on,switch,π,λ01 +setlocal lispwords+=define-qi-syntax,define-qi-syntax-parser,define-qi-syntax-rule + +" gui-easy +setlocal lispwords+=if-view,case-view,cond-view,list-view,dyn-view +setlocal lispwords+=case/dep +setlocal lispwords+=define/obs + +let b:undo_indent = "setlocal indentexpr< lisp< lispoptions< ai< si< lw<" diff --git a/runtime/indent/raku.vim b/runtime/indent/raku.vim new file mode 100644 index 0000000..753a2b0 --- /dev/null +++ b/runtime/indent/raku.vim @@ -0,0 +1,130 @@ +" Vim indent file +" Language: Perl 6 +" Maintainer: vim-perl <vim-perl@googlegroups.com> +" Homepage: https://github.com/vim-perl/vim-perl +" Bugs/requests: https://github.com/vim-perl/vim-perl/issues +" Last Change: 2020 Apr 15 +" 2023 Aug 28 by Vim Project (undo_indent) +" Contributors: Andy Lester <andy@petdance.com> +" Hinrik Örn Sigurðsson <hinrik.sig@gmail.com> +" +" Adapted from indent/perl.vim by Rafael Garcia-Suarez <rgarciasuarez@free.fr> + +" Suggestions and improvements by : +" Aaron J. Sherman (use syntax for hints) +" Artem Chuprina (play nice with folding) +" TODO: +" This file still relies on stuff from the Perl 5 syntax file, which Perl 6 +" does not use. +" +" Things that are not or not properly indented (yet) : +" - Continued statements +" print "foo", +" "bar"; +" print "foo" +" if bar(); +" - Multiline regular expressions (m//x) +" (The following probably needs modifying the perl syntax file) +" - qw() lists +" - Heredocs with terminators that don't match \I\i* + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" Is syntax highlighting active ? +let b:indent_use_syntax = has("syntax") + +setlocal indentexpr=GetRakuIndent() + +" we reset it first because the Perl 5 indent file might have been loaded due +" to a .pl/pm file extension, and indent files don't clean up afterwards +setlocal indentkeys& + +setlocal indentkeys+=0=,0),0],0>,0»,0=or,0=and +if !b:indent_use_syntax + setlocal indentkeys+=0=EO +endif + +let b:undo_indent = "setlocal indentexpr< indentkeys<" + +let s:cpo_save = &cpo +set cpo-=C + +function! GetRakuIndent() + + " Get the line to be indented + let cline = getline(v:lnum) + + " Indent POD markers to column 0 + if cline =~ '^\s*=\L\@!' + return 0 + endif + + " Get current syntax item at the line's first char + let csynid = '' + if b:indent_use_syntax + let csynid = synIDattr(synID(v:lnum,1,0),"name") + endif + + " Don't reindent POD and heredocs + if csynid =~ "^rakuPod" + return indent(v:lnum) + endif + + + " Now get the indent of the previous perl line. + + " Find a non-blank line above the current line. + let lnum = prevnonblank(v:lnum - 1) + " Hit the start of the file, use zero indent. + if lnum == 0 + return 0 + endif + let line = getline(lnum) + let ind = indent(lnum) + " Skip heredocs, POD, and comments on 1st column + if b:indent_use_syntax + let skippin = 2 + while skippin + let synid = synIDattr(synID(lnum,1,0),"name") + if (synid =~ "^rakuPod" || synid =~ "rakuComment") + let lnum = prevnonblank(lnum - 1) + if lnum == 0 + return 0 + endif + let line = getline(lnum) + let ind = indent(lnum) + let skippin = 1 + else + let skippin = 0 + endif + endwhile + endif + + if line =~ '[<«\[{(]\s*\(#[^)}\]»>]*\)\=$' + let ind = ind + &sw + endif + if cline =~ '^\s*[)}\]»>]' + let ind = ind - &sw + endif + + " Indent lines that begin with 'or' or 'and' + if cline =~ '^\s*\(or\|and\)\>' + if line !~ '^\s*\(or\|and\)\>' + let ind = ind + &sw + endif + elseif line =~ '^\s*\(or\|and\)\>' + let ind = ind - &sw + endif + + return ind + +endfunction + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim:ts=8:sts=4:sw=4:expandtab:ft=vim diff --git a/runtime/indent/raml.vim b/runtime/indent/raml.vim new file mode 100644 index 0000000..73756ae --- /dev/null +++ b/runtime/indent/raml.vim @@ -0,0 +1,12 @@ +" Vim indent file +" Language: RAML (RESTful API Modeling Language) +" Maintainer: mucheng <leisurelicht@gmail.com> +" License: VIM LICENSE +" Latest Revision: 2018-11-03 + +if exists("b:did_indent") + finish +endif + +" Same as yaml indenting. +runtime! indent/yaml.vim diff --git a/runtime/indent/rapid.vim b/runtime/indent/rapid.vim new file mode 100644 index 0000000..b1fa00b --- /dev/null +++ b/runtime/indent/rapid.vim @@ -0,0 +1,255 @@ +" ABB Rapid Command indent file for Vim +" Language: ABB Rapid Command +" Maintainer: Patrick Meiser-Knosowski <knosowski@graeffrobotics.de> +" Version: 2.2.7 +" Last Change: 12. May 2023 +" Credits: Based on indent/vim.vim +" +" Suggestions of improvement are very welcome. Please email me! +" +" Known bugs: ../doc/rapid.txt +" +" TODO +" * indent wrapped lines which do not end with an ; or special key word, +" maybe this is a better idea, but then () and [] has to be changed as +" well +" + +if exists("g:rapidNoSpaceIndent") + if !exists("g:rapidSpaceIndent") + let g:rapidSpaceIndent = !g:rapidNoSpaceIndent + endif + unlet g:rapidNoSpaceIndent +endif + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") || get(g:,'rapidNoIndent',0) + finish +endif +let b:did_indent = 1 + +setlocal nolisp +setlocal nosmartindent +setlocal autoindent +setlocal indentexpr=GetRapidIndent() +if get(g:,'rapidNewStyleIndent',0) + setlocal indentkeys=!^F,o,O,0=~endmodule,0=~error,0=~undo,0=~backward,0=~endproc,0=~endrecord,0=~endtrap,0=~endfunc,0=~else,0=~endif,0=~endtest,0=~endfor,0=~endwhile,:,<[>,<]>,<(>,<)> +else + setlocal indentkeys=!^F,o,O,0=~endmodule,0=~error,0=~undo,0=~backward,0=~endproc,0=~endrecord,0=~endtrap,0=~endfunc,0=~else,0=~endif,0=~endtest,0=~endfor,0=~endwhile,: +endif +let b:undo_indent="setlocal lisp< si< ai< inde< indk<" + +if get(g:,'rapidSpaceIndent',1) + " Use spaces for indention, 2 is enough. + " More or even tabs wastes space on the teach pendant. + setlocal softtabstop=2 + setlocal shiftwidth=2 + setlocal expandtab + setlocal shiftround + let b:undo_indent = b:undo_indent." sts< sw< et< sr<" +endif + +" Only define the function once. +if exists("*GetRapidIndent") + finish +endif + +let s:keepcpo= &cpo +set cpo&vim + +function GetRapidIndent() + let ignorecase_save = &ignorecase + try + let &ignorecase = 0 + return s:GetRapidIndentIntern() + finally + let &ignorecase = ignorecase_save + endtry +endfunction + +function s:GetRapidIndentIntern() abort + + let l:currentLineNum = v:lnum + let l:currentLine = getline(l:currentLineNum) + + if l:currentLine =~ '^!' && !get(g:,'rapidCommentIndent',0) + " If current line is ! line comment, do not change indent + " This may be useful if code is commented out at the first column. + return 0 + endif + + " Find a non-blank line above the current line. + let l:preNoneBlankLineNum = s:RapidPreNoneBlank(v:lnum - 1) + if l:preNoneBlankLineNum == 0 + " At the start of the file use zero indent. + return 0 + endif + + let l:preNoneBlankLine = getline(l:preNoneBlankLineNum) + let l:ind = indent(l:preNoneBlankLineNum) + + " Define add a 'shiftwidth' pattern + let l:addShiftwidthPattern = '\c\v^\s*(' + let l:addShiftwidthPattern .= '((local|task)\s+)?(module|record|proc|func|trap)\s+\k' + let l:addShiftwidthPattern .= '|(backward|error|undo)>' + let l:addShiftwidthPattern .= ')' + " + " Define Subtract 'shiftwidth' pattern + let l:subtractShiftwidthPattern = '\c\v^\s*(' + let l:subtractShiftwidthPattern .= 'end(module|record|proc|func|trap)>' + let l:subtractShiftwidthPattern .= '|(backward|error|undo)>' + let l:subtractShiftwidthPattern .= ')' + + " Add shiftwidth + if l:preNoneBlankLine =~ l:addShiftwidthPattern + \|| s:RapidLenTilStr(l:preNoneBlankLineNum, "then", 0)>=0 + \|| s:RapidLenTilStr(l:preNoneBlankLineNum, "else", 0)>=0 + \|| s:RapidLenTilStr(l:preNoneBlankLineNum, "do", 0)>=0 + \|| s:RapidLenTilStr(l:preNoneBlankLineNum, "case", 0)>=0 + \|| s:RapidLenTilStr(l:preNoneBlankLineNum, "default", 0)>=0 + let l:ind += &sw + endif + + " Subtract shiftwidth + if l:currentLine =~ l:subtractShiftwidthPattern + \|| s:RapidLenTilStr(l:currentLineNum, "endif", 0)>=0 + \|| s:RapidLenTilStr(l:currentLineNum, "endfor", 0)>=0 + \|| s:RapidLenTilStr(l:currentLineNum, "endwhile", 0)>=0 + \|| s:RapidLenTilStr(l:currentLineNum, "endtest", 0)>=0 + \|| s:RapidLenTilStr(l:currentLineNum, "else", 0)>=0 + \|| s:RapidLenTilStr(l:currentLineNum, "elseif", 0)>=0 + \|| s:RapidLenTilStr(l:currentLineNum, "case", 0)>=0 + \|| s:RapidLenTilStr(l:currentLineNum, "default", 0)>=0 + let l:ind = l:ind - &sw + endif + + " First case (or default) after a test gets the indent of the test. + if (s:RapidLenTilStr(l:currentLineNum, "case", 0)>=0 || s:RapidLenTilStr(l:currentLineNum, "default", 0)>=0) && s:RapidLenTilStr(l:preNoneBlankLineNum, "test", 0)>=0 + let l:ind += &sw + endif + + " continued lines with () or [] + let l:OpenSum = s:RapidLoneParen(l:preNoneBlankLineNum,"(") + s:RapidLoneParen(l:preNoneBlankLineNum,"[") + if get(g:,'rapidNewStyleIndent',0) + let l:CloseSum = s:RapidLoneParen(l:preNoneBlankLineNum,")") + s:RapidLoneParen(l:currentLineNum,"]") + else + let l:CloseSum = s:RapidLoneParen(l:preNoneBlankLineNum,")") + s:RapidLoneParen(l:preNoneBlankLineNum,"]") + endif + if l:OpenSum > l:CloseSum + let l:ind += (l:OpenSum * 4 * &sw) + elseif l:OpenSum < l:CloseSum + let l:ind -= (l:CloseSum * 4 * &sw) + endif + + return l:ind +endfunction + +" Returns the length of the line until a:str occur outside a string or +" comment. Search starts at string index a:startIdx. +" If a:str is a word also add word boundaries and case insensitivity. +" Note: rapidTodoComment and rapidDebugComment are not taken into account. +function s:RapidLenTilStr(lnum, str, startIdx) abort + + let l:line = getline(a:lnum) + let l:len = strlen(l:line) + let l:idx = a:startIdx + let l:str = a:str + if l:str =~ '^\k\+$' + let l:str = '\c\<' . l:str . '\>' + endif + + while l:len > l:idx + let l:idx = match(l:line, l:str, l:idx) + if l:idx < 0 + " a:str not found + return -1 + endif + let l:synName = synIDattr(synID(a:lnum,l:idx+1,0),"name") + if l:synName != "rapidString" + \&& l:synName != "rapidConcealableString" + \&& (l:synName != "rapidComment" || l:str =~ '^!') + " a:str found outside string or line comment + return l:idx + endif + " a:str is part of string or line comment + let l:idx += 1 " continue search for a:str + endwhile + + " a:str not found or l:len <= a:startIdx + return -1 +endfunction + +" a:lchar should be one of (, ), [, ], { or } +" returns the number of opening/closing parentheses which have no +" closing/opening match in getline(a:lnum) +function s:RapidLoneParen(lnum,lchar) abort + if a:lchar == "(" || a:lchar == ")" + let l:opnParChar = "(" + let l:clsParChar = ")" + elseif a:lchar == "[" || a:lchar == "]" + let l:opnParChar = "[" + let l:clsParChar = "]" + elseif a:lchar == "{" || a:lchar == "}" + let l:opnParChar = "{" + let l:clsParChar = "}" + else + return 0 + endif + + let l:line = getline(a:lnum) + + " look for the first ! which is not part of a string + let l:len = s:RapidLenTilStr(a:lnum,"!",0) + if l:len == 0 + return 0 " first char is !; ignored + endif + + let l:opnParen = 0 + " count opening brackets + let l:i = 0 + while l:i >= 0 + let l:i = s:RapidLenTilStr(a:lnum, l:opnParChar, l:i) + if l:i >= 0 + let l:opnParen += 1 + let l:i += 1 + endif + endwhile + + let l:clsParen = 0 + " count closing brackets + let l:i = 0 + while l:i >= 0 + let l:i = s:RapidLenTilStr(a:lnum, l:clsParChar, l:i) + if l:i >= 0 + let l:clsParen += 1 + let l:i += 1 + endif + endwhile + + if (a:lchar == "(" || a:lchar == "[" || a:lchar == "{") && l:opnParen>l:clsParen + return (l:opnParen-l:clsParen) + elseif (a:lchar == ")" || a:lchar == "]" || a:lchar == "}") && l:clsParen>l:opnParen + return (l:clsParen-l:opnParen) + endif + + return 0 +endfunction + +" This function works almost like prevnonblank() but handles %%%-headers and +" comments like blank lines +function s:RapidPreNoneBlank(lnum) abort + + let nPreNoneBlank = prevnonblank(a:lnum) + + while nPreNoneBlank>0 && getline(nPreNoneBlank) =~ '\v\c^\s*(\%\%\%|!)' + " Previous none blank line irrelevant. Look further aback. + let nPreNoneBlank = prevnonblank(nPreNoneBlank - 1) + endwhile + + return nPreNoneBlank +endfunction + +let &cpo = s:keepcpo +unlet s:keepcpo + +" vim:sw=2 sts=2 et diff --git a/runtime/indent/readline.vim b/runtime/indent/readline.vim new file mode 100644 index 0000000..0ab0f82 --- /dev/null +++ b/runtime/indent/readline.vim @@ -0,0 +1,39 @@ +" Vim indent file +" Language: readline configuration file +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Last Change: 24 Sep 2021 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetReadlineIndent() +setlocal indentkeys=!^F,o,O,=$else,=$endif +setlocal nosmartindent + +let b:undo_indent = "setl inde< indk< si<" + +if exists("*GetReadlineIndent") + finish +endif + +function GetReadlineIndent() + let lnum = prevnonblank(v:lnum - 1) + if lnum == 0 + return 0 + endif + + let ind = indent(lnum) + + if getline(lnum) =~ '^\s*$\(if\|else\)\>' + let ind = ind + shiftwidth() + endif + + if getline(v:lnum) =~ '^\s*$\(else\|endif\)\>' + let ind = ind - shiftwidth() + endif + + return ind +endfunction diff --git a/runtime/indent/rhelp.vim b/runtime/indent/rhelp.vim new file mode 100644 index 0000000..334802a --- /dev/null +++ b/runtime/indent/rhelp.vim @@ -0,0 +1,110 @@ +" Vim indent file +" Language: R Documentation (Help), *.Rd +" Author: Jakson Alves de Aquino <jalvesaq@gmail.com> +" Homepage: https://github.com/jalvesaq/R-Vim-runtime +" Last Change: Mon Feb 27, 2023 07:01PM + + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +runtime indent/r.vim +let s:RIndent = function(substitute(&indentexpr, "()", "", "")) +let b:did_indent = 1 + +setlocal noautoindent +setlocal nocindent +setlocal nosmartindent +setlocal nolisp +setlocal indentkeys=0{,0},:,!^F,o,O,e +setlocal indentexpr=GetCorrectRHelpIndent() + +let b:undo_indent = "setl ai< cin< inde< indk< lisp< si<" + +" Only define the functions once. +if exists("*GetRHelpIndent") + finish +endif + +function s:SanitizeRHelpLine(line) + let newline = substitute(a:line, '\\\\', "x", "g") + let newline = substitute(newline, '\\{', "x", "g") + let newline = substitute(newline, '\\}', "x", "g") + let newline = substitute(newline, '\\%', "x", "g") + let newline = substitute(newline, '%.*', "", "") + let newline = substitute(newline, '\s*$', "", "") + return newline +endfunction + +function GetRHelpIndent() + + let clnum = line(".") " current line + if clnum == 1 + return 0 + endif + let cline = getline(clnum) + + if cline =~ '^\s*}\s*$' + let i = clnum + let bb = -1 + while bb != 0 && i > 1 + let i -= 1 + let line = s:SanitizeRHelpLine(getline(i)) + let line2 = substitute(line, "{", "", "g") + let openb = strlen(line) - strlen(line2) + let line3 = substitute(line2, "}", "", "g") + let closeb = strlen(line2) - strlen(line3) + let bb += openb - closeb + endwhile + return indent(i) + endif + + if cline =~ '^\s*#ifdef\>' || cline =~ '^\s*#endif\>' + return 0 + endif + + let lnum = clnum - 1 + let line = getline(lnum) + if line =~ '^\s*#ifdef\>' || line =~ '^\s*#endif\>' + let lnum -= 1 + let line = getline(lnum) + endif + while lnum > 1 && (line =~ '^\s*$' || line =~ '^#ifdef' || line =~ '^#endif') + let lnum -= 1 + let line = getline(lnum) + endwhile + if lnum == 1 + return 0 + endif + let line = s:SanitizeRHelpLine(line) + let line2 = substitute(line, "{", "", "g") + let openb = strlen(line) - strlen(line2) + let line3 = substitute(line2, "}", "", "g") + let closeb = strlen(line2) - strlen(line3) + let bb = openb - closeb + + let ind = indent(lnum) + (bb * shiftwidth()) + + if line =~ '^\s*}\s*$' + let ind = indent(lnum) + endif + + if ind < 0 + return 0 + endif + + return ind +endfunction + +function GetCorrectRHelpIndent() + let lastsection = search('^\\[a-z]*{', "bncW") + let secname = getline(lastsection) + if secname =~ '^\\usage{' || secname =~ '^\\examples{' || secname =~ '^\\dontshow{' || secname =~ '^\\dontrun{' || secname =~ '^\\donttest{' || secname =~ '^\\testonly{' || secname =~ '^\\method{.*}{.*}(' + return s:RIndent() + else + return GetRHelpIndent() + endif +endfunction + +" vim: sw=2 diff --git a/runtime/indent/rmd.vim b/runtime/indent/rmd.vim new file mode 100644 index 0000000..a043b0c --- /dev/null +++ b/runtime/indent/rmd.vim @@ -0,0 +1,88 @@ +" Vim indent file +" Language: Rmd +" Author: Jakson Alves de Aquino <jalvesaq@gmail.com> +" Homepage: https://github.com/jalvesaq/R-Vim-runtime +" Last Change: Wed Nov 09, 2022 09:44PM + + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +runtime indent/r.vim +let s:RIndent = function(substitute(&indentexpr, "()", "", "")) +let b:did_indent = 1 + +setlocal indentkeys=0{,0},<:>,!^F,o,O,e +setlocal indentexpr=GetRmdIndent() + +let b:undo_indent = "setl inde< indk<" + +if exists("*GetRmdIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +" Simple Python indentation algorithm +function s:GetPyIndent() + let plnum = prevnonblank(v:lnum - 1) + let pline = getline(plnum) + let cline = getline(v:lnum) + if pline =~ '^s```\s*{\s*python ' + return 0 + elseif pline =~ ':$' + return indent(plnum) + &shiftwidth + elseif cline =~ 'else:$' + return indent(plnum) - &shiftwidth + endif + return indent(plnum) +endfunction + +function s:GetMdIndent() + let pline = getline(v:lnum - 1) + let cline = getline(v:lnum) + if prevnonblank(v:lnum - 1) < v:lnum - 1 || cline =~ '^\s*[-\+\*]\s' || cline =~ '^\s*\d\+\.\s\+' + return indent(v:lnum) + elseif pline =~ '^\s*[-\+\*]\s' + return indent(v:lnum - 1) + 2 + elseif pline =~ '^\s*\d\+\.\s\+' + return indent(v:lnum - 1) + 3 + elseif pline =~ '^\[\^\S\+\]: ' + return indent(v:lnum - 1) + shiftwidth() + endif + return indent(prevnonblank(v:lnum - 1)) +endfunction + +function s:GetYamlIndent() + let plnum = prevnonblank(v:lnum - 1) + let pline = getline(plnum) + if pline =~ ':\s*$' + return indent(plnum) + shiftwidth() + elseif pline =~ '^\s*- ' + return indent(v:lnum) + 2 + endif + return indent(plnum) +endfunction + +function GetRmdIndent() + if getline(".") =~ '^[ \t]*```{r .*}$' || getline(".") =~ '^[ \t]*```$' + return 0 + endif + if search('^[ \t]*```{r', "bncW") > search('^[ \t]*```$', "bncW") + return s:RIndent() + elseif v:lnum > 1 && (search('^---$', "bnW") == 1 && + \ (search('^---$', "nW") > v:lnum || search('^\.\.\.$', "nW") > v:lnum)) + return s:GetYamlIndent() + elseif search('^[ \t]*```{python', "bncW") > search('^[ \t]*```$', "bncW") + return s:GetPyIndent() + else + return s:GetMdIndent() + endif +endfunction + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim: sw=2 diff --git a/runtime/indent/rnoweb.vim b/runtime/indent/rnoweb.vim new file mode 100644 index 0000000..668cdb7 --- /dev/null +++ b/runtime/indent/rnoweb.vim @@ -0,0 +1,49 @@ +" Vim indent file +" Language: Rnoweb +" Author: Jakson Alves de Aquino <jalvesaq@gmail.com> +" Homepage: https://github.com/jalvesaq/R-Vim-runtime +" Last Change: Mon Feb 27, 2023 07:17PM + + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +runtime indent/tex.vim + +function s:NoTeXIndent() + return indent(line(".")) +endfunction + +if &indentexpr == "" || &indentexpr == "GetRnowebIndent()" + let s:TeXIndent = function("s:NoTeXIndent") +else + let s:TeXIndent = function(substitute(&indentexpr, "()", "", "")) +endif + +unlet! b:did_indent +runtime indent/r.vim +let s:RIndent = function(substitute(&indentexpr, "()", "", "")) +let b:did_indent = 1 + +setlocal indentkeys=0{,0},!^F,o,O,e,},=\bibitem,=\item +setlocal indentexpr=GetRnowebIndent() + +let b:undo_indent = "setl inde< indk<" + +if exists("*GetRnowebIndent") + finish +endif + +function GetRnowebIndent() + let curline = getline(".") + if curline =~ '^<<.*>>=$' || curline =~ '^\s*@$' + return 0 + endif + if search("^<<", "bncW") > search("^@", "bncW") + return s:RIndent() + endif + return s:TeXIndent() +endfunction + +" vim: sw=2 diff --git a/runtime/indent/rpl.vim b/runtime/indent/rpl.vim new file mode 100644 index 0000000..8577c4d --- /dev/null +++ b/runtime/indent/rpl.vim @@ -0,0 +1,63 @@ +" Vim indent file +" Language: RPL/2 +" Version: 0.2 +" Last Change: 2017 Jun 13 +" Maintainer: BERTRAND Joël <rpl2@free.fr> + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal autoindent +setlocal indentkeys+==~end,=~case,=~if,=~then,=~else,=~do,=~until,=~while,=~repeat,=~select,=~default,=~for,=~start,=~next,=~step,<<>,<>> + +" Define the appropriate indent function but only once +setlocal indentexpr=RplGetFreeIndent() +if exists("*RplGetFreeIndent") + finish +endif + +let b:undo_indent = "set ai< indentkeys< indentexpr<" + +function RplGetIndent(lnum) + let ind = indent(a:lnum) + let prevline=getline(a:lnum) + " Strip tail comment + let prevstat=substitute(prevline, '!.*$', '', '') + + " Add a shiftwidth to statements following if, iferr, then, else, elseif, + " case, select, default, do, until, while, repeat, for, start + if prevstat =~? '\<\(if\|iferr\|do\|while\)\>' && prevstat =~? '\<end\>' + elseif prevstat =~? '\(^\|\s\+\)<<\($\|\s\+\)' && prevstat =~? '\s\+>>\($\|\s\+\)' + elseif prevstat =~? '\<\(if\|iferr\|then\|else\|elseif\|select\|case\|do\|until\|while\|repeat\|for\|start\|default\)\>' || prevstat =~? '\(^\|\s\+\)<<\($\|\s\+\)' + let ind = ind + shiftwidth() + endif + + " Subtract a shiftwidth from then, else, elseif, end, until, repeat, next, + " step + let line = getline(v:lnum) + if line =~? '^\s*\(then\|else\|elseif\|until\|repeat\|next\|step\|default\|end\)\>' + let ind = ind - shiftwidth() + elseif line =~? '^\s*>>\($\|\s\+\)' + let ind = ind - shiftwidth() + endif + + return ind +endfunction + +function RplGetFreeIndent() + " Find the previous non-blank line + let lnum = prevnonblank(v:lnum - 1) + + " Use zero indent at the top of the file + if lnum == 0 + return 0 + endif + + let ind=RplGetIndent(lnum) + return ind +endfunction + +" vim:sw=2 tw=130 diff --git a/runtime/indent/rrst.vim b/runtime/indent/rrst.vim new file mode 100644 index 0000000..585c5e6 --- /dev/null +++ b/runtime/indent/rrst.vim @@ -0,0 +1,49 @@ +" Vim indent file +" Language: Rrst +" Author: Jakson Alves de Aquino <jalvesaq@gmail.com> +" Homepage: https://github.com/jalvesaq/R-Vim-runtime +" Last Change: Feb 25, 2023 + + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +runtime indent/r.vim +let s:RIndent = function(substitute(&indentexpr, "()", "", "")) +let b:did_indent = 1 + +setlocal indentkeys=0{,0},:,!^F,o,O,e +setlocal indentexpr=GetRrstIndent() + +let b:undo_indent = "setl inde< indk<" + +if exists("*GetRrstIndent") + finish +endif + +function GetRstIndent() + let pline = getline(v:lnum - 1) + let cline = getline(v:lnum) + if prevnonblank(v:lnum - 1) < v:lnum - 1 || cline =~ '^\s*[-\+\*]\s' || cline =~ '^\s*\d\+\.\s\+' + return indent(v:lnum) + elseif pline =~ '^\s*[-\+\*]\s' + return indent(v:lnum - 1) + 2 + elseif pline =~ '^\s*\d\+\.\s\+' + return indent(v:lnum - 1) + 3 + endif + return indent(prevnonblank(v:lnum - 1)) +endfunction + +function GetRrstIndent() + if getline(".") =~ '^\.\. {r .*}$' || getline(".") =~ '^\.\. \.\.$' + return 0 + endif + if search('^\.\. {r', "bncW") > search('^\.\. \.\.$', "bncW") + return s:RIndent() + else + return GetRstIndent() + endif +endfunction + +" vim: sw=2 diff --git a/runtime/indent/rst.vim b/runtime/indent/rst.vim new file mode 100644 index 0000000..e3c1086 --- /dev/null +++ b/runtime/indent/rst.vim @@ -0,0 +1,77 @@ +" Vim indent file +" Vim reST indent file +" Language: reStructuredText Documentation Format +" Maintainer: Marshall Ward <marshall.ward@gmail.com> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Latest Revision: 2020-03-31 +" 2023 Aug 28 by Vim Project (undo_indent) + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetRSTIndent() +setlocal indentkeys=!^F,o,O +setlocal nosmartindent + +let b:undo_indent = "setlocal indentexpr< indentkeys< smartindent<" + +if exists("*GetRSTIndent") + finish +endif + +let s:itemization_pattern = '^\s*[-*+]\s' +let s:enumeration_pattern = '^\s*\%(\d\+\|#\)\.\s\+' +let s:note_pattern = '^\.\. ' + +function! s:get_paragraph_start() + let paragraph_mark_start = getpos("'{")[1] + return getline(paragraph_mark_start) =~ '\S' ? paragraph_mark_start : paragraph_mark_start + 1 +endfunction + +function GetRSTIndent() + let lnum = prevnonblank(v:lnum - 1) + if lnum == 0 + return 0 + endif + + let ind = indent(lnum) + let line = getline(lnum) + + let psnum = s:get_paragraph_start() + if psnum != 0 + if getline(psnum) =~ s:note_pattern + let ind = 3 + endif + endif + + if line =~ s:itemization_pattern + let ind += 2 + elseif line =~ s:enumeration_pattern + let ind += matchend(line, s:enumeration_pattern) + endif + + let line = getline(v:lnum - 1) + + " Indent :FIELD: lines. Don’t match if there is no text after the field or + " if the text ends with a sent-ender. + if line =~ '^:.\+:\s\{-1,\}\S.\+[^.!?:]$' + return matchend(line, '^:.\{-1,}:\s\+') + endif + + if line =~ '^\s*$' + execute lnum + call search('^\s*\%([-*+]\s\|\%(\d\+\|#\)\.\s\|\.\.\|$\)', 'bW') + let line = getline('.') + if line =~ s:itemization_pattern + let ind -= 2 + elseif line =~ s:enumeration_pattern + let ind -= matchend(line, s:enumeration_pattern) + elseif line =~ '^\s*\.\.' + let ind -= 3 + endif + endif + + return ind +endfunction diff --git a/runtime/indent/ruby.vim b/runtime/indent/ruby.vim new file mode 100644 index 0000000..ea5a2a7 --- /dev/null +++ b/runtime/indent/ruby.vim @@ -0,0 +1,990 @@ +" Vim indent file +" Language: Ruby +" Maintainer: Andrew Radev <andrey.radev@gmail.com> +" Previous Maintainer: Nikolai Weibull <now at bitwi.se> +" URL: https://github.com/vim-ruby/vim-ruby +" Release Coordinator: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2022 Jun 30 + +" 0. Initialization {{{1 +" ================= + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +if !exists('g:ruby_indent_access_modifier_style') + " Possible values: "normal", "indent", "outdent" + let g:ruby_indent_access_modifier_style = 'normal' +endif + +if !exists('g:ruby_indent_assignment_style') + " Possible values: "variable", "hanging" + let g:ruby_indent_assignment_style = 'hanging' +endif + +if !exists('g:ruby_indent_block_style') + " Possible values: "expression", "do" + let g:ruby_indent_block_style = 'do' +endif + +if !exists('g:ruby_indent_hanging_elements') + " Non-zero means hanging indents are enabled, zero means disabled + let g:ruby_indent_hanging_elements = 1 +endif + +setlocal nosmartindent + +" Now, set up our indentation expression and keys that trigger it. +setlocal indentexpr=GetRubyIndent(v:lnum) +setlocal indentkeys=0{,0},0),0],!^F,o,O,e,:,. +setlocal indentkeys+==end,=else,=elsif,=when,=in\ ,=ensure,=rescue,==begin,==end +setlocal indentkeys+==private,=protected,=public + +let b:undo_indent = "setlocal indentexpr< indentkeys< smartindent<" + +" Only define the function once. +if exists("*GetRubyIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +" 1. Variables {{{1 +" ============ + +" Syntax group names that are strings. +let s:syng_string = + \ ['String', 'Interpolation', 'InterpolationDelimiter', 'StringEscape'] + +" Syntax group names that are strings or documentation. +let s:syng_stringdoc = s:syng_string + ['Documentation'] + +" Syntax group names that are or delimit strings/symbols/regexes or are comments. +let s:syng_strcom = s:syng_stringdoc + [ + \ 'Character', + \ 'Comment', + \ 'HeredocDelimiter', + \ 'PercentRegexpDelimiter', + \ 'PercentStringDelimiter', + \ 'PercentSymbolDelimiter', + \ 'Regexp', + \ 'RegexpCharClass', + \ 'RegexpDelimiter', + \ 'RegexpEscape', + \ 'StringDelimiter', + \ 'Symbol', + \ 'SymbolDelimiter', + \ ] + +" Expression used to check whether we should skip a match with searchpair(). +let s:skip_expr = + \ 'index(map('.string(s:syng_strcom).',"hlID(''ruby''.v:val)"), synID(line("."),col("."),1)) >= 0' + +" Regex used for words that, at the start of a line, add a level of indent. +let s:ruby_indent_keywords = + \ '^\s*\zs\<\%(module\|class\|if\|for' . + \ '\|while\|until\|else\|elsif\|case\|when\|in\|unless\|begin\|ensure\|rescue' . + \ '\|\%(\K\k*[!?]\?\s\+\)\=def\):\@!\>' . + \ '\|\%([=,*/%+-]\|<<\|>>\|:\s\)\s*\zs' . + \ '\<\%(if\|for\|while\|until\|case\|unless\|begin\):\@!\>' + +" Def without an end clause: def method_call(...) = <expression> +let s:ruby_endless_def = '\<def\s\+\%(\k\+\.\)\=\k\+[!?]\=\%((.*)\|\s\)\s*=' + +" Regex used for words that, at the start of a line, remove a level of indent. +let s:ruby_deindent_keywords = + \ '^\s*\zs\<\%(ensure\|else\|rescue\|elsif\|when\|in\|end\):\@!\>' + +" Regex that defines the start-match for the 'end' keyword. +"let s:end_start_regex = '\%(^\|[^.]\)\<\%(module\|class\|def\|if\|for\|while\|until\|case\|unless\|begin\|do\)\>' +" TODO: the do here should be restricted somewhat (only at end of line)? +let s:end_start_regex = + \ '\C\%(^\s*\|[=,*/%+\-|;{]\|<<\|>>\|:\s\)\s*\zs' . + \ '\<\%(module\|class\|if\|for\|while\|until\|case\|unless\|begin' . + \ '\|\%(\K\k*[!?]\?\s\+\)\=def\):\@!\>' . + \ '\|\%(^\|[^.:@$]\)\@<=\<do:\@!\>' + +" Regex that defines the middle-match for the 'end' keyword. +let s:end_middle_regex = '\<\%(ensure\|else\|\%(\%(^\|;\)\s*\)\@<=\<rescue:\@!\>\|when\|\%(\%(^\|;\)\s*\)\@<=\<in\|elsif\):\@!\>' + +" Regex that defines the end-match for the 'end' keyword. +let s:end_end_regex = '\%(^\|[^.:@$]\)\@<=\<end:\@!\>' + +" Expression used for searchpair() call for finding a match for an 'end' keyword. +function! s:EndSkipExpr() + if eval(s:skip_expr) + return 1 + elseif expand('<cword>') == 'do' + \ && getline(".") =~ '^\s*\<\(while\|until\|for\):\@!\>' + return 1 + elseif getline('.') =~ s:ruby_endless_def + return 1 + elseif getline('.') =~ '\<def\s\+\k\+[!?]\=([^)]*$' + " Then it's a `def method(` with a possible `) =` later + call search('\<def\s\+\k\+\zs(', 'W', line('.')) + normal! % + return getline('.') =~ ')\s*=' + else + return 0 + endif +endfunction + +let s:end_skip_expr = function('s:EndSkipExpr') + +" Regex that defines continuation lines, not including (, {, or [. +let s:non_bracket_continuation_regex = + \ '\%([\\.,:*/%+]\|\<and\|\<or\|\%(<%\)\@<![=-]\|:\@<![^[:alnum:]:][|&?]\|||\|&&\)\s*\%(#.*\)\=$' + +" Regex that defines continuation lines. +let s:continuation_regex = + \ '\%(%\@<![({[\\.,:*/%+]\|\<and\|\<or\|\%(<%\)\@<![=-]\|:\@<![^[:alnum:]:][|&?]\|||\|&&\)\s*\%(#.*\)\=$' + +" Regex that defines continuable keywords +let s:continuable_regex = + \ '\C\%(^\s*\|[=,*/%+\-|;{]\|<<\|>>\|:\s\)\s*\zs' . + \ '\<\%(if\|for\|while\|until\|unless\):\@!\>' + +" Regex that defines bracket continuations +let s:bracket_continuation_regex = '%\@<!\%([({[]\)\s*\%(#.*\)\=$' + +" Regex that defines dot continuations +let s:dot_continuation_regex = '%\@<!\.\s*\%(#.*\)\=$' + +" Regex that defines backslash continuations +let s:backslash_continuation_regex = '%\@<!\\\s*$' + +" Regex that defines end of bracket continuation followed by another continuation +let s:bracket_switch_continuation_regex = '^\([^(]\+\zs).\+\)\+'.s:continuation_regex + +" Regex that defines the first part of a splat pattern +let s:splat_regex = '[[,(]\s*\*\s*\%(#.*\)\=$' + +" Regex that describes all indent access modifiers +let s:access_modifier_regex = '\C^\s*\%(public\|protected\|private\)\s*\%(#.*\)\=$' + +" Regex that describes the indent access modifiers (excludes public) +let s:indent_access_modifier_regex = '\C^\s*\%(protected\|private\)\s*\%(#.*\)\=$' + +" Regex that defines blocks. +" +" Note that there's a slight problem with this regex and s:continuation_regex. +" Code like this will be matched by both: +" +" method_call do |(a, b)| +" +" The reason is that the pipe matches a hanging "|" operator. +" +let s:block_regex = + \ '\%(\<do:\@!\>\|%\@<!{\)\s*\%(|[^|]*|\)\=\s*\%(#.*\)\=$' + +let s:block_continuation_regex = '^\s*[^])}\t ].*'.s:block_regex + +" Regex that describes a leading operator (only a method call's dot for now) +let s:leading_operator_regex = '^\s*\%(&\=\.\)' + +" 2. GetRubyIndent Function {{{1 +" ========================= + +function! GetRubyIndent(...) abort + " 2.1. Setup {{{2 + " ---------- + + let indent_info = {} + + " The value of a single shift-width + if exists('*shiftwidth') + let indent_info.sw = shiftwidth() + else + let indent_info.sw = &sw + endif + + " For the current line, use the first argument if given, else v:lnum + let indent_info.clnum = a:0 ? a:1 : v:lnum + let indent_info.cline = getline(indent_info.clnum) + + " Set up variables for restoring position in file. Could use clnum here. + let indent_info.col = col('.') + + " 2.2. Work on the current line {{{2 + " ----------------------------- + let indent_callback_names = [ + \ 's:AccessModifier', + \ 's:ClosingBracketOnEmptyLine', + \ 's:BlockComment', + \ 's:DeindentingKeyword', + \ 's:MultilineStringOrLineComment', + \ 's:ClosingHeredocDelimiter', + \ 's:LeadingOperator', + \ ] + + for callback_name in indent_callback_names +" Decho "Running: ".callback_name + let indent = call(function(callback_name), [indent_info]) + + if indent >= 0 +" Decho "Match: ".callback_name." indent=".indent." info=".string(indent_info) + return indent + endif + endfor + + " 2.3. Work on the previous line. {{{2 + " ------------------------------- + + " Special case: we don't need the real s:PrevNonBlankNonString for an empty + " line inside a string. And that call can be quite expensive in that + " particular situation. + let indent_callback_names = [ + \ 's:EmptyInsideString', + \ ] + + for callback_name in indent_callback_names +" Decho "Running: ".callback_name + let indent = call(function(callback_name), [indent_info]) + + if indent >= 0 +" Decho "Match: ".callback_name." indent=".indent." info=".string(indent_info) + return indent + endif + endfor + + " Previous line number + let indent_info.plnum = s:PrevNonBlankNonString(indent_info.clnum - 1) + let indent_info.pline = getline(indent_info.plnum) + + let indent_callback_names = [ + \ 's:StartOfFile', + \ 's:AfterAccessModifier', + \ 's:ContinuedLine', + \ 's:AfterBlockOpening', + \ 's:AfterHangingSplat', + \ 's:AfterUnbalancedBracket', + \ 's:AfterLeadingOperator', + \ 's:AfterEndKeyword', + \ 's:AfterIndentKeyword', + \ ] + + for callback_name in indent_callback_names +" Decho "Running: ".callback_name + let indent = call(function(callback_name), [indent_info]) + + if indent >= 0 +" Decho "Match: ".callback_name." indent=".indent." info=".string(indent_info) + return indent + endif + endfor + + " 2.4. Work on the MSL line. {{{2 + " -------------------------- + let indent_callback_names = [ + \ 's:PreviousNotMSL', + \ 's:IndentingKeywordInMSL', + \ 's:ContinuedHangingOperator', + \ ] + + " Most Significant line based on the previous one -- in case it's a + " continuation of something above + let indent_info.plnum_msl = s:GetMSL(indent_info.plnum) + + for callback_name in indent_callback_names +" Decho "Running: ".callback_name + let indent = call(function(callback_name), [indent_info]) + + if indent >= 0 +" Decho "Match: ".callback_name." indent=".indent." info=".string(indent_info) + return indent + endif + endfor + + " }}}2 + + " By default, just return the previous line's indent +" Decho "Default case matched" + return indent(indent_info.plnum) +endfunction + +" 3. Indenting Logic Callbacks {{{1 +" ============================ + +function! s:AccessModifier(cline_info) abort + let info = a:cline_info + + " If this line is an access modifier keyword, align according to the closest + " class declaration. + if g:ruby_indent_access_modifier_style == 'indent' + if s:Match(info.clnum, s:access_modifier_regex) + let class_lnum = s:FindContainingClass() + if class_lnum > 0 + return indent(class_lnum) + info.sw + endif + endif + elseif g:ruby_indent_access_modifier_style == 'outdent' + if s:Match(info.clnum, s:access_modifier_regex) + let class_lnum = s:FindContainingClass() + if class_lnum > 0 + return indent(class_lnum) + endif + endif + endif + + return -1 +endfunction + +function! s:ClosingBracketOnEmptyLine(cline_info) abort + let info = a:cline_info + + " If we got a closing bracket on an empty line, find its match and indent + " according to it. For parentheses we indent to its column - 1, for the + " others we indent to the containing line's MSL's level. Return -1 if fail. + let col = matchend(info.cline, '^\s*[]})]') + + if col > 0 && !s:IsInStringOrComment(info.clnum, col) + call cursor(info.clnum, col) + let closing_bracket = info.cline[col - 1] + let bracket_pair = strpart('(){}[]', stridx(')}]', closing_bracket) * 2, 2) + + if searchpair(escape(bracket_pair[0], '\['), '', bracket_pair[1], 'bW', s:skip_expr) > 0 + if closing_bracket == ')' && col('.') != col('$') - 1 + if g:ruby_indent_hanging_elements + let ind = virtcol('.') - 1 + else + let ind = indent(line('.')) + end + elseif g:ruby_indent_block_style == 'do' + let ind = indent(line('.')) + else " g:ruby_indent_block_style == 'expression' + let ind = indent(s:GetMSL(line('.'))) + endif + endif + + return ind + endif + + return -1 +endfunction + +function! s:BlockComment(cline_info) abort + " If we have a =begin or =end set indent to first column. + if match(a:cline_info.cline, '^\s*\%(=begin\|=end\)$') != -1 + return 0 + endif + return -1 +endfunction + +function! s:DeindentingKeyword(cline_info) abort + let info = a:cline_info + + " If we have a deindenting keyword, find its match and indent to its level. + " TODO: this is messy + if s:Match(info.clnum, s:ruby_deindent_keywords) + call cursor(info.clnum, 1) + + if searchpair(s:end_start_regex, s:end_middle_regex, s:end_end_regex, 'bW', + \ s:end_skip_expr) > 0 + let msl = s:GetMSL(line('.')) + let line = getline(line('.')) + + if s:IsAssignment(line, col('.')) && + \ strpart(line, col('.') - 1, 2) !~ 'do' + " assignment to case/begin/etc, on the same line + if g:ruby_indent_assignment_style == 'hanging' + " hanging indent + let ind = virtcol('.') - 1 + else + " align with variable + let ind = indent(line('.')) + endif + elseif g:ruby_indent_block_style == 'do' + " align to line of the "do", not to the MSL + let ind = indent(line('.')) + elseif getline(msl) =~ '=\s*\(#.*\)\=$' + " in the case of assignment to the MSL, align to the starting line, + " not to the MSL + let ind = indent(line('.')) + else + " align to the MSL + let ind = indent(msl) + endif + endif + return ind + endif + + return -1 +endfunction + +function! s:MultilineStringOrLineComment(cline_info) abort + let info = a:cline_info + + " If we are in a multi-line string or line-comment, don't do anything to it. + if s:IsInStringOrDocumentation(info.clnum, matchend(info.cline, '^\s*') + 1) + return indent(info.clnum) + endif + return -1 +endfunction + +function! s:ClosingHeredocDelimiter(cline_info) abort + let info = a:cline_info + + " If we are at the closing delimiter of a "<<" heredoc-style string, set the + " indent to 0. + if info.cline =~ '^\k\+\s*$' + \ && s:IsInStringDelimiter(info.clnum, 1) + \ && search('\V<<'.info.cline, 'nbW') > 0 + return 0 + endif + + return -1 +endfunction + +function! s:LeadingOperator(cline_info) abort + " If the current line starts with a leading operator, add a level of indent. + if s:Match(a:cline_info.clnum, s:leading_operator_regex) + return indent(s:GetMSL(a:cline_info.clnum)) + a:cline_info.sw + endif + return -1 +endfunction + +function! s:EmptyInsideString(pline_info) abort + " If the line is empty and inside a string (the previous line is a string, + " too), use the previous line's indent + let info = a:pline_info + + let plnum = prevnonblank(info.clnum - 1) + let pline = getline(plnum) + + if info.cline =~ '^\s*$' + \ && s:IsInStringOrComment(plnum, 1) + \ && s:IsInStringOrComment(plnum, strlen(pline)) + return indent(plnum) + endif + return -1 +endfunction + +function! s:StartOfFile(pline_info) abort + " At the start of the file use zero indent. + if a:pline_info.plnum == 0 + return 0 + endif + return -1 +endfunction + +function! s:AfterAccessModifier(pline_info) abort + let info = a:pline_info + + if g:ruby_indent_access_modifier_style == 'indent' + " If the previous line was a private/protected keyword, add a + " level of indent. + if s:Match(info.plnum, s:indent_access_modifier_regex) + return indent(info.plnum) + info.sw + endif + elseif g:ruby_indent_access_modifier_style == 'outdent' + " If the previous line was a private/protected/public keyword, add + " a level of indent, since the keyword has been out-dented. + if s:Match(info.plnum, s:access_modifier_regex) + return indent(info.plnum) + info.sw + endif + endif + return -1 +endfunction + +" Example: +" +" if foo || bar || +" baz || bing +" puts "foo" +" end +" +function! s:ContinuedLine(pline_info) abort + let info = a:pline_info + + let col = s:Match(info.plnum, s:ruby_indent_keywords) + if s:Match(info.plnum, s:continuable_regex) && + \ s:Match(info.plnum, s:continuation_regex) + if col > 0 && s:IsAssignment(info.pline, col) + if g:ruby_indent_assignment_style == 'hanging' + " hanging indent + let ind = col - 1 + else + " align with variable + let ind = indent(info.plnum) + endif + else + let ind = indent(s:GetMSL(info.plnum)) + endif + return ind + info.sw + info.sw + endif + return -1 +endfunction + +function! s:AfterBlockOpening(pline_info) abort + let info = a:pline_info + + " If the previous line ended with a block opening, add a level of indent. + if s:Match(info.plnum, s:block_regex) + if g:ruby_indent_block_style == 'do' + " don't align to the msl, align to the "do" + let ind = indent(info.plnum) + info.sw + else + let plnum_msl = s:GetMSL(info.plnum) + + if getline(plnum_msl) =~ '=\s*\(#.*\)\=$' + " in the case of assignment to the msl, align to the starting line, + " not to the msl + let ind = indent(info.plnum) + info.sw + else + let ind = indent(plnum_msl) + info.sw + endif + endif + + return ind + endif + + return -1 +endfunction + +function! s:AfterLeadingOperator(pline_info) abort + " If the previous line started with a leading operator, use its MSL's level + " of indent + if s:Match(a:pline_info.plnum, s:leading_operator_regex) + return indent(s:GetMSL(a:pline_info.plnum)) + endif + return -1 +endfunction + +function! s:AfterHangingSplat(pline_info) abort + let info = a:pline_info + + " If the previous line ended with the "*" of a splat, add a level of indent + if info.pline =~ s:splat_regex + return indent(info.plnum) + info.sw + endif + return -1 +endfunction + +function! s:AfterUnbalancedBracket(pline_info) abort + let info = a:pline_info + + " If the previous line contained unclosed opening brackets and we are still + " in them, find the rightmost one and add indent depending on the bracket + " type. + " + " If it contained hanging closing brackets, find the rightmost one, find its + " match and indent according to that. + if info.pline =~ '[[({]' || info.pline =~ '[])}]\s*\%(#.*\)\=$' + let [opening, closing] = s:ExtraBrackets(info.plnum) + + if opening.pos != -1 + if !g:ruby_indent_hanging_elements + return indent(info.plnum) + info.sw + elseif opening.type == '(' && searchpair('(', '', ')', 'bW', s:skip_expr) > 0 + if col('.') + 1 == col('$') + return indent(info.plnum) + info.sw + else + return virtcol('.') + endif + else + let nonspace = matchend(info.pline, '\S', opening.pos + 1) - 1 + return nonspace > 0 ? nonspace : indent(info.plnum) + info.sw + endif + elseif closing.pos != -1 + call cursor(info.plnum, closing.pos + 1) + normal! % + + if strpart(info.pline, closing.pos) =~ '^)\s*=' + " special case: the closing `) =` of an endless def + return indent(s:GetMSL(line('.'))) + endif + + if s:Match(line('.'), s:ruby_indent_keywords) + return indent('.') + info.sw + else + return indent(s:GetMSL(line('.'))) + endif + else + call cursor(info.clnum, info.col) + end + endif + + return -1 +endfunction + +function! s:AfterEndKeyword(pline_info) abort + let info = a:pline_info + " If the previous line ended with an "end", match that "end"s beginning's + " indent. + let col = s:Match(info.plnum, '\%(^\|[^.:@$]\)\<end\>\s*\%(#.*\)\=$') + if col > 0 + call cursor(info.plnum, col) + if searchpair(s:end_start_regex, '', s:end_end_regex, 'bW', + \ s:end_skip_expr) > 0 + let n = line('.') + let ind = indent('.') + let msl = s:GetMSL(n) + if msl != n + let ind = indent(msl) + end + return ind + endif + end + return -1 +endfunction + +function! s:AfterIndentKeyword(pline_info) abort + let info = a:pline_info + let col = s:Match(info.plnum, s:ruby_indent_keywords) + + if col > 0 && s:Match(info.plnum, s:ruby_endless_def) <= 0 + call cursor(info.plnum, col) + let ind = virtcol('.') - 1 + info.sw + " TODO: make this better (we need to count them) (or, if a searchpair + " fails, we know that something is lacking an end and thus we indent a + " level + if s:Match(info.plnum, s:end_end_regex) + let ind = indent('.') + elseif s:IsAssignment(info.pline, col) + if g:ruby_indent_assignment_style == 'hanging' + " hanging indent + let ind = col + info.sw - 1 + else + " align with variable + let ind = indent(info.plnum) + info.sw + endif + endif + return ind + endif + + return -1 +endfunction + +function! s:PreviousNotMSL(msl_info) abort + let info = a:msl_info + + " If the previous line wasn't a MSL + if info.plnum != info.plnum_msl + " If previous line ends bracket and begins non-bracket continuation decrease indent by 1. + if s:Match(info.plnum, s:bracket_switch_continuation_regex) + " TODO (2016-10-07) Wrong/unused? How could it be "1"? + return indent(info.plnum) - 1 + " If previous line is a continuation return its indent. + elseif s:Match(info.plnum, s:non_bracket_continuation_regex) + return indent(info.plnum) + endif + endif + + return -1 +endfunction + +function! s:IndentingKeywordInMSL(msl_info) abort + let info = a:msl_info + " If the MSL line had an indenting keyword in it, add a level of indent. + " TODO: this does not take into account contrived things such as + " module Foo; class Bar; end + let col = s:Match(info.plnum_msl, s:ruby_indent_keywords) + if col > 0 && s:Match(info.plnum_msl, s:ruby_endless_def) <= 0 + let ind = indent(info.plnum_msl) + info.sw + if s:Match(info.plnum_msl, s:end_end_regex) + let ind = ind - info.sw + elseif s:IsAssignment(getline(info.plnum_msl), col) + if g:ruby_indent_assignment_style == 'hanging' + " hanging indent + let ind = col + info.sw - 1 + else + " align with variable + let ind = indent(info.plnum_msl) + info.sw + endif + endif + return ind + endif + return -1 +endfunction + +function! s:ContinuedHangingOperator(msl_info) abort + let info = a:msl_info + + " If the previous line ended with [*+/.,-=], but wasn't a block ending or a + " closing bracket, indent one extra level. + if s:Match(info.plnum_msl, s:non_bracket_continuation_regex) && !s:Match(info.plnum_msl, '^\s*\([\])}]\|end\)') + if info.plnum_msl == info.plnum + let ind = indent(info.plnum_msl) + info.sw + else + let ind = indent(info.plnum_msl) + endif + return ind + endif + + return -1 +endfunction + +" 4. Auxiliary Functions {{{1 +" ====================== + +function! s:IsInRubyGroup(groups, lnum, col) abort + let ids = map(copy(a:groups), 'hlID("ruby".v:val)') + return index(ids, synID(a:lnum, a:col, 1)) >= 0 +endfunction + +" Check if the character at lnum:col is inside a string, comment, or is ascii. +function! s:IsInStringOrComment(lnum, col) abort + return s:IsInRubyGroup(s:syng_strcom, a:lnum, a:col) +endfunction + +" Check if the character at lnum:col is inside a string. +function! s:IsInString(lnum, col) abort + return s:IsInRubyGroup(s:syng_string, a:lnum, a:col) +endfunction + +" Check if the character at lnum:col is inside a string or documentation. +function! s:IsInStringOrDocumentation(lnum, col) abort + return s:IsInRubyGroup(s:syng_stringdoc, a:lnum, a:col) +endfunction + +" Check if the character at lnum:col is inside a string delimiter +function! s:IsInStringDelimiter(lnum, col) abort + return s:IsInRubyGroup( + \ ['HeredocDelimiter', 'PercentStringDelimiter', 'StringDelimiter'], + \ a:lnum, a:col + \ ) +endfunction + +function! s:IsAssignment(str, pos) abort + return strpart(a:str, 0, a:pos - 1) =~ '=\s*$' +endfunction + +" Find line above 'lnum' that isn't empty, in a comment, or in a string. +function! s:PrevNonBlankNonString(lnum) abort + let in_block = 0 + let lnum = prevnonblank(a:lnum) + while lnum > 0 + " Go in and out of blocks comments as necessary. + " If the line isn't empty (with opt. comment) or in a string, end search. + let line = getline(lnum) + if line =~ '^=begin' + if in_block + let in_block = 0 + else + break + endif + elseif !in_block && line =~ '^=end' + let in_block = 1 + elseif !in_block && line !~ '^\s*#.*$' && !(s:IsInStringOrComment(lnum, 1) + \ && s:IsInStringOrComment(lnum, strlen(line))) + break + endif + let lnum = prevnonblank(lnum - 1) + endwhile + return lnum +endfunction + +" Find line above 'lnum' that started the continuation 'lnum' may be part of. +function! s:GetMSL(lnum) abort + " Start on the line we're at and use its indent. + let msl = a:lnum + let lnum = s:PrevNonBlankNonString(a:lnum - 1) + while lnum > 0 + " If we have a continuation line, or we're in a string, use line as MSL. + " Otherwise, terminate search as we have found our MSL already. + let line = getline(lnum) + + if !s:Match(msl, s:backslash_continuation_regex) && + \ s:Match(lnum, s:backslash_continuation_regex) + " If the current line doesn't end in a backslash, but the previous one + " does, look for that line's msl + " + " Example: + " foo = "bar" \ + " "baz" + " + let msl = lnum + elseif s:Match(msl, s:leading_operator_regex) + " If the current line starts with a leading operator, keep its indent + " and keep looking for an MSL. + let msl = lnum + elseif s:Match(lnum, s:splat_regex) + " If the above line looks like the "*" of a splat, use the current one's + " indentation. + " + " Example: + " Hash[* + " method_call do + " something + " + return msl + elseif s:Match(lnum, s:non_bracket_continuation_regex) && + \ s:Match(msl, s:non_bracket_continuation_regex) + " If the current line is a non-bracket continuation and so is the + " previous one, keep its indent and continue looking for an MSL. + " + " Example: + " method_call one, + " two, + " three + " + let msl = lnum + elseif s:Match(lnum, s:dot_continuation_regex) && + \ (s:Match(msl, s:bracket_continuation_regex) || s:Match(msl, s:block_continuation_regex)) + " If the current line is a bracket continuation or a block-starter, but + " the previous is a dot, keep going to see if the previous line is the + " start of another continuation. + " + " Example: + " parent. + " method_call { + " three + " + let msl = lnum + elseif s:Match(lnum, s:non_bracket_continuation_regex) && + \ (s:Match(msl, s:bracket_continuation_regex) || s:Match(msl, s:block_continuation_regex)) + " If the current line is a bracket continuation or a block-starter, but + " the previous is a non-bracket one, respect the previous' indentation, + " and stop here. + " + " Example: + " method_call one, + " two { + " three + " + return lnum + elseif s:Match(lnum, s:bracket_continuation_regex) && + \ (s:Match(msl, s:bracket_continuation_regex) || s:Match(msl, s:block_continuation_regex)) + " If both lines are bracket continuations (the current may also be a + " block-starter), use the current one's and stop here + " + " Example: + " method_call( + " other_method_call( + " foo + return msl + elseif s:Match(lnum, s:block_regex) && + \ !s:Match(msl, s:continuation_regex) && + \ !s:Match(msl, s:block_continuation_regex) + " If the previous line is a block-starter and the current one is + " mostly ordinary, use the current one as the MSL. + " + " Example: + " method_call do + " something + " something_else + return msl + else + let col = match(line, s:continuation_regex) + 1 + if (col > 0 && !s:IsInStringOrComment(lnum, col)) + \ || s:IsInString(lnum, strlen(line)) + let msl = lnum + else + break + endif + endif + + let lnum = s:PrevNonBlankNonString(lnum - 1) + endwhile + return msl +endfunction + +" Check if line 'lnum' has more opening brackets than closing ones. +function! s:ExtraBrackets(lnum) abort + let opening = {'parentheses': [], 'braces': [], 'brackets': []} + let closing = {'parentheses': [], 'braces': [], 'brackets': []} + + let line = getline(a:lnum) + let pos = match(line, '[][(){}]', 0) + + " Save any encountered opening brackets, and remove them once a matching + " closing one has been found. If a closing bracket shows up that doesn't + " close anything, save it for later. + while pos != -1 + if !s:IsInStringOrComment(a:lnum, pos + 1) + if line[pos] == '(' + call add(opening.parentheses, {'type': '(', 'pos': pos}) + elseif line[pos] == ')' + if empty(opening.parentheses) + call add(closing.parentheses, {'type': ')', 'pos': pos}) + else + let opening.parentheses = opening.parentheses[0:-2] + endif + elseif line[pos] == '{' + call add(opening.braces, {'type': '{', 'pos': pos}) + elseif line[pos] == '}' + if empty(opening.braces) + call add(closing.braces, {'type': '}', 'pos': pos}) + else + let opening.braces = opening.braces[0:-2] + endif + elseif line[pos] == '[' + call add(opening.brackets, {'type': '[', 'pos': pos}) + elseif line[pos] == ']' + if empty(opening.brackets) + call add(closing.brackets, {'type': ']', 'pos': pos}) + else + let opening.brackets = opening.brackets[0:-2] + endif + endif + endif + + let pos = match(line, '[][(){}]', pos + 1) + endwhile + + " Find the rightmost brackets, since they're the ones that are important in + " both opening and closing cases + let rightmost_opening = {'type': '(', 'pos': -1} + let rightmost_closing = {'type': ')', 'pos': -1} + + for opening in opening.parentheses + opening.braces + opening.brackets + if opening.pos > rightmost_opening.pos + let rightmost_opening = opening + endif + endfor + + for closing in closing.parentheses + closing.braces + closing.brackets + if closing.pos > rightmost_closing.pos + let rightmost_closing = closing + endif + endfor + + return [rightmost_opening, rightmost_closing] +endfunction + +function! s:Match(lnum, regex) abort + let line = getline(a:lnum) + let offset = match(line, '\C'.a:regex) + let col = offset + 1 + + while offset > -1 && s:IsInStringOrComment(a:lnum, col) + let offset = match(line, '\C'.a:regex, offset + 1) + let col = offset + 1 + endwhile + + if offset > -1 + return col + else + return 0 + endif +endfunction + +" Locates the containing class/module's definition line, ignoring nested classes +" along the way. +" +function! s:FindContainingClass() abort + let saved_position = getpos('.') + + while searchpair(s:end_start_regex, s:end_middle_regex, s:end_end_regex, 'bW', + \ s:end_skip_expr) > 0 + if expand('<cword>') =~# '\<class\|module\>' + let found_lnum = line('.') + call setpos('.', saved_position) + return found_lnum + endif + endwhile + + call setpos('.', saved_position) + return 0 +endfunction + +" }}}1 + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim:set sw=2 sts=2 ts=8 et: diff --git a/runtime/indent/rust.vim b/runtime/indent/rust.vim new file mode 100644 index 0000000..7c055ec --- /dev/null +++ b/runtime/indent/rust.vim @@ -0,0 +1,286 @@ +" Vim indent file +" Language: Rust +" Author: Chris Morgan <me@chrismorgan.info> +" Last Change: 2023-09-11 +" For bugs, patches and license go to https://github.com/rust-lang/rust.vim + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal cindent +setlocal cinoptions=L0,(s,Ws,J1,j1,m1 +setlocal cinkeys=0{,0},!^F,o,O,0[,0],0(,0) +" Don't think cinwords will actually do anything at all... never mind +setlocal cinwords=for,if,else,while,loop,impl,mod,unsafe,trait,struct,enum,fn,extern,macro + +" Some preliminary settings +setlocal nolisp " Make sure lisp indenting doesn't supersede us +setlocal autoindent " indentexpr isn't much help otherwise +" Also do indentkeys, otherwise # gets shoved to column 0 :-/ +setlocal indentkeys=0{,0},!^F,o,O,0[,0],0(,0) + +setlocal indentexpr=GetRustIndent(v:lnum) + +let b:undo_indent = "setlocal cindent< cinoptions< cinkeys< cinwords< lisp< autoindent< indentkeys< indentexpr<" + +" Only define the function once. +if exists("*GetRustIndent") + finish +endif + +" vint: -ProhibitAbbreviationOption +let s:save_cpo = &cpo +set cpo&vim +" vint: +ProhibitAbbreviationOption + +" Come here when loading the script the first time. + +function! s:get_line_trimmed(lnum) + " Get the line and remove a trailing comment. + " Use syntax highlighting attributes when possible. + " NOTE: this is not accurate; /* */ or a line continuation could trick it + let line = getline(a:lnum) + let line_len = strlen(line) + if has('syntax_items') + " If the last character in the line is a comment, do a binary search for + " the start of the comment. synID() is slow, a linear search would take + " too long on a long line. + if synIDattr(synID(a:lnum, line_len, 1), "name") =~? 'Comment\|Todo' + let min = 1 + let max = line_len + while min < max + let col = (min + max) / 2 + if synIDattr(synID(a:lnum, col, 1), "name") =~? 'Comment\|Todo' + let max = col + else + let min = col + 1 + endif + endwhile + let line = strpart(line, 0, min - 1) + endif + return substitute(line, "\s*$", "", "") + else + " Sorry, this is not complete, nor fully correct (e.g. string "//"). + " Such is life. + return substitute(line, "\s*//.*$", "", "") + endif +endfunction + +function! s:is_string_comment(lnum, col) + if has('syntax_items') + for id in synstack(a:lnum, a:col) + let synname = synIDattr(id, "name") + if synname ==# "rustString" || synname =~# "^rustComment" + return 1 + endif + endfor + else + " without syntax, let's not even try + return 0 + endif +endfunction + +if exists('*shiftwidth') + function! s:shiftwidth() + return shiftwidth() + endfunc +else + function! s:shiftwidth() + return &shiftwidth + endfunc +endif + +function GetRustIndent(lnum) + " Starting assumption: cindent (called at the end) will do it right + " normally. We just want to fix up a few cases. + + let line = getline(a:lnum) + + if has('syntax_items') + let synname = synIDattr(synID(a:lnum, 1, 1), "name") + if synname ==# "rustString" + " If the start of the line is in a string, don't change the indent + return -1 + elseif synname =~? '\(Comment\|Todo\)' + \ && line !~# '^\s*/\*' " not /* opening line + if synname =~? "CommentML" " multi-line + if line !~# '^\s*\*' && getline(a:lnum - 1) =~# '^\s*/\*' + " This is (hopefully) the line after a /*, and it has no + " leader, so the correct indentation is that of the + " previous line. + return GetRustIndent(a:lnum - 1) + endif + endif + " If it's in a comment, let cindent take care of it now. This is + " for cases like "/*" where the next line should start " * ", not + " "* " as the code below would otherwise cause for module scope + " Fun fact: " /*\n*\n*/" takes two calls to get right! + return cindent(a:lnum) + endif + endif + + " cindent gets second and subsequent match patterns/struct members wrong, + " as it treats the comma as indicating an unfinished statement:: + " + " match a { + " b => c, + " d => e, + " f => g, + " }; + + " Search backwards for the previous non-empty line. + let prevlinenum = prevnonblank(a:lnum - 1) + let prevline = s:get_line_trimmed(prevlinenum) + while prevlinenum > 1 && prevline !~# '[^[:blank:]]' + let prevlinenum = prevnonblank(prevlinenum - 1) + let prevline = s:get_line_trimmed(prevlinenum) + endwhile + + " A standalone '{', '}', or 'where' + let l:standalone_open = line =~# '\V\^\s\*{\s\*\$' + let l:standalone_close = line =~# '\V\^\s\*}\s\*\$' + let l:standalone_where = line =~# '\V\^\s\*where\s\*\$' + if l:standalone_open || l:standalone_close || l:standalone_where + " ToDo: we can search for more items than 'fn' and 'if'. + let [l:found_line, l:col, l:submatch] = + \ searchpos('\<\(fn\)\|\(if\)\>', 'bnWp') + if l:found_line !=# 0 + " Now we count the number of '{' and '}' in between the match + " locations and the current line (there is probably a better + " way to compute this). + let l:i = l:found_line + let l:search_line = strpart(getline(l:i), l:col - 1) + let l:opens = 0 + let l:closes = 0 + while l:i < a:lnum + let l:search_line2 = substitute(l:search_line, '\V{', '', 'g') + let l:opens += strlen(l:search_line) - strlen(l:search_line2) + let l:search_line3 = substitute(l:search_line2, '\V}', '', 'g') + let l:closes += strlen(l:search_line2) - strlen(l:search_line3) + let l:i += 1 + let l:search_line = getline(l:i) + endwhile + if l:standalone_open || l:standalone_where + if l:opens ==# l:closes + return indent(l:found_line) + endif + else + " Expect to find just one more close than an open + if l:opens ==# l:closes + 1 + return indent(l:found_line) + endif + endif + endif + endif + + " A standalone 'where' adds a shift. + let l:standalone_prevline_where = prevline =~# '\V\^\s\*where\s\*\$' + if l:standalone_prevline_where + return indent(prevlinenum) + 4 + endif + + " Handle where clauses nicely: subsequent values should line up nicely. + if prevline[len(prevline) - 1] ==# "," + \ && prevline =~# '^\s*where\s' + return indent(prevlinenum) + 6 + endif + + let l:last_prevline_character = prevline[len(prevline) - 1] + + " A line that ends with '.<expr>;' is probably an end of a long list + " of method operations. + if prevline =~# '\V\^\s\*.' && l:last_prevline_character ==# ';' + call cursor(a:lnum - 1, 1) + let l:scope_start = searchpair('{\|(', '', '}\|)', 'nbW', + \ 's:is_string_comment(line("."), col("."))') + if l:scope_start != 0 && l:scope_start < a:lnum + return indent(l:scope_start) + 4 + endif + endif + + if l:last_prevline_character ==# "," + \ && s:get_line_trimmed(a:lnum) !~# '^\s*[\[\]{})]' + \ && prevline !~# '^\s*fn\s' + \ && prevline !~# '([^()]\+,$' + \ && s:get_line_trimmed(a:lnum) !~# '^\s*\S\+\s*=>' + " Oh ho! The previous line ended in a comma! I bet cindent will try to + " take this too far... For now, let's normally use the previous line's + " indent. + + " One case where this doesn't work out is where *this* line contains + " square or curly brackets; then we normally *do* want to be indenting + " further. + " + " Another case where we don't want to is one like a function + " definition with arguments spread over multiple lines: + " + " fn foo(baz: Baz, + " baz: Baz) // <-- cindent gets this right by itself + " + " Another case is similar to the previous, except calling a function + " instead of defining it, or any conditional expression that leaves + " an open paren: + " + " foo(baz, + " baz); + " + " if baz && (foo || + " bar) { + " + " Another case is when the current line is a new match arm. + " + " There are probably other cases where we don't want to do this as + " well. Add them as needed. + return indent(prevlinenum) + endif + + if !has("patch-7.4.355") + " cindent before 7.4.355 doesn't do the module scope well at all; e.g.:: + " + " static FOO : &'static [bool] = [ + " true, + " false, + " false, + " true, + " ]; + " + " uh oh, next statement is indented further! + + " Note that this does *not* apply the line continuation pattern properly; + " that's too hard to do correctly for my liking at present, so I'll just + " start with these two main cases (square brackets and not returning to + " column zero) + + call cursor(a:lnum, 1) + if searchpair('{\|(', '', '}\|)', 'nbW', + \ 's:is_string_comment(line("."), col("."))') == 0 + if searchpair('\[', '', '\]', 'nbW', + \ 's:is_string_comment(line("."), col("."))') == 0 + " Global scope, should be zero + return 0 + else + " At the module scope, inside square brackets only + "if getline(a:lnum)[0] == ']' || search('\[', '', '\]', 'nW') == a:lnum + if line =~# "^\\s*]" + " It's the closing line, dedent it + return 0 + else + return &shiftwidth + endif + endif + endif + endif + + " Fall back on cindent, which does it mostly right + return cindent(a:lnum) +endfunction + +" vint: -ProhibitAbbreviationOption +let &cpo = s:save_cpo +unlet s:save_cpo +" vint: +ProhibitAbbreviationOption + +" vim: set et sw=4 sts=4 ts=8: diff --git a/runtime/indent/sas.vim b/runtime/indent/sas.vim new file mode 100644 index 0000000..bbbbbf0 --- /dev/null +++ b/runtime/indent/sas.vim @@ -0,0 +1,140 @@ +" Vim indent file +" Language: SAS +" Maintainer: Zhen-Huan Hu <wildkeny@gmail.com> +" Version: 3.0.3 +" Last Change: 2022 Apr 06 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetSASIndent() +setlocal indentkeys+=;,=~data,=~proc,=~macro + +let b:undo_indent = "setl inde< indk<" + +if exists("*GetSASIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +" Regex that captures the start of a data/proc section +let s:section_str = '\v%(^|;)\s*%(data|proc)>' +" Regex that captures the end of a run-processing section +let s:section_run = '\v%(^|;)\s*run\s*;' +" Regex that captures the end of a data/proc section +let s:section_end = '\v%(^|;)\s*%(quit|enddata)\s*;' + +" Regex that captures the start of a control block (anything inside a section) +let s:block_str = '\v<%(do>%([^;]+<%(to|over|while)>[^;]+)=|%(compute|define\s+%(column|footer|header|style|table|tagset|crosstabs|statgraph)|edit|layout|method|select)>[^;]+|begingraph)\s*;' +" Regex that captures the end of a control block (anything inside a section) +let s:block_end = '\v<%(end|endcomp|endlayout|endgraph)\s*;' + +" Regex that captures the start of a macro +let s:macro_str = '\v%(^|;)\s*\%macro>' +" Regex that captures the end of a macro +let s:macro_end = '\v%(^|;)\s*\%mend\s*;' + +" Regex that defines the end of the program +let s:program_end = '\v%(^|;)\s*endsas\s*;' + +" List of procs supporting run-processing +let s:run_processing_procs = [ + \ 'catalog', 'chart', 'datasets', 'document', 'ds2', 'plot', 'sql', + \ 'gareabar', 'gbarline', 'gchart', 'gkpi', 'gmap', 'gplot', 'gradar', 'greplay', 'gslide', 'gtile', + \ 'anova', 'arima', 'catmod', 'factex', 'glm', 'model', 'optex', 'plan', 'reg', + \ 'iml', + \ ] + +" Find the line number of previous keyword defined by the regex +function! s:PrevMatch(lnum, regex) + let prev_lnum = prevnonblank(a:lnum - 1) + while prev_lnum > 0 + let prev_line = getline(prev_lnum) + if prev_line =~? a:regex + break + else + let prev_lnum = prevnonblank(prev_lnum - 1) + endif + endwhile + return prev_lnum +endfunction + +" Main function +function! GetSASIndent() + let prev_lnum = prevnonblank(v:lnum - 1) + if prev_lnum ==# 0 + " Leave the indentation of the first line unchanged + return indent(1) + else + let prev_line = getline(prev_lnum) + " Previous non-blank line contains the start of a macro/section/block + " while not the end of a macro/section/block (at the same line) + if (prev_line =~? s:section_str && prev_line !~? s:section_run && prev_line !~? s:section_end) || + \ (prev_line =~? s:block_str && prev_line !~? s:block_end) || + \ (prev_line =~? s:macro_str && prev_line !~? s:macro_end) + let ind = indent(prev_lnum) + shiftwidth() + elseif prev_line =~? s:section_run && prev_line !~? s:section_end + let prev_section_str_lnum = s:PrevMatch(v:lnum, s:section_str) + let prev_section_end_lnum = max([ + \ s:PrevMatch(v:lnum, s:section_end), + \ s:PrevMatch(v:lnum, s:macro_end ), + \ s:PrevMatch(v:lnum, s:program_end)]) + " Check if the section supports run-processing + if prev_section_end_lnum < prev_section_str_lnum && + \ getline(prev_section_str_lnum) =~? '\v%(^|;)\s*proc\s+%(' . + \ join(s:run_processing_procs, '|') . ')>' + let ind = indent(prev_lnum) + shiftwidth() + else + let ind = indent(prev_lnum) + endif + else + let ind = indent(prev_lnum) + endif + endif + " Re-adjustments based on the inputs of the current line + let curr_line = getline(v:lnum) + if curr_line =~? s:program_end + " End of the program + " Same indentation as the first non-blank line + return indent(nextnonblank(1)) + elseif curr_line =~? s:macro_end + " Current line is the end of a macro + " Match the indentation of the start of the macro + return indent(s:PrevMatch(v:lnum, s:macro_str)) + elseif curr_line =~? s:block_end && curr_line !~? s:block_str + " Re-adjust if current line is the end of a block + " while not the beginning of a block (at the same line) + " Returning the indent of previous block start directly + " would not work due to nesting + let ind = ind - shiftwidth() + elseif curr_line =~? s:section_str || curr_line =~? s:section_run || curr_line =~? s:section_end + " Re-adjust if current line is the start/end of a section + " since the end of a section could be inexplicit + let prev_section_str_lnum = s:PrevMatch(v:lnum, s:section_str) + " Check if the previous section supports run-processing + if getline(prev_section_str_lnum) =~? '\v%(^|;)\s*proc\s+%(' . + \ join(s:run_processing_procs, '|') . ')>' + let prev_section_end_lnum = max([ + \ s:PrevMatch(v:lnum, s:section_end), + \ s:PrevMatch(v:lnum, s:macro_end ), + \ s:PrevMatch(v:lnum, s:program_end)]) + else + let prev_section_end_lnum = max([ + \ s:PrevMatch(v:lnum, s:section_end), + \ s:PrevMatch(v:lnum, s:section_run), + \ s:PrevMatch(v:lnum, s:macro_end ), + \ s:PrevMatch(v:lnum, s:program_end)]) + endif + if prev_section_end_lnum < prev_section_str_lnum + let ind = ind - shiftwidth() + endif + endif + return ind +endfunction + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/indent/sass.vim b/runtime/indent/sass.vim new file mode 100644 index 0000000..45dc869 --- /dev/null +++ b/runtime/indent/sass.vim @@ -0,0 +1,38 @@ +" Vim indent file +" Language: Sass +" Maintainer: Tim Pope <vimNOSPAM@tpope.org> +" Last Change: 2023 Dec 28 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal autoindent +setlocal indentexpr=GetSassIndent() +setlocal indentkeys=o,O,*<Return>,<:>,!^F + +let b:undo_indent = "setl ai< inde< indk<" + +" Only define the function once. +if exists("*GetSassIndent") + finish +endif + +let s:property = '^\s*:\|^\s*[[:alnum:]#{}-]\+\%(:\|\s*=\)' +let s:extend = '^\s*\%(@extend\|@include\|+\)' + +function! GetSassIndent() + let lnum = prevnonblank(v:lnum-1) + let line = substitute(getline(lnum),'\s\+$','','') + let cline = substitute(substitute(getline(v:lnum),'\s\+$','',''),'^\s\+','','') + let line = substitute(line,'^\s\+','','') + let indent = indent(lnum) + if line !~ s:property && line !~ s:extend && cline =~ s:property + return indent + shiftwidth() + else + return -1 + endif +endfunction + +" vim:set sw=2: diff --git a/runtime/indent/scala.vim b/runtime/indent/scala.vim new file mode 100644 index 0000000..c6aba4e --- /dev/null +++ b/runtime/indent/scala.vim @@ -0,0 +1,615 @@ +" Vim indent file +" Language: Scala (http://scala-lang.org/) +" Original Author: Stefan Matthias Aust +" Modifications By: Derek Wyatt +" URL: https://github.com/derekwyatt/vim-scala +" Last Change: 2016 Aug 26 +" 2023 Aug 28 by Vim Project (undo_indent) + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal autoindent +setlocal indentexpr=GetScalaIndent() +setlocal indentkeys=0{,0},0),!^F,<>>,o,O,e,=case,<CR> + +let b:undo_indent = "setl ai< inde< indk<" + +if exists("*GetScalaIndent") + finish +endif +let s:keepcpo= &cpo +set cpo&vim + +let s:annotationMatcher = '@[A-Za-z._]\+\s\+' +let s:modifierMatcher = s:annotationMatcher . '\|\%(private\|protected\)\%(\[[^\]]*\]\)\?\s\+\|abstract\s\+\|override\s\+\|final\s\+' +let s:defMatcher = '\%(' . s:modifierMatcher . '\)*\<def\>' +let s:valMatcher = '\%(' . s:modifierMatcher . '\|lazy\s\+\)*\<va[lr]\>' +let s:funcNameMatcher = '\w\+' +let s:typeSpecMatcher = '\%(\s*\[\_[^\]]*\]\)' +let s:defArgMatcher = '\%((\_.\{-})\)' +let s:returnTypeMatcher = '\%(:\s*\w\+' . s:typeSpecMatcher . '\?\)' +let g:fullDefMatcher = '^\s*' . s:defMatcher . '\s\+' . s:funcNameMatcher . '\s*' . s:typeSpecMatcher . '\?\s*' . s:defArgMatcher . '\?\s*' . s:returnTypeMatcher . '\?\s*[={]' + +function! scala#ConditionalConfirm(msg) + if 0 + call confirm(a:msg) + endif +endfunction + +function! scala#GetLine(lnum) + let line = substitute(getline(a:lnum), '//.*$', '', '') + let line = substitute(line, '"\(.\|\\"\)\{-}"', '""', 'g') + return line +endfunction + +function! scala#CountBrackets(line, openBracket, closedBracket) + let line = substitute(a:line, '"\(.\|\\"\)\{-}"', '', 'g') + let open = substitute(line, '[^' . a:openBracket . ']', '', 'g') + let close = substitute(line, '[^' . a:closedBracket . ']', '', 'g') + return strlen(open) - strlen(close) +endfunction + +function! scala#CountParens(line) + return scala#CountBrackets(a:line, '(', ')') +endfunction + +function! scala#CountCurlies(line) + return scala#CountBrackets(a:line, '{', '}') +endfunction + +function! scala#LineEndsInIncomplete(line) + if a:line =~ '[.,]\s*$' + return 1 + else + return 0 + endif +endfunction + +function! scala#LineIsAClosingXML(line) + if a:line =~ '^\s*</\w' + return 1 + else + return 0 + endif +endfunction + +function! scala#LineCompletesXML(lnum, line) + let savedpos = getpos('.') + call setpos('.', [savedpos[0], a:lnum, 0, savedpos[3]]) + let tag = substitute(a:line, '^.*</\([^>]*\)>.*$', '\1', '') + let [lineNum, colnum] = searchpairpos('<' . tag . '>', '', '</' . tag . '>', 'Wbn') + call setpos('.', savedpos) + let pline = scala#GetLine(prevnonblank(lineNum - 1)) + if pline =~ '=\s*$' + return 1 + else + return 0 + endif +endfunction + +function! scala#IsParentCase() + let savedpos = getpos('.') + call setpos('.', [savedpos[0], savedpos[1], 0, savedpos[3]]) + let [l, c] = searchpos('^\s*\%(' . s:defMatcher . '\|\%(\<case\>\)\)', 'bnW') + let retvalue = -1 + if l != 0 && search('\%' . l . 'l\s*\<case\>', 'bnW') + let retvalue = l + endif + call setpos('.', savedpos) + return retvalue +endfunction + +function! scala#CurlyMatcher() + let matchline = scala#GetLineThatMatchesBracket('{', '}') + if scala#CountParens(scala#GetLine(matchline)) < 0 + let savedpos = getpos('.') + call setpos('.', [savedpos[0], matchline, 9999, savedpos[3]]) + call searchpos('{', 'Wbc') + call searchpos(')', 'Wb') + let [lnum, colnum] = searchpairpos('(', '', ')', 'Wbn') + call setpos('.', savedpos) + let line = scala#GetLine(lnum) + if line =~ '^\s*' . s:defMatcher + return lnum + else + return matchline + endif + else + return matchline + endif +endfunction + +function! scala#GetLineAndColumnThatMatchesCurly() + return scala#GetLineAndColumnThatMatchesBracket('{', '}') +endfunction + +function! scala#GetLineAndColumnThatMatchesParen() + return scala#GetLineAndColumnThatMatchesBracket('(', ')') +endfunction + +function! scala#GetLineAndColumnThatMatchesBracket(openBracket, closedBracket) + let savedpos = getpos('.') + let curline = scala#GetLine(line('.')) + if curline =~ a:closedBracket . '.*' . a:openBracket . '.*' . a:closedBracket + call setpos('.', [savedpos[0], savedpos[1], 0, savedpos[3]]) + call searchpos(a:closedBracket . '\ze[^' . a:closedBracket . a:openBracket . ']*' . a:openBracket, 'W') + else + call setpos('.', [savedpos[0], savedpos[1], 9999, savedpos[3]]) + call searchpos(a:closedBracket, 'Wbc') + endif + let [lnum, colnum] = searchpairpos(a:openBracket, '', a:closedBracket, 'Wbn') + call setpos('.', savedpos) + return [lnum, colnum] +endfunction + +function! scala#GetLineThatMatchesCurly() + return scala#GetLineThatMatchesBracket('{', '}') +endfunction + +function! scala#GetLineThatMatchesParen() + return scala#GetLineThatMatchesBracket('(', ')') +endfunction + +function! scala#GetLineThatMatchesBracket(openBracket, closedBracket) + let [lnum, colnum] = scala#GetLineAndColumnThatMatchesBracket(a:openBracket, a:closedBracket) + return lnum +endfunction + +function! scala#NumberOfBraceGroups(line) + let line = substitute(a:line, '[^()]', '', 'g') + if strlen(line) == 0 + return 0 + endif + let line = substitute(line, '^)*', '', 'g') + if strlen(line) == 0 + return 0 + endif + let line = substitute(line, '^(', '', 'g') + if strlen(line) == 0 + return 0 + endif + let c = 1 + let counter = 0 + let groupCount = 0 + while counter < strlen(line) + let char = strpart(line, counter, 1) + if char == '(' + let c = c + 1 + elseif char == ')' + let c = c - 1 + endif + if c == 0 + let groupCount = groupCount + 1 + endif + let counter = counter + 1 + endwhile + return groupCount +endfunction + +function! scala#MatchesIncompleteDefValr(line) + if a:line =~ '^\s*\%(' . s:defMatcher . '\|' . s:valMatcher . '\).*[=({]\s*$' + return 1 + else + return 0 + endif +endfunction + +function! scala#LineIsCompleteIf(line) + if scala#CountBrackets(a:line, '{', '}') == 0 && + \ scala#CountBrackets(a:line, '(', ')') == 0 && + \ a:line =~ '^\s*\<if\>\s*([^)]*)\s*\S.*$' + return 1 + else + return 0 + endif +endfunction + +function! scala#LineCompletesIfElse(lnum, line) + if a:line =~ '^\s*\%(\<if\>\|\%(}\s*\)\?\<else\>\)' + return 0 + endif + let result = search('^\%(\s*\<if\>\s*(.*).*\n\|\s*\<if\>\s*(.*)\s*\n.*\n\)\%(\s*\<else\>\s*\<if\>\s*(.*)\s*\n.*\n\)*\%(\s*\<else\>\s*\n\|\s*\<else\>[^{]*\n\)\?\%' . a:lnum . 'l', 'Wbn') + if result != 0 && scala#GetLine(prevnonblank(a:lnum - 1)) !~ '{\s*$' + return result + endif + return 0 +endfunction + +function! scala#GetPrevCodeLine(lnum) + " This needs to skip comment lines + return prevnonblank(a:lnum - 1) +endfunction + +function! scala#InvertBracketType(openBracket, closedBracket) + if a:openBracket == '(' + return [ '{', '}' ] + else + return [ '(', ')' ] + endif +endfunction + +function! scala#Testhelper(lnum, line, openBracket, closedBracket, iteration) + let bracketCount = scala#CountBrackets(a:line, a:openBracket, a:closedBracket) + " There are more '}' braces than '{' on this line so it may be completing the function definition + if bracketCount < 0 + let [matchedLNum, matchedColNum] = scala#GetLineAndColumnThatMatchesBracket(a:openBracket, a:closedBracket) + if matchedLNum == a:lnum + return -1 + endif + let matchedLine = scala#GetLine(matchedLNum) + if ! scala#MatchesIncompleteDefValr(matchedLine) + let bracketLine = substitute(substitute(matchedLine, '\%' . matchedColNum . 'c.*$', '', ''), '[^{}()]', '', 'g') + if bracketLine =~ '}$' + return scala#Testhelper(matchedLNum, matchedLine, '{', '}', a:iteration + 1) + elseif bracketLine =~ ')$' + return scala#Testhelper(matchedLNum, matchedLine, '(', ')', a:iteration + 1) + else + let prevCodeLNum = scala#GetPrevCodeLine(matchedLNum) + if scala#MatchesIncompleteDefValr(scala#GetLine(prevCodeLNum)) + return prevCodeLNum + else + return -1 + endif + endif + else + " return indent value instead + return matchedLNum + endif + " There's an equal number of '{' and '}' on this line so it may be a single line function definition + elseif bracketCount == 0 + if a:iteration == 0 + let otherBracketType = scala#InvertBracketType(a:openBracket, a:closedBracket) + return scala#Testhelper(a:lnum, a:line, otherBracketType[0], otherBracketType[1], a:iteration + 1) + else + let prevCodeLNum = scala#GetPrevCodeLine(a:lnum) + let prevCodeLine = scala#GetLine(prevCodeLNum) + if scala#MatchesIncompleteDefValr(prevCodeLine) && prevCodeLine !~ '{\s*$' + return prevCodeLNum + else + let possibleIfElse = scala#LineCompletesIfElse(a:lnum, a:line) + if possibleIfElse != 0 + let defValrLine = prevnonblank(possibleIfElse - 1) + let possibleDefValr = scala#GetLine(defValrLine) + if scala#MatchesIncompleteDefValr(possibleDefValr) && possibleDefValr =~ '^.*=\s*$' + return possibleDefValr + else + return -1 + endif + else + return -1 + endif + endif + endif + else + return -1 + endif +endfunction + +function! scala#Test(lnum, line, openBracket, closedBracket) + return scala#Testhelper(a:lnum, a:line, a:openBracket, a:closedBracket, 0) +endfunction + +function! scala#LineCompletesDefValr(lnum, line) + let bracketCount = scala#CountBrackets(a:line, '{', '}') + if bracketCount < 0 + let matchedBracket = scala#GetLineThatMatchesBracket('{', '}') + if ! scala#MatchesIncompleteDefValr(scala#GetLine(matchedBracket)) + let possibleDefValr = scala#GetLine(prevnonblank(matchedBracket - 1)) + if matchedBracket != -1 && scala#MatchesIncompleteDefValr(possibleDefValr) + return 1 + else + return 0 + endif + else + return 0 + endif + elseif bracketCount == 0 + let bracketCount = scala#CountBrackets(a:line, '(', ')') + if bracketCount < 0 + let matchedBracket = scala#GetLineThatMatchesBracket('(', ')') + if ! scala#MatchesIncompleteDefValr(scala#GetLine(matchedBracket)) + let possibleDefValr = scala#GetLine(prevnonblank(matchedBracket - 1)) + if matchedBracket != -1 && scala#MatchesIncompleteDefValr(possibleDefValr) + return 1 + else + return 0 + endif + else + return 0 + endif + elseif bracketCount == 0 + let possibleDefValr = scala#GetLine(prevnonblank(a:lnum - 1)) + if scala#MatchesIncompleteDefValr(possibleDefValr) && possibleDefValr =~ '^.*=\s*$' + return 1 + else + let possibleIfElse = scala#LineCompletesIfElse(a:lnum, a:line) + if possibleIfElse != 0 + let possibleDefValr = scala#GetLine(prevnonblank(possibleIfElse - 1)) + if scala#MatchesIncompleteDefValr(possibleDefValr) && possibleDefValr =~ '^.*=\s*$' + return 2 + else + return 0 + endif + else + return 0 + endif + endif + else + return 0 + endif + endif +endfunction + +function! scala#SpecificLineCompletesBrackets(lnum, openBracket, closedBracket) + let savedpos = getpos('.') + call setpos('.', [savedpos[0], a:lnum, 9999, savedpos[3]]) + let retv = scala#LineCompletesBrackets(a:openBracket, a:closedBracket) + call setpos('.', savedpos) + + return retv +endfunction + +function! scala#LineCompletesBrackets(openBracket, closedBracket) + let savedpos = getpos('.') + let offline = 0 + while offline == 0 + let [lnum, colnum] = searchpos(a:closedBracket, 'Wb') + let [lnumA, colnumA] = searchpairpos(a:openBracket, '', a:closedBracket, 'Wbn') + if lnum != lnumA + let [lnumB, colnumB] = searchpairpos(a:openBracket, '', a:closedBracket, 'Wbnr') + let offline = 1 + endif + endwhile + call setpos('.', savedpos) + if lnumA == lnumB && colnumA == colnumB + return lnumA + else + return -1 + endif +endfunction + +function! GetScalaIndent() + " Find a non-blank line above the current line. + let prevlnum = prevnonblank(v:lnum - 1) + + " Hit the start of the file, use zero indent. + if prevlnum == 0 + return 0 + endif + + let ind = indent(prevlnum) + let originalIndentValue = ind + let prevline = scala#GetLine(prevlnum) + let curlnum = v:lnum + let curline = scala#GetLine(curlnum) + if get(g:, 'scala_scaladoc_indent', 0) + let star_indent = 2 + else + let star_indent = 1 + end + + if prevline =~ '^\s*/\*\*' + if prevline =~ '\*/\s*$' + return ind + else + return ind + star_indent + endif + endif + + if curline =~ '^\s*\*' + return cindent(curlnum) + endif + + " If this line starts with a { then make it indent the same as the previous line + if curline =~ '^\s*{' + call scala#ConditionalConfirm("1") + " Unless, of course, the previous one is a { as well + if prevline !~ '^\s*{' + call scala#ConditionalConfirm("2") + return indent(prevlnum) + endif + endif + + " '.' continuations + if curline =~ '^\s*\.' + if prevline =~ '^\s*\.' + return ind + else + return ind + shiftwidth() + endif + endif + + " Indent html literals + if prevline !~ '/>\s*$' && prevline =~ '^\s*<[a-zA-Z][^>]*>\s*$' + call scala#ConditionalConfirm("3") + return ind + shiftwidth() + endif + + " assumes curly braces around try-block + if curline =~ '^\s*}\s*\<catch\>' + return ind - shiftwidth() + elseif curline =~ '^\s*\<catch\>' + return ind + endif + + " Add a shiftwidth()' after lines that start a block + " If 'if', 'for' or 'while' end with ), this is a one-line block + " If 'val', 'var', 'def' end with =, this is a one-line block + if (prevline =~ '^\s*\<\%(\%(}\?\s*else\s\+\)\?if\|for\|while\)\>.*[)=]\s*$' && scala#NumberOfBraceGroups(prevline) <= 1) + \ || prevline =~ '^\s*' . s:defMatcher . '.*=\s*$' + \ || prevline =~ '^\s*' . s:valMatcher . '.*[=]\s*$' + \ || prevline =~ '^\s*\%(}\s*\)\?\<else\>\s*$' + \ || prevline =~ '=\s*$' + call scala#ConditionalConfirm("4") + let ind = ind + shiftwidth() + elseif prevline =~ '^\s*\<\%(}\?\s*else\s\+\)\?if\>' && curline =~ '^\s*}\?\s*\<else\>' + return ind + endif + + let lineCompletedBrackets = 0 + let bracketCount = scala#CountBrackets(prevline, '{', '}') + if bracketCount > 0 || prevline =~ '.*{\s*$' + call scala#ConditionalConfirm("5b") + let ind = ind + shiftwidth() + elseif bracketCount < 0 + call scala#ConditionalConfirm("6b") + " if the closing brace actually completes the braces entirely, then we + " have to indent to line that started the whole thing + let completeLine = scala#LineCompletesBrackets('{', '}') + if completeLine != -1 + call scala#ConditionalConfirm("8b") + let prevCompleteLine = scala#GetLine(prevnonblank(completeLine - 1)) + " However, what actually started this part looks like it was a function + " definition, so we need to indent to that line instead. This is + " actually pretty weak at the moment. + if prevCompleteLine =~ '=\s*$' + call scala#ConditionalConfirm("9b") + let ind = indent(prevnonblank(completeLine - 1)) + else + call scala#ConditionalConfirm("10b") + let ind = indent(completeLine) + endif + else + let lineCompletedBrackets = 1 + endif + endif + + if ind == originalIndentValue + let bracketCount = scala#CountBrackets(prevline, '(', ')') + if bracketCount > 0 || prevline =~ '.*(\s*$' + call scala#ConditionalConfirm("5a") + let ind = ind + shiftwidth() + elseif bracketCount < 0 + call scala#ConditionalConfirm("6a") + " if the closing brace actually completes the braces entirely, then we + " have to indent to line that started the whole thing + let completeLine = scala#LineCompletesBrackets('(', ')') + if completeLine != -1 && prevline !~ '^.*{\s*$' + call scala#ConditionalConfirm("8a") + let prevCompleteLine = scala#GetLine(prevnonblank(completeLine - 1)) + " However, what actually started this part looks like it was a function + " definition, so we need to indent to that line instead. This is + " actually pretty weak at the moment. + if prevCompleteLine =~ '=\s*$' + call scala#ConditionalConfirm("9a") + let ind = indent(prevnonblank(completeLine - 1)) + else + call scala#ConditionalConfirm("10a") + let ind = indent(completeLine) + endif + else + " This is the only part that's different from from the '{', '}' one below + " Yup... some refactoring is necessary at some point. + let ind = ind + (bracketCount * shiftwidth()) + let lineCompletedBrackets = 1 + endif + endif + endif + + if curline =~ '^\s*}\?\s*\<else\>\%(\s\+\<if\>\s*(.*)\)\?\s*{\?\s*$' && + \ ! scala#LineIsCompleteIf(prevline) && + \ prevline !~ '^.*}\s*$' + let ind = ind - shiftwidth() + endif + + " Subtract a shiftwidth()' on '}' or html + let curCurlyCount = scala#CountCurlies(curline) + if curCurlyCount < 0 + call scala#ConditionalConfirm("14a") + let matchline = scala#CurlyMatcher() + return indent(matchline) + elseif curline =~ '^\s*</[a-zA-Z][^>]*>' + call scala#ConditionalConfirm("14c") + return ind - shiftwidth() + endif + + let prevParenCount = scala#CountParens(prevline) + if prevline =~ '^\s*\<for\>.*$' && prevParenCount > 0 + call scala#ConditionalConfirm("15") + let ind = indent(prevlnum) + 5 + endif + + let prevCurlyCount = scala#CountCurlies(prevline) + if prevCurlyCount == 0 && prevline =~ '^.*\%(=>\|⇒\)\s*$' && prevline !~ '^\s*this\s*:.*\%(=>\|⇒\)\s*$' && curline !~ '^\s*\<case\>' + call scala#ConditionalConfirm("16") + let ind = ind + shiftwidth() + endif + + if ind == originalIndentValue && curline =~ '^\s*\<case\>' + call scala#ConditionalConfirm("17") + let parentCase = scala#IsParentCase() + if parentCase != -1 + call scala#ConditionalConfirm("17a") + return indent(parentCase) + endif + endif + + if prevline =~ '^\s*\*/' + \ || prevline =~ '*/\s*$' + call scala#ConditionalConfirm("18") + let ind = ind - star_indent + endif + + if scala#LineEndsInIncomplete(prevline) + call scala#ConditionalConfirm("19") + return ind + endif + + if scala#LineIsAClosingXML(prevline) + if scala#LineCompletesXML(prevlnum, prevline) + call scala#ConditionalConfirm("20a") + return ind - shiftwidth() + else + call scala#ConditionalConfirm("20b") + return ind + endif + endif + + if ind == originalIndentValue + "let indentMultiplier = scala#LineCompletesDefValr(prevlnum, prevline) + "if indentMultiplier != 0 + " call scala#ConditionalConfirm("19a") + " let ind = ind - (indentMultiplier * shiftwidth()) + let defValrLine = scala#Test(prevlnum, prevline, '{', '}') + if defValrLine != -1 + call scala#ConditionalConfirm("21a") + let ind = indent(defValrLine) + elseif lineCompletedBrackets == 0 + call scala#ConditionalConfirm("21b") + if scala#GetLine(prevnonblank(prevlnum - 1)) =~ '^.*\<else\>\s*\%(//.*\)\?$' + call scala#ConditionalConfirm("21c") + let ind = ind - shiftwidth() + elseif scala#LineCompletesIfElse(prevlnum, prevline) + call scala#ConditionalConfirm("21d") + let ind = ind - shiftwidth() + elseif scala#CountParens(curline) < 0 && curline =~ '^\s*)' && scala#GetLine(scala#GetLineThatMatchesBracket('(', ')')) =~ '.*(\s*$' + " Handles situations that look like this: + " + " val a = func( + " 10 + " ) + " + " or + " + " val a = func( + " 10 + " ).somethingHere() + call scala#ConditionalConfirm("21e") + let ind = ind - shiftwidth() + endif + endif + endif + + call scala#ConditionalConfirm("returning " . ind) + + return ind +endfunction + +let &cpo = s:keepcpo +unlet s:keepcpo + +" vim:set sw=2 sts=2 ts=8 et: +" vim600:fdm=marker fdl=1 fdc=0: diff --git a/runtime/indent/scheme.vim b/runtime/indent/scheme.vim new file mode 100644 index 0000000..496da32 --- /dev/null +++ b/runtime/indent/scheme.vim @@ -0,0 +1,14 @@ +" Vim indent file +" Language: Scheme +" Last Change: 2018 Jan 31 +" Maintainer: Evan Hanson <evhan@foldling.org> +" Previous Maintainer: Sergey Khorev <sergey.khorev@gmail.com> +" URL: https://foldling.org/vim/indent/scheme.vim + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +" Use the Lisp indenting +runtime! indent/lisp.vim diff --git a/runtime/indent/scss.vim b/runtime/indent/scss.vim new file mode 100644 index 0000000..82bba49 --- /dev/null +++ b/runtime/indent/scss.vim @@ -0,0 +1,12 @@ +" Vim indent file +" Language: SCSS +" Maintainer: Tim Pope <vimNOSPAM@tpope.org> +" Last Change: 2010 Jul 26 + +if exists("b:did_indent") + finish +endif + +runtime! indent/css.vim + +" vim:set sw=2: diff --git a/runtime/indent/sdl.vim b/runtime/indent/sdl.vim new file mode 100644 index 0000000..40fe63f --- /dev/null +++ b/runtime/indent/sdl.vim @@ -0,0 +1,95 @@ +" Vim indent file +" Language: SDL +" Maintainer: Michael Piefel <entwurf@piefel.de> +" Last Change: 2021 Oct 03 + +" Shamelessly stolen from the Vim-Script indent file + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetSDLIndent() +setlocal indentkeys+==~end,=~state,*<Return> + +let b:undo_indent = "setl inde< indk<" + +" Only define the function once. +if exists("*GetSDLIndent") +" finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +function! GetSDLIndent() + " Find a non-blank line above the current line. + let lnum = prevnonblank(v:lnum - 1) + + " At the start of the file use zero indent. + if lnum == 0 + return 0 + endif + + let ind = indent(lnum) + let virtuality = '^\s*\(\(virtual\|redefined\|finalized\)\s\+\)\=\s*' + + " Add a single space to comments which use asterisks + if getline(lnum) =~ '^\s*\*' + let ind = ind - 1 + endif + if getline(v:lnum) =~ '^\s*\*' + let ind = ind + 1 + endif + + " Add a 'shiftwidth' after states, different blocks, decision (and alternatives), inputs + if (getline(lnum) =~? '^\s*\(start\|state\|system\|package\|connection\|channel\|alternative\|macro\|operator\|newtype\|select\|substructure\|decision\|generator\|refinement\|service\|method\|exceptionhandler\|asntype\|syntype\|value\|(.*):\|\(priority\s\+\)\=input\|provided\)' + \ || getline(lnum) =~? virtuality . '\(process\|procedure\|block\|object\)') + \ && getline(lnum) !~? 'end[[:alpha:]]\+;$' + let ind = ind + shiftwidth() + endif + + " Subtract a 'shiftwidth' after states + if getline(lnum) =~? '^\s*\(stop\|return\>\|nextstate\)' + let ind = ind - shiftwidth() + endif + + " Subtract a 'shiftwidth' on on end (uncompleted line) + if getline(v:lnum) =~? '^\s*end\>' + let ind = ind - shiftwidth() + endif + + " Put each alternatives where the corresponding decision was + if getline(v:lnum) =~? '^\s*\((.*)\|else\):' + normal k + let ind = indent(searchpair('^\s*decision', '', '^\s*enddecision', 'bW', + \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? "sdlString"')) + endif + + " Put each state where the preceding state was + if getline(v:lnum) =~? '^\s*state\>' + let ind = indent(search('^\s*start', 'bW')) + endif + + " Systems and packages are always in column 0 + if getline(v:lnum) =~? '^\s*\(\(end\)\=system\|\(end\)\=package\)' + return 0 + endif + + " Put each end* where the corresponding begin was + if getline(v:lnum) =~? '^\s*end[[:alpha:]]' + normal k + let partner=matchstr(getline(v:lnum), '\(' . virtuality . 'end\)\@<=[[:alpha:]]\+') + let ind = indent(searchpair(virtuality . partner, '', '^\s*end' . partner, 'bW', + \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? "sdlString"')) + endif + + return ind +endfunction + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim:sw=2 diff --git a/runtime/indent/sh.vim b/runtime/indent/sh.vim new file mode 100644 index 0000000..aa47c6d --- /dev/null +++ b/runtime/indent/sh.vim @@ -0,0 +1,303 @@ +" Vim indent file +" Language: Shell Script +" Maintainer: Christian Brabandt <cb@256bit.org> +" Original Author: Nikolai Weibull <now@bitwi.se> +" Previous Maintainer: Peter Aronoff <telemachus@arpinum.org> +" Latest Revision: 2019-10-24 +" License: Vim (see :h license) +" Repository: https://github.com/chrisbra/vim-sh-indent +" Changelog: +" 20190726 - Correctly skip if keywords in syntax comments +" (issue #17) +" 20190603 - Do not indent in zsh filetypes with an `if` in comments +" 20190428 - De-indent fi correctly when typing with +" https://github.com/chrisbra/vim-sh-indent/issues/15 +" 20190325 - Indent fi; correctly +" https://github.com/chrisbra/vim-sh-indent/issues/14 +" 20190319 - Indent arrays (only zsh and bash) +" https://github.com/chrisbra/vim-sh-indent/issues/13 +" 20190316 - Make use of searchpairpos for nested if sections +" fixes https://github.com/chrisbra/vim-sh-indent/issues/11 +" 20190201 - Better check for closing if sections +" 20180724 - make check for zsh syntax more rigid (needs word-boundaries) +" 20180326 - better support for line continuation +" 20180325 - better detection of function definitions +" 20180127 - better support for zsh complex commands +" 20170808: - better indent of line continuation +" 20170502: - get rid of buffer-shiftwidth function +" 20160912: - preserve indentation of here-doc blocks +" 20160627: - detect heredocs correctly +" 20160213: - detect function definition correctly +" 20160202: - use shiftwidth() function +" 20151215: - set b:undo_indent variable +" 20150728: - add foreach detection for zsh + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetShIndent() +setlocal indentkeys+=0=then,0=do,0=else,0=elif,0=fi,0=esac,0=done,0=end,),0=;;,0=;& +setlocal indentkeys+=0=fin,0=fil,0=fip,0=fir,0=fix +setlocal indentkeys-=:,0# +setlocal nosmartindent + +let b:undo_indent = 'setlocal indentexpr< indentkeys< smartindent<' + +if exists("*GetShIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +let s:sh_indent_defaults = { + \ 'default': function('shiftwidth'), + \ 'continuation-line': function('shiftwidth'), + \ 'case-labels': function('shiftwidth'), + \ 'case-statements': function('shiftwidth'), + \ 'case-breaks': 0 } + +function! s:indent_value(option) + let Value = exists('b:sh_indent_options') + \ && has_key(b:sh_indent_options, a:option) ? + \ b:sh_indent_options[a:option] : + \ s:sh_indent_defaults[a:option] + if type(Value) == type(function('type')) + return Value() + endif + return Value +endfunction + +function! GetShIndent() + let curline = getline(v:lnum) + let lnum = prevnonblank(v:lnum - 1) + if lnum == 0 + return 0 + endif + let line = getline(lnum) + + let pnum = prevnonblank(lnum - 1) + let pline = getline(pnum) + let ind = indent(lnum) + + " Check contents of previous lines + " should not apply to e.g. commented lines + if line =~ '^\s*\%(if\|then\|do\|else\|elif\|case\|while\|until\|for\|select\|foreach\)\>' || + \ (&ft is# 'zsh' && line =~ '^\s*\<\%(if\|then\|do\|else\|elif\|case\|while\|until\|for\|select\|foreach\)\>') + if !s:is_end_expression(line) + let ind += s:indent_value('default') + endif + elseif s:is_case_label(line, pnum) + if !s:is_case_ended(line) + let ind += s:indent_value('case-statements') + endif + " function definition + elseif s:is_function_definition(line) + if line !~ '}\s*\%(#.*\)\=$' + let ind += s:indent_value('default') + endif + " array (only works for zsh or bash) + elseif s:is_array(line) && line !~ ')\s*$' && (&ft is# 'zsh' || s:is_bash()) + let ind += s:indent_value('continuation-line') + " end of array + elseif curline =~ '^\s*)$' + let ind -= s:indent_value('continuation-line') + elseif s:is_continuation_line(line) + if pnum == 0 || !s:is_continuation_line(pline) + let ind += s:indent_value('continuation-line') + endif + elseif s:end_block(line) && !s:start_block(line) + let ind = indent(lnum) + elseif pnum != 0 && + \ s:is_continuation_line(pline) && + \ !s:end_block(curline) && + \ !s:is_end_expression(curline) + " only add indent, if line and pline is in the same block + let i = v:lnum + let ind2 = indent(s:find_continued_lnum(pnum)) + while !s:is_empty(getline(i)) && i > pnum + let i -= 1 + endw + if i == pnum + let ind += ind2 + else + let ind = ind2 + endif + endif + + let pine = line + " Check content of current line + let line = curline + " Current line is a endif line, so get indent from start of "if condition" line + " TODO: should we do the same for other "end" lines? + if curline =~ '^\s*\%(fi\);\?\s*\%(#.*\)\=$' + let ind = indent(v:lnum) + let previous_line = searchpair('\<if\>', '', '\<fi\>\zs', 'bnW', 'synIDattr(synID(line("."),col("."), 1),"name") =~? "comment\\|quote"') + if previous_line > 0 + let ind = indent(previous_line) + endif + elseif line =~ '^\s*\%(then\|do\|else\|elif\|done\|end\)\>' || s:end_block(line) + let ind -= s:indent_value('default') + elseif line =~ '^\s*esac\>' && s:is_case_empty(getline(v:lnum - 1)) + let ind -= s:indent_value('default') + elseif line =~ '^\s*esac\>' + let ind -= (s:is_case_label(pine, lnum) && s:is_case_ended(pine) ? + \ 0 : s:indent_value('case-statements')) + + \ s:indent_value('case-labels') + if s:is_case_break(pine) + let ind += s:indent_value('case-breaks') + endif + elseif s:is_case_label(line, lnum) + if s:is_case(pine) + let ind = indent(lnum) + s:indent_value('case-labels') + else + let ind -= (s:is_case_label(pine, lnum) && s:is_case_ended(pine) ? + \ 0 : s:indent_value('case-statements')) - + \ s:indent_value('case-breaks') + endif + elseif s:is_case_break(line) + let ind -= s:indent_value('case-breaks') + elseif s:is_here_doc(line) + let ind = 0 + " statements, executed within a here document. Keep the current indent + elseif match(map(synstack(v:lnum, 1), 'synIDattr(v:val, "name")'), '\c\mheredoc') > -1 + return indent(v:lnum) + elseif s:is_comment(line) && s:is_empty(getline(v:lnum-1)) + return indent(v:lnum) + endif + + return ind > 0 ? ind : 0 +endfunction + +function! s:is_continuation_line(line) + " Comment, cannot be a line continuation + if a:line =~ '^\s*#' + return 0 + else + " start-of-line + " \\ or && or || or | + " followed optionally by { or # + return a:line =~ '\%(\%(^\|[^\\]\)\\\|&&\|||\||\)' . + \ '\s*\({\s*\)\=\(#.*\)\=$' + endif +endfunction + +function! s:find_continued_lnum(lnum) + let i = a:lnum + while i > 1 && s:is_continuation_line(getline(i - 1)) + let i -= 1 + endwhile + return i +endfunction + +function! s:is_function_definition(line) + return a:line =~ '^\s*\<\k\+\>\s*()\s*{' || + \ a:line =~ '^\s*{' || + \ a:line =~ '^\s*function\s*\k\+\s*\%(()\)\?\s*{' +endfunction + +function! s:is_array(line) + return a:line =~ '^\s*\<\k\+\>=(' +endfunction + +function! s:is_case_label(line, pnum) + if a:line !~ '^\s*(\=.*)' + return 0 + endif + + if a:pnum > 0 + let pine = getline(a:pnum) + if !(s:is_case(pine) || s:is_case_ended(pine)) + return 0 + endif + endif + + let suffix = substitute(a:line, '^\s*(\=', "", "") + let nesting = 0 + let i = 0 + let n = strlen(suffix) + while i < n + let c = suffix[i] + let i += 1 + if c == '\\' + let i += 1 + elseif c == '(' + let nesting += 1 + elseif c == ')' + if nesting == 0 + return 1 + endif + let nesting -= 1 + endif + endwhile + return 0 +endfunction + +function! s:is_case(line) + return a:line =~ '^\s*case\>' +endfunction + +function! s:is_case_break(line) + return a:line =~ '^\s*;[;&]' +endfunction + +function! s:is_here_doc(line) + if a:line =~ '^\w\+$' + let here_pat = '<<-\?'. s:escape(a:line). '\$' + return search(here_pat, 'bnW') > 0 + endif + return 0 +endfunction + +function! s:is_case_ended(line) + return s:is_case_break(a:line) || a:line =~ ';[;&]\s*\%(#.*\)\=$' +endfunction + +function! s:is_case_empty(line) + if a:line =~ '^\s*$' || a:line =~ '^\s*#' + return s:is_case_empty(getline(v:lnum - 1)) + else + return a:line =~ '^\s*case\>' + endif +endfunction + +function! s:escape(pattern) + return '\V'. escape(a:pattern, '\\') +endfunction + +function! s:is_empty(line) + return a:line =~ '^\s*$' +endfunction + +function! s:end_block(line) + return a:line =~ '^\s*}' +endfunction + +function! s:start_block(line) + return a:line =~ '{\s*\(#.*\)\?$' +endfunction + +function! s:find_start_block(lnum) + let i = a:lnum + while i > 1 && !s:start_block(getline(i)) + let i -= 1 + endwhile + return i +endfunction + +function! s:is_comment(line) + return a:line =~ '^\s*#' +endfunction + +function! s:is_end_expression(line) + return a:line =~ '\<\%(fi\|esac\|done\|end\)\>\s*\%(#.*\)\=$' +endfunction + +function! s:is_bash() + return get(g:, 'is_bash', 0) || get(b:, 'is_bash', 0) +endfunction + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/indent/sml.vim b/runtime/indent/sml.vim new file mode 100644 index 0000000..a0b0c3e --- /dev/null +++ b/runtime/indent/sml.vim @@ -0,0 +1,220 @@ +" Vim indent file +" Language: SML +" Maintainer: Saikat Guha <sg266@cornell.edu> +" Hubert Chao <hc85@cornell.edu> +" Original OCaml Version: +" Jean-Francois Yuen <jfyuen@ifrance.com> +" Mike Leary <leary@nwlink.com> +" Markus Mottl <markus@oefai.at> +" OCaml URL: http://www.oefai.at/~markus/vim/indent/ocaml.vim +" Last Change: 2022 Apr 06 +" 2002 Nov 06 - Some fixes (JY) +" 2002 Oct 28 - Fixed bug with indentation of ']' (MM) +" 2002 Oct 22 - Major rewrite (JY) +" 2022 April: b:undo_indent added by Doug Kearns + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal expandtab +setlocal indentexpr=GetSMLIndent() +setlocal indentkeys+=0=and,0=else,0=end,0=handle,0=if,0=in,0=let,0=then,0=val,0=fun,0=\|,0=*),0) +setlocal nolisp +setlocal nosmartindent +setlocal textwidth=80 +setlocal shiftwidth=2 + +let b:undo_indent = "setl et< inde< indk< lisp< si< sw< tw<" + +" Comment formatting +if (has("comments")) + set comments=sr:(*,mb:*,ex:*) + set fo=cqort +endif + +" Only define the function once. +"if exists("*GetSMLIndent") +"finish +"endif + +" Define some patterns: +let s:beflet = '^\s*\(initializer\|method\|try\)\|\(\<\(begin\|do\|else\|in\|then\|try\)\|->\|;\)\s*$' +let s:letpat = '^\s*\(let\|type\|module\|class\|open\|exception\|val\|include\|external\)\>' +let s:letlim = '\(\<\(sig\|struct\)\|;;\)\s*$' +let s:lim = '^\s*\(exception\|external\|include\|let\|module\|open\|type\|val\)\>' +let s:module = '\<\%(let\|sig\|struct\)\>' +let s:obj = '^\s*\(constraint\|inherit\|initializer\|method\|val\)\>\|\<\(object\|object\s*(.*)\)\s*$' +let s:type = '^\s*\%(let\|type\)\>.*=' +let s:val = '^\s*\(val\|external\)\>.*:' + +" Skipping pattern, for comments +function! s:SkipPattern(lnum, pat) + let def = prevnonblank(a:lnum - 1) + while def > 0 && getline(def) =~ a:pat + let def = prevnonblank(def - 1) + endwhile + return def +endfunction + +" Indent for ';;' to match multiple 'let' +function! s:GetInd(lnum, pat, lim) + let llet = search(a:pat, 'bW') + let old = indent(a:lnum) + while llet > 0 + let old = indent(llet) + let nb = s:SkipPattern(llet, '^\s*(\*.*\*)\s*$') + if getline(nb) =~ a:lim + return old + endif + let llet = search(a:pat, 'bW') + endwhile + return old +endfunction + +" Indent pairs +function! s:FindPair(pstart, pmid, pend) + call search(a:pend, 'bW') +" return indent(searchpair(a:pstart, a:pmid, a:pend, 'bWn', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\\|comment"')) + let lno = searchpair(a:pstart, a:pmid, a:pend, 'bW', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\\|comment"') + if lno == -1 + return indent(lno) + else + return col(".") - 1 + endif +endfunction + +function! s:FindLet(pstart, pmid, pend) + call search(a:pend, 'bW') +" return indent(searchpair(a:pstart, a:pmid, a:pend, 'bWn', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\\|comment"')) + let lno = searchpair(a:pstart, a:pmid, a:pend, 'bW', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\\|comment"') + let moduleLine = getline(lno) + if lno == -1 || moduleLine =~ '^\s*\(fun\|structure\|signature\)\>' + return indent(lno) + else + return col(".") - 1 + endif +endfunction + +" Indent 'let' +"function! s:FindLet(pstart, pmid, pend) +" call search(a:pend, 'bW') +" return indent(searchpair(a:pstart, a:pmid, a:pend, 'bWn', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\\|comment" || getline(".") =~ "^\\s*let\\>.*=.*\\<in\\s*$" || getline(prevnonblank(".") - 1) =~ "^\\s*let\\>.*=\\s*$\\|" . s:beflet')) +"endfunction + +function! GetSMLIndent() + " Find a non-blank line above the current line. + let lnum = prevnonblank(v:lnum - 1) + + " At the start of the file use zero indent. + if lnum == 0 + return 0 + endif + + let ind = indent(lnum) + let lline = getline(lnum) + + " Return double 'shiftwidth' after lines matching: + if lline =~ '^\s*|.*=>\s*$' + return ind + 2 *shiftwidth() + elseif lline =~ '^\s*val\>.*=\s*$' + return ind + shiftwidth() + endif + + let line = getline(v:lnum) + + " Indent lines starting with 'end' to matching module + if line =~ '^\s*end\>' + return s:FindLet(s:module, '', '\<end\>') + + " Match 'else' with 'if' + elseif line =~ '^\s*else\>' + if lline !~ '^\s*\(if\|else\|then\)\>' + return s:FindPair('\<if\>', '', '\<then\>') + else + return ind + endif + + " Match 'then' with 'if' + elseif line =~ '^\s*then\>' + if lline !~ '^\s*\(if\|else\|then\)\>' + return s:FindPair('\<if\>', '', '\<then\>') + else + return ind + endif + + " Indent if current line begins with ']' + elseif line =~ '^\s*\]' + return s:FindPair('\[','','\]') + + " Indent current line starting with 'in' to last matching 'let' + elseif line =~ '^\s*in\>' + let ind = s:FindLet('\<let\>','','\<in\>') + + " Indent from last matching module if line matches: + elseif line =~ '^\s*\(fun\|val\|open\|structure\|and\|datatype\|type\|exception\)\>' + cursor(lnum,1) + let lastModule = indent(searchpair(s:module, '', '\<end\>', 'bWn', 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\\|comment"')) + if lastModule == -1 + return 0 + else + return lastModule + shiftwidth() + endif + + " Indent lines starting with '|' from matching 'case', 'handle' + elseif line =~ '^\s*|' + " cursor(lnum,1) + let lastSwitch = search('\<\(case\|handle\|fun\|datatype\)\>','bW') + let switchLine = getline(lastSwitch) + let switchLineIndent = indent(lastSwitch) + if lline =~ '^\s*|' + return ind + endif + if switchLine =~ '\<case\>' + return col(".") + 2 + elseif switchLine =~ '\<handle\>' + return switchLineIndent + shiftwidth() + elseif switchLine =~ '\<datatype\>' + call search('=') + return col(".") - 1 + else + return switchLineIndent + 2 + endif + + + " Indent if last line ends with 'sig', 'struct', 'let', 'then', 'else', + " 'in' + elseif lline =~ '\<\(sig\|struct\|let\|in\|then\|else\)\s*$' + let ind = ind + shiftwidth() + + " Indent if last line ends with 'of', align from 'case' + elseif lline =~ '\<\(of\)\s*$' + call search('\<case\>',"bW") + let ind = col(".")+4 + + " Indent if current line starts with 'of' + elseif line =~ '^\s*of\>' + call search('\<case\>',"bW") + let ind = col(".")+1 + + + " Indent if last line starts with 'fun', 'case', 'fn' + elseif lline =~ '^\s*\(fun\|fn\|case\)\>' + let ind = ind + shiftwidth() + + endif + + " Don't indent 'let' if last line started with 'fun', 'fn' + if line =~ '^\s*let\>' + if lline =~ '^\s*\(fun\|fn\)' + let ind = ind - shiftwidth() + endif + endif + + return ind + +endfunction + +" vim:sw=2 diff --git a/runtime/indent/solidity.vim b/runtime/indent/solidity.vim new file mode 100644 index 0000000..55a07c0 --- /dev/null +++ b/runtime/indent/solidity.vim @@ -0,0 +1,446 @@ +" Vim indent file +" Language: Solidity +" Maintainer: Cothi (jiungdev@gmail.com) +" Original Author: tomlion (https://github.com/tomlion/vim-solidity) +" Last Change: 2022 Sep 27 +" 2023 Aug 22 Vim Project (undo_indent) +" +" Acknowledgement: Based off of vim-javascript +" +" 0. Initialization {{{1 +" ================= + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal nosmartindent + +" Now, set up our indentation expression and keys that trigger it. +setlocal indentexpr=GetSolidityIndent() +setlocal indentkeys=0{,0},0),0],0\,,!^F,o,O,e + +let b:undo_indent = "setlocal indentexpr< indentkeys< smartindent<" + +" Only define the function once. +if exists("*GetSolidityIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +" 1. Variables {{{1 +" ============ + +let s:js_keywords = '^\s*\(break\|case\|catch\|continue\|debugger\|default\|delete\|do\|else\|finally\|for\|function\|if\|in\|instanceof\|new\|return\|switch\|this\|throw\|try\|typeof\|var\|void\|while\|with\)' + +" Regex of syntax group names that are or delimit string or are comments. +let s:syng_strcom = 'string\|regex\|comment\c' + +" Regex of syntax group names that are strings. +let s:syng_string = 'regex\c' + +" Regex of syntax group names that are strings or documentation. +let s:syng_multiline = 'comment\c' + +" Regex of syntax group names that are line comment. +let s:syng_linecom = 'linecomment\c' + +" Expression used to check whether we should skip a match with searchpair(). +let s:skip_expr = "synIDattr(synID(line('.'),col('.'),1),'name') =~ '".s:syng_strcom."'" + +let s:line_term = '\s*\%(\%(\/\/\).*\)\=$' + +" Regex that defines continuation lines, not including (, {, or [. +let s:continuation_regex = '\%([\\*+/.:]\|\%(<%\)\@<![=-]\|\W[|&?]\|||\|&&\)' . s:line_term + +" Regex that defines continuation lines. +" TODO: this needs to deal with if ...: and so on +let s:msl_regex = '\%([\\*+/.:([]\|\%(<%\)\@<![=-]\|\W[|&?]\|||\|&&\)' . s:line_term + +let s:one_line_scope_regex = '\<\%(if\|else\|for\|while\)\>[^{;]*' . s:line_term + +" Regex that defines blocks. +let s:block_regex = '\%([{[]\)\s*\%(|\%([*@]\=\h\w*,\=\s*\)\%(,\s*[*@]\=\h\w*\)*|\)\=' . s:line_term + +let s:var_stmt = '^\s*var' + +let s:comma_first = '^\s*,' +let s:comma_last = ',\s*$' + +let s:ternary = '^\s\+[?|:]' +let s:ternary_q = '^\s\+?' + +" 2. Auxiliary Functions {{{1 +" ====================== + +" Check if the character at lnum:col is inside a string, comment, or is ascii. +function s:IsInStringOrComment(lnum, col) + return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_strcom +endfunction + +" Check if the character at lnum:col is inside a string. +function s:IsInString(lnum, col) + return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_string +endfunction + +" Check if the character at lnum:col is inside a multi-line comment. +function s:IsInMultilineComment(lnum, col) + return !s:IsLineComment(a:lnum, a:col) && synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_multiline +endfunction + +" Check if the character at lnum:col is a line comment. +function s:IsLineComment(lnum, col) + return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_linecom +endfunction + +" Find line above 'lnum' that isn't empty, in a comment, or in a string. +function s:PrevNonBlankNonString(lnum) + let in_block = 0 + let lnum = prevnonblank(a:lnum) + while lnum > 0 + " Go in and out of blocks comments as necessary. + " If the line isn't empty (with opt. comment) or in a string, end search. + let line = getline(lnum) + if line =~ '/\*' + if in_block + let in_block = 0 + else + break + endif + elseif !in_block && line =~ '\*/' + let in_block = 1 + elseif !in_block && line !~ '^\s*\%(//\).*$' && !(s:IsInStringOrComment(lnum, 1) && s:IsInStringOrComment(lnum, strlen(line))) + break + endif + let lnum = prevnonblank(lnum - 1) + endwhile + return lnum +endfunction + +" Find line above 'lnum' that started the continuation 'lnum' may be part of. +function s:GetMSL(lnum, in_one_line_scope) + " Start on the line we're at and use its indent. + let msl = a:lnum + let lnum = s:PrevNonBlankNonString(a:lnum - 1) + while lnum > 0 + " If we have a continuation line, or we're in a string, use line as MSL. + " Otherwise, terminate search as we have found our MSL already. + let line = getline(lnum) + let col = match(line, s:msl_regex) + 1 + if (col > 0 && !s:IsInStringOrComment(lnum, col)) || s:IsInString(lnum, strlen(line)) + let msl = lnum + else + " Don't use lines that are part of a one line scope as msl unless the + " flag in_one_line_scope is set to 1 + " + if a:in_one_line_scope + break + end + let msl_one_line = s:Match(lnum, s:one_line_scope_regex) + if msl_one_line == 0 + break + endif + endif + let lnum = s:PrevNonBlankNonString(lnum - 1) + endwhile + return msl +endfunction + +function s:RemoveTrailingComments(content) + let single = '\/\/\(.*\)\s*$' + let multi = '\/\*\(.*\)\*\/\s*$' + return substitute(substitute(a:content, single, '', ''), multi, '', '') +endfunction + +" Find if the string is inside var statement (but not the first string) +function s:InMultiVarStatement(lnum) + let lnum = s:PrevNonBlankNonString(a:lnum - 1) + +" let type = synIDattr(synID(lnum, indent(lnum) + 1, 0), 'name') + + " loop through previous expressions to find a var statement + while lnum > 0 + let line = getline(lnum) + + " if the line is a js keyword + if (line =~ s:js_keywords) + " check if the line is a var stmt + " if the line has a comma first or comma last then we can assume that we + " are in a multiple var statement + if (line =~ s:var_stmt) + return lnum + endif + + " other js keywords, not a var + return 0 + endif + + let lnum = s:PrevNonBlankNonString(lnum - 1) + endwhile + + " beginning of program, not a var + return 0 +endfunction + +" Find line above with beginning of the var statement or returns 0 if it's not +" this statement +function s:GetVarIndent(lnum) + let lvar = s:InMultiVarStatement(a:lnum) + let prev_lnum = s:PrevNonBlankNonString(a:lnum - 1) + + if lvar + let line = s:RemoveTrailingComments(getline(prev_lnum)) + + " if the previous line doesn't end in a comma, return to regular indent + if (line !~ s:comma_last) + return indent(prev_lnum) - &sw + else + return indent(lvar) + &sw + endif + endif + + return -1 +endfunction + + +" Check if line 'lnum' has more opening brackets than closing ones. +function s:LineHasOpeningBrackets(lnum) + let open_0 = 0 + let open_2 = 0 + let open_4 = 0 + let line = getline(a:lnum) + let pos = match(line, '[][(){}]', 0) + while pos != -1 + if !s:IsInStringOrComment(a:lnum, pos + 1) + let idx = stridx('(){}[]', line[pos]) + if idx % 2 == 0 + let open_{idx} = open_{idx} + 1 + else + let open_{idx - 1} = open_{idx - 1} - 1 + endif + endif + let pos = match(line, '[][(){}]', pos + 1) + endwhile + return (open_0 > 0) . (open_2 > 0) . (open_4 > 0) +endfunction + +function s:Match(lnum, regex) + let col = match(getline(a:lnum), a:regex) + 1 + return col > 0 && !s:IsInStringOrComment(a:lnum, col) ? col : 0 +endfunction + +function s:IndentWithContinuation(lnum, ind, width) + " Set up variables to use and search for MSL to the previous line. + let p_lnum = a:lnum + let lnum = s:GetMSL(a:lnum, 1) + let line = getline(lnum) + + " If the previous line wasn't a MSL and is continuation return its indent. + " TODO: the || s:IsInString() thing worries me a bit. + if p_lnum != lnum + if s:Match(p_lnum,s:continuation_regex)||s:IsInString(p_lnum,strlen(line)) + return a:ind + endif + endif + + " Set up more variables now that we know we aren't continuation bound. + let msl_ind = indent(lnum) + + " If the previous line ended with [*+/.-=], start a continuation that + " indents an extra level. + if s:Match(lnum, s:continuation_regex) + if lnum == p_lnum + return msl_ind + a:width + else + return msl_ind + endif + endif + + return a:ind +endfunction + +function s:InOneLineScope(lnum) + let msl = s:GetMSL(a:lnum, 1) + if msl > 0 && s:Match(msl, s:one_line_scope_regex) + return msl + endif + return 0 +endfunction + +function s:ExitingOneLineScope(lnum) + let msl = s:GetMSL(a:lnum, 1) + if msl > 0 + " if the current line is in a one line scope .. + if s:Match(msl, s:one_line_scope_regex) + return 0 + else + let prev_msl = s:GetMSL(msl - 1, 1) + if s:Match(prev_msl, s:one_line_scope_regex) + return prev_msl + endif + endif + endif + return 0 +endfunction + +" 3. GetSolidityIndent Function {{{1 +" ========================= + +function GetSolidityIndent() + " 3.1. Setup {{{2 + " ---------- + + " Set up variables for restoring position in file. Could use v:lnum here. + let vcol = col('.') + + " 3.2. Work on the current line {{{2 + " ----------------------------- + + let ind = -1 + " Get the current line. + let line = getline(v:lnum) + " previous nonblank line number + let prevline = prevnonblank(v:lnum - 1) + + " If we got a closing bracket on an empty line, find its match and indent + " according to it. For parentheses we indent to its column - 1, for the + " others we indent to the containing line's MSL's level. Return -1 if fail. + let col = matchend(line, '^\s*[],})]') + if col > 0 && !s:IsInStringOrComment(v:lnum, col) + call cursor(v:lnum, col) + + let lvar = s:InMultiVarStatement(v:lnum) + if lvar + let prevline_contents = s:RemoveTrailingComments(getline(prevline)) + + " check for comma first + if (line[col - 1] =~ ',') + " if the previous line ends in comma or semicolon don't indent + if (prevline_contents =~ '[;,]\s*$') + return indent(s:GetMSL(line('.'), 0)) + " get previous line indent, if it's comma first return prevline indent + elseif (prevline_contents =~ s:comma_first) + return indent(prevline) + " otherwise we indent 1 level + else + return indent(lvar) + &sw + endif + endif + endif + + + let bs = strpart('(){}[]', stridx(')}]', line[col - 1]) * 2, 2) + if searchpair(escape(bs[0], '\['), '', bs[1], 'bW', s:skip_expr) > 0 + if line[col-1]==')' && col('.') != col('$') - 1 + let ind = virtcol('.')-1 + else + let ind = indent(s:GetMSL(line('.'), 0)) + endif + endif + return ind + endif + + " If the line is comma first, dedent 1 level + if (getline(prevline) =~ s:comma_first) + return indent(prevline) - &sw + endif + + if (line =~ s:ternary) + if (getline(prevline) =~ s:ternary_q) + return indent(prevline) + else + return indent(prevline) + &sw + endif + endif + + " If we are in a multi-line comment, cindent does the right thing. + if s:IsInMultilineComment(v:lnum, 1) && !s:IsLineComment(v:lnum, 1) + return cindent(v:lnum) + endif + + " Check for multiple var assignments +" let var_indent = s:GetVarIndent(v:lnum) +" if var_indent >= 0 +" return var_indent +" endif + + " 3.3. Work on the previous line. {{{2 + " ------------------------------- + + " If the line is empty and the previous nonblank line was a multi-line + " comment, use that comment's indent. Deduct one char to account for the + " space in ' */'. + if line =~ '^\s*$' && s:IsInMultilineComment(prevline, 1) + return indent(prevline) - 1 + endif + + " Find a non-blank, non-multi-line string line above the current line. + let lnum = s:PrevNonBlankNonString(v:lnum - 1) + + " If the line is empty and inside a string, use the previous line. + if line =~ '^\s*$' && lnum != prevline + return indent(prevnonblank(v:lnum)) + endif + + " At the start of the file use zero indent. + if lnum == 0 + return 0 + endif + + " Set up variables for current line. + let line = getline(lnum) + let ind = indent(lnum) + + " If the previous line ended with a block opening, add a level of indent. + if s:Match(lnum, s:block_regex) + return indent(s:GetMSL(lnum, 0)) + &sw + endif + + " If the previous line contained an opening bracket, and we are still in it, + " add indent depending on the bracket type. + if line =~ '[[({]' + let counts = s:LineHasOpeningBrackets(lnum) + if counts[0] == '1' && searchpair('(', '', ')', 'bW', s:skip_expr) > 0 + if col('.') + 1 == col('$') + return ind + &sw + else + return virtcol('.') + endif + elseif counts[1] == '1' || counts[2] == '1' + return ind + &sw + else + call cursor(v:lnum, vcol) + end + endif + + " 3.4. Work on the MSL line. {{{2 + " -------------------------- + + let ind_con = ind + let ind = s:IndentWithContinuation(lnum, ind_con, &sw) + + " }}}2 + " + " + let ols = s:InOneLineScope(lnum) + if ols > 0 + let ind = ind + &sw + else + let ols = s:ExitingOneLineScope(lnum) + while ols > 0 && ind > 0 + let ind = ind - &sw + let ols = s:InOneLineScope(ols - 1) + endwhile + endif + + return ind +endfunction + +" }}}1 + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/indent/sql.vim b/runtime/indent/sql.vim new file mode 100644 index 0000000..4f82b96 --- /dev/null +++ b/runtime/indent/sql.vim @@ -0,0 +1,39 @@ +" Vim indent file loader +" Language: SQL +" Maintainer: David Fishburn <fishburn at ianywhere dot com> +" Last Change: Thu Sep 15 2005 10:27:51 AM +" Version: 1.0 +" Download: http://vim.sourceforge.net/script.php?script_id=495 + +" Description: Checks for a: +" buffer local variable, +" global variable, +" If the above exist, it will source the type specified. +" If none exist, it will source the default sqlanywhere.vim file. + + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +" Default to the standard Vim distribution file +let filename = 'sqlanywhere' + +" Check for overrides. Buffer variables have the highest priority. +if exists("b:sql_type_override") + " Check the runtimepath to see if the file exists + if globpath(&runtimepath, 'indent/'.b:sql_type_override.'.vim') != '' + let filename = b:sql_type_override + endif +elseif exists("g:sql_type_default") + if globpath(&runtimepath, 'indent/'.g:sql_type_default.'.vim') != '' + let filename = g:sql_type_default + endif +endif + +" Source the appropriate file +exec 'runtime indent/'.filename.'.vim' + + +" vim:sw=4: diff --git a/runtime/indent/sqlanywhere.vim b/runtime/indent/sqlanywhere.vim new file mode 100644 index 0000000..4772b59 --- /dev/null +++ b/runtime/indent/sqlanywhere.vim @@ -0,0 +1,399 @@ +" 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 diff --git a/runtime/indent/sshconfig.vim b/runtime/indent/sshconfig.vim new file mode 100644 index 0000000..b456a9e --- /dev/null +++ b/runtime/indent/sshconfig.vim @@ -0,0 +1,34 @@ +" Vim indent file +" Language: ssh config file +" Maintainer: JasonKim <git@jasonk.me> +" Last Change: 2020 May 16 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal autoindent +setlocal indentexpr=GetSshconfigIndent(v:lnum) +setlocal indentkeys=o,O,*<Return>,0=~host\ ,0=~match\ ,0#,!^F + +let b:undo_indent = "setlocal autoindent< indentexpr< indentkeys<" + +if exists("*GetSshconfigIndent") + finish +endif + +function GetSshconfigIndent(lnum) + let sw = shiftwidth() + let prev_lnum = prevnonblank(a:lnum - 1) + let curr_lnum = a:lnum + let prev_line = getline(prev_lnum) + let curr_line = getline(curr_lnum) + if curr_line =~? '^\s*\(host\|match\)\s' + return 0 + elseif prev_line =~? '^\s*\(host\|match\)\s' + return sw + else + return indent(prev_lnum) + endif +endfunction diff --git a/runtime/indent/systemverilog.vim b/runtime/indent/systemverilog.vim new file mode 100644 index 0000000..42a05a0 --- /dev/null +++ b/runtime/indent/systemverilog.vim @@ -0,0 +1,279 @@ +" Vim indent file +" Language: SystemVerilog +" Maintainer: kocha <kocha.lsifrontend@gmail.com> +" Last Change: 05-Feb-2017 by Bilal Wasim +" 03-Aug-2022 Improved indent + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=SystemVerilogIndent() +setlocal indentkeys=!^F,o,O,0),0},=begin,=end,=join,=endcase,=join_any,=join_none +setlocal indentkeys+==endmodule,=endfunction,=endtask,=endspecify +setlocal indentkeys+==endclass,=endpackage,=endsequence,=endclocking +setlocal indentkeys+==endinterface,=endgroup,=endprogram,=endproperty,=endchecker +setlocal indentkeys+==`else,=`elsif,=`endif + +let b:undo_indent = "setl inde< indk<" + +" Only define the function once. +if exists("*SystemVerilogIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +let s:multiple_comment = 0 +let s:open_statement = 0 + +function SystemVerilogIndent() + + if exists('b:systemverilog_indent_width') + let offset = b:systemverilog_indent_width + else + let offset = shiftwidth() + endif + if exists('b:systemverilog_indent_modules') + let indent_modules = offset + else + let indent_modules = 0 + endif + + if exists('b:systemverilog_indent_ifdef_off') + let indent_ifdef = 0 + else + let indent_ifdef = 1 + endif + + " Find a non-blank line above the current line. + let lnum = prevnonblank(v:lnum - 1) + + " At the start of the file use zero indent. + if lnum == 0 + return 0 + endif + + let lnum2 = prevnonblank(lnum - 1) + let curr_line = getline(v:lnum) + let last_line = getline(lnum) + let last_line2 = getline(lnum2) + let ind = indent(lnum) + let ind2 = indent(lnum - 1) + " Define the condition of an open statement + " Exclude the match of //, /* or */ + let sv_openstat = '\(\<or\>\|\([*/]\)\@<![*(,{><+-/%^&|!=?:]\([*/]\)\@!\)' + " Define the condition when the statement ends with a one-line comment + let sv_comment = '\(//.*\|/\*.*\*/\s*\)' + if exists('b:systemverilog_indent_verbose') + let vverb_str = 'INDENT VERBOSE: '. v:lnum .":" + let vverb = 1 + else + let vverb = 0 + endif + + " Multiple-line comment count + if curr_line =~ '^\s*/\*' && curr_line !~ '/\*.\{-}\*/' + let s:multiple_comment += 1 + if vverb | echom vverb_str "Start of multiple-line comment" | endif + elseif curr_line =~ '\*/\s*$' && curr_line !~ '/\*.\{-}\*/' + let s:multiple_comment -= 1 + if vverb | echom vverb_str "End of multiple-line comment" | endif + return ind + endif + " Maintain indentation during commenting. + if s:multiple_comment > 0 + return ind + endif + + " Indent after if/else/for/case/always/initial/specify/fork blocks + if last_line =~ '^\s*\(end\)\=\s*`\@<!\<\(if\|else\)\>' || + \ last_line =~ '^\s*\<\(for\|while\|repeat\|case\%[[zx]]\|do\|foreach\|forever\|randcase\)\>' || + \ last_line =~ '^\s*\<\(always\|always_comb\|always_ff\|always_latch\)\>' || + \ last_line =~ '^\s*\<\(initial\|specify\|fork\|final\)\>' + if last_line !~ '\(;\|\<end\>\|\*/\)\s*' . sv_comment . '*$' || + \ last_line =~ '\(//\|/\*\).*\(;\|\<end\>\)\s*' . sv_comment . '*$' + let ind = ind + offset + if vverb | echom vverb_str "Indent after a block statement." | endif + endif + " Indent after function/task/class/package/sequence/clocking/ + " interface/covergroup/property/checkerprogram blocks + elseif last_line =~ '^\s*\<\(function\|task\|class\|package\)\>' || + \ last_line =~ '^\s*\<\(sequence\|clocking\|interface\)\>' || + \ last_line =~ '^\s*\(\w\+\s*:\)\=\s*\<covergroup\>' || + \ last_line =~ '^\s*\<\(property\|checker\|program\)\>' || + \ ( last_line =~ '^\s*\<virtual\>' && last_line =~ '\<\(function\|task\|class\|interface\)\>' ) || + \ ( last_line =~ '^\s*\<pure\>' && last_line =~ '\<virtual\>' && last_line =~ '\<\(function\|task\)\>' ) + if last_line !~ '\<end\>\s*' . sv_comment . '*$' || + \ last_line =~ '\(//\|/\*\).*\(;\|\<end\>\)\s*' . sv_comment . '*$' + let ind = ind + offset + if vverb + echom vverb_str "Indent after function/task/class block statement." + endif + endif + + " Indent after module/function/task/specify/fork blocks + elseif last_line =~ '^\s*\(\<extern\>\s*\)\=\<module\>' + let ind = ind + indent_modules + if vverb && indent_modules + echom vverb_str "Indent after module statement." + endif + if last_line =~ '[(,]\s*' . sv_comment . '*$' && + \ last_line !~ '\(//\|/\*\).*[(,]\s*' . sv_comment . '*$' + let ind = ind + offset + if vverb + echom vverb_str "Indent after a multiple-line module statement." + endif + endif + + " Indent after a 'begin' statement + elseif last_line =~ '\(\<begin\>\)\(\s*:\s*\w\+\)*' . sv_comment . '*$' && + \ last_line !~ '\(//\|/\*\).*\(\<begin\>\)' && + \ ( last_line2 !~ sv_openstat . '\s*' . sv_comment . '*$' || + \ last_line2 =~ '^\s*[^=!]\+\s*:\s*' . sv_comment . '*$' ) + let ind = ind + offset + if vverb | echom vverb_str "Indent after begin statement." | endif + + " Indent after a '{' or a '(' + elseif last_line =~ '[{(]' . sv_comment . '*$' && + \ last_line !~ '\(//\|/\*\).*[{(]' && + \ ( last_line2 !~ sv_openstat . '\s*' . sv_comment . '*$' || + \ last_line2 =~ '^\s*[^=!]\+\s*:\s*' . sv_comment . '*$' ) + let ind = ind + offset + if vverb | echom vverb_str "Indent after begin statement." | endif + + " Ignore de-indent for the end of one-line block + elseif ( last_line !~ '\<begin\>' || + \ last_line =~ '\(//\|/\*\).*\<begin\>' ) && + \ last_line2 =~ '\<\(`\@<!if\|`\@<!else\|for\|always\|initial\|do\|foreach\|forever\|final\)\>.*' . + \ sv_comment . '*$' && + \ last_line2 !~ '\(//\|/\*\).*\<\(`\@<!if\|`\@<!else\|for\|always\|initial\|do\|foreach\|forever\|final\)\>' && + \ last_line2 !~ sv_openstat . '\s*' . sv_comment . '*$' && + \ ( last_line2 !~ '\<begin\>' || + \ last_line2 =~ '\(//\|/\*\).*\<begin\>' ) && + \ last_line2 =~ ')*\s*;\s*' . sv_comment . '*$' + if vverb + echom vverb_str "Ignore de-indent after the end of one-line statement." + endif + + " De-indent for the end of one-line block + elseif ( last_line !~ '\<begin\>' || + \ last_line =~ '\(//\|/\*\).*\<begin\>' ) && + \ last_line2 =~ '\<\(`\@<!if\|`\@<!else\|for\|always\|initial\|do\|foreach\|forever\|final\)\>.*' . + \ sv_comment . '*$' && + \ last_line2 !~ '\(//\|/\*\).*\<\(`\@<!if\|`\@<!else\|for\|always\|initial\|do\|foreach\|forever\|final\)\>' && + \ last_line2 !~ sv_openstat . '\s*' . sv_comment . '*$' && + \ last_line2 !~ '\(;\|\<end\>\|\*/\)\s*' . sv_comment . '*$' && + \ ( last_line2 !~ '\<begin\>' || + \ last_line2 =~ '\(//\|/\*\).*\<begin\>' ) + let ind = ind - offset + if vverb + echom vverb_str "De-indent after the end of one-line statement." + endif + + " Multiple-line statement (including case statement) + " Open statement + " Ident the first open line + elseif last_line =~ sv_openstat . '\s*' . sv_comment . '*$' && + \ last_line !~ '\(//\|/\*\).*' . sv_openstat . '\s*$' && + \ last_line2 !~ sv_openstat . '\s*' . sv_comment . '*$' + let ind = ind + offset + let s:open_statement = 1 + if vverb | echom vverb_str "Indent after an open statement." | endif + + " `ifdef or `ifndef or `elsif or `else + elseif last_line =~ '^\s*`\<\(ifn\?def\|elsif\|else\)\>' && indent_ifdef + let ind = ind + offset + if vverb + echom vverb_str "Indent after a `ifdef or `ifndef or `elsif or `else statement." + endif + + endif + + " Re-indent current line + + " De-indent on the end of the block + " join/end/endcase/endfunction/endtask/endspecify + if curr_line =~ '^\s*\<\(join\|join_any\|join_none\|\|end\|endcase\)\>' || + \ curr_line =~ '^\s*\<\(endfunction\|endtask\|endspecify\|endclass\)\>' || + \ curr_line =~ '^\s*\<\(endpackage\|endsequence\|endclocking\|endinterface\)\>' || + \ curr_line =~ '^\s*\<\(endgroup\|endproperty\|endchecker\|endprogram\)\>' + let ind = ind - offset + if vverb | echom vverb_str "De-indent the end of a block." | endif + if s:open_statement == 1 + let ind = ind - offset + let s:open_statement = 0 + if vverb | echom vverb_str "De-indent the close statement." | endif + endif + elseif curr_line =~ '^\s*\<endmodule\>' + let ind = ind - indent_modules + if vverb && indent_modules + echom vverb_str "De-indent the end of a module." + endif + + " De-indent on a stand-alone 'begin' + elseif curr_line =~ '^\s*\<begin\>' + if last_line !~ '^\s*\<\(function\|task\|specify\|module\|class\|package\)\>' || + \ last_line !~ '^\s*\<\(sequence\|clocking\|interface\|covergroup\)\>' || + \ last_line !~ '^\s*\<\(property\|checker\|program\)\>' && + \ last_line !~ '^\s*\()*\s*;\|)\+\)\s*' . sv_comment . '*$' && + \ ( last_line =~ + \ '\<\(`\@<!if\|`\@<!else\|for\|case\%[[zx]]\|always\|initial\|do\|foreach\|forever\|randcase\|final\)\>' || + \ last_line =~ ')\s*' . sv_comment . '*$' || + \ last_line =~ sv_openstat . '\s*' . sv_comment . '*$' ) + let ind = ind - offset + if vverb + echom vverb_str "De-indent a stand alone begin statement." + endif + if s:open_statement == 1 + let ind = ind - offset + let s:open_statement = 0 + if vverb | echom vverb_str "De-indent the close statement." | endif + endif + endif + + " " Close statement + " " De-indent for an optional close parenthesis and a semicolon, and only + " " if there exists precedent non-whitespace char + " elseif last_line =~ ')*\s*;\s*' . sv_comment . '*$' && + " \ last_line !~ '^\s*)*\s*;\s*' . sv_comment . '*$' && + " \ last_line !~ '\(//\|/\*\).*\S)*\s*;\s*' . sv_comment . '*$' && + " \ ( last_line2 =~ sv_openstat . '\s*' . sv_comment . '*$' && + " \ last_line2 !~ ';\s*//.*$') && + " \ last_line2 !~ '^\s*' . sv_comment . '$' + " let ind = ind - offset + " if vverb | echom vverb_str "De-indent after a close statement." | endif + + " " De-indent after the end of multiple-line statement + " elseif curr_line =~ '^\s*)' && + " \ ( last_line =~ sv_openstat . '\s*' . sv_comment . '*$' || + " \ last_line !~ sv_openstat . '\s*' . sv_comment . '*$' && + " \ last_line2 =~ sv_openstat . '\s*' . sv_comment . '*$' ) + " let ind = ind - offset + " if vverb + " echom vverb_str "De-indent the end of a multiple statement." + " endif + + " De-indent `elsif or `else or `endif + elseif curr_line =~ '^\s*`\<\(elsif\|else\|endif\)\>' && indent_ifdef + let ind = ind - offset + if vverb | echom vverb_str "De-indent `elsif or `else or `endif statement." | endif + if b:systemverilog_open_statement == 1 + let ind = ind - offset + let b:systemverilog_open_statement = 0 + if vverb | echom vverb_str "De-indent the open statement." | endif + endif + endif + + " Return the indentation + return ind +endfunction + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim:sw=2 + diff --git a/runtime/indent/tcl.vim b/runtime/indent/tcl.vim new file mode 100644 index 0000000..c35150d --- /dev/null +++ b/runtime/indent/tcl.vim @@ -0,0 +1,103 @@ +" Vim indent file +" Language: Tcl +" Maintainer: Chris Heithoff <chrisheithoff@gmail.com> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Last Change: 24 Sep 2021 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetTclIndent() +setlocal indentkeys=0{,0},!^F,o,O,0] +setlocal nosmartindent + +let b:undo_indent = "setl inde< indk< si<" + +if exists("*GetTclIndent") + finish +endif + +function s:prevnonblanknoncomment(lnum) + let lnum = prevnonblank(a:lnum) + while lnum > 0 + let line = getline(lnum) + if line !~ '^\s*\(#\|$\)' + break + endif + let lnum = prevnonblank(lnum - 1) + endwhile + return lnum +endfunction + +function s:ends_with_backslash(lnum) + let line = getline(a:lnum) + if line =~ '\\\s*$' + return 1 + else + return 0 + endif +endfunction + +function s:count_braces(lnum, count_open) + let n_open = 0 + let n_close = 0 + let line = getline(a:lnum) + let pattern = '[{}]' + let i = match(line, pattern) + while i != -1 + if synIDattr(synID(a:lnum, i + 1, 0), 'name') !~ 'tcl\%(Comment\|String\)' + if line[i] == '{' + let n_open += 1 + elseif line[i] == '}' + if n_open > 0 + let n_open -= 1 + else + let n_close += 1 + endif + endif + endif + let i = match(line, pattern, i + 1) + endwhile + return a:count_open ? n_open : n_close +endfunction + +function GetTclIndent() + let line = getline(v:lnum) + + " Get the line number of the previous non-blank or non-comment line. + let pnum = s:prevnonblanknoncomment(v:lnum - 1) + if pnum == 0 + return 0 + endif + + " ..and the previous line before the previous line. + let pnum2 = s:prevnonblanknoncomment(pnum-1) + + " Default indentation is to preserve the previous indentation. + let ind = indent(pnum) + + " ...but if previous line introduces an open brace, then increase current line's indentation + if s:count_braces(pnum, 1) > 0 + let ind += shiftwidth() + else + " Look for backslash line continuation on the previous two lines. + let slash1 = s:ends_with_backslash(pnum) + let slash2 = s:ends_with_backslash(pnum2) + if slash1 && !slash2 + " If the previous line begins a line continuation. + let ind += shiftwidth() + elseif !slash1 && slash2 + " If two lines ago was the end of a line continuation group of lines. + let ind -= shiftwidth() + endif + endif + + " If the current line begins with a closed brace, then decrease the indentation by one. + if line =~ '^\s*}' + let ind -= shiftwidth() + endif + + return ind +endfunction diff --git a/runtime/indent/tcsh.vim b/runtime/indent/tcsh.vim new file mode 100644 index 0000000..93d96e7 --- /dev/null +++ b/runtime/indent/tcsh.vim @@ -0,0 +1,53 @@ +" Vim indent file +" Language: C-shell (tcsh) +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Previous Maintainer: Gautam Iyer <gi1242+vim@NoSpam.com> where NoSpam=gmail (Original Author) +" Last Change: 2021 Oct 15 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +let b:did_indent = 1 + +setlocal indentexpr=TcshGetIndent() +setlocal indentkeys+=e,0=end +setlocal indentkeys-=0{,0},0),:,0# + +let b:undo_indent = "setl inde< indk<" + +" Only define the function once. +if exists("*TcshGetIndent") + finish +endif + +function TcshGetIndent() + " Find a non-blank line above the current line. + let lnum = prevnonblank(v:lnum - 1) + + " Hit the start of the file, use zero indent. + if lnum == 0 + return 0 + endif + + " Add indent if previous line begins with while or foreach + " OR line ends with case <str>:, default:, else, then or \ + let ind = indent(lnum) + let line = getline(lnum) + if line =~ '\v^\s*%(while|foreach)>|^\s*%(case\s.*:|default:|else)\s*$|%(<then|\\)$' + let ind = ind + shiftwidth() + endif + + if line =~ '\v^\s*breaksw>' + let ind = ind - shiftwidth() + endif + + " Subtract indent if current line has on end, endif, endsw, case commands + let line = getline(v:lnum) + if line =~ '\v^\s*%(else|end|endif|endsw)\s*$' + let ind = ind - shiftwidth() + endif + + return ind +endfunction diff --git a/runtime/indent/teraterm.vim b/runtime/indent/teraterm.vim new file mode 100644 index 0000000..181c9a3 --- /dev/null +++ b/runtime/indent/teraterm.vim @@ -0,0 +1,57 @@ +" Vim indent file +" Language: Tera Term Language (TTL) +" Based on Tera Term Version 4.100 +" Maintainer: Ken Takata +" URL: https://github.com/k-takata/vim-teraterm +" Last Change: 2021-10-18 +" Filenames: *.ttl +" License: VIM License + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal nosmartindent +setlocal noautoindent +setlocal indentexpr=GetTeraTermIndent(v:lnum) +setlocal indentkeys=!^F,o,O,e +setlocal indentkeys+==elseif,=endif,=loop,=next,=enduntil,=endwhile + +let b:undo_indent = "setl ai< inde< indk< si<" + +if exists("*GetTeraTermIndent") + finish +endif + +function! GetTeraTermIndent(lnum) + let l:prevlnum = prevnonblank(a:lnum-1) + if l:prevlnum == 0 + " top of file + return 0 + endif + + " grab the previous and current line, stripping comments. + let l:prevl = substitute(getline(l:prevlnum), ';.*$', '', '') + let l:thisl = substitute(getline(a:lnum), ';.*$', '', '') + let l:previ = indent(l:prevlnum) + + let l:ind = l:previ + + if l:prevl =~ '^\s*if\>.*\<then\>' + " previous line opened a block + let l:ind += shiftwidth() + endif + if l:prevl =~ '^\s*\%(elseif\|else\|do\|until\|while\|for\)\>' + " previous line opened a block + let l:ind += shiftwidth() + endif + if l:thisl =~ '^\s*\%(elseif\|else\|endif\|enduntil\|endwhile\|loop\|next\)\>' + " this line closed a block + let l:ind -= shiftwidth() + endif + + return l:ind +endfunction + +" vim: ts=8 sw=2 sts=2 diff --git a/runtime/indent/testdir/README.txt b/runtime/indent/testdir/README.txt new file mode 100644 index 0000000..6597560 --- /dev/null +++ b/runtime/indent/testdir/README.txt @@ -0,0 +1,97 @@ +TESTING INDENT SCRIPTS + +We'll use FILETYPE for the filetype name here. + + +FORMAT OF THE FILETYPE.IN FILE + +First of all, create a FILETYPE.in file. It should contain: + +- A modeline setting the 'filetype' and any other option values. + This must work like a comment for FILETYPE. E.g. for vim: + " vim: set ft=vim sw=4 : + +- At least one block of lines to indent, prefixed with START_INDENT and + followed by END_INDENT. These lines must also look like a comment for your + FILETYPE. You would normally leave out all indent, so that the effect of + the indent command results in adding indent. Example: + + " START_INDENT + func Some() + let x = 1 + endfunc + " END_INDENT + + If you just want to test normal indenting with default options, you can make + this a large number of lines. Just add all kinds of language constructs, + nested statements, etc. with valid syntax. + +- Optionally, add lines with INDENT_EXE after START_INDENT, followed by a Vim + command. This will be executed before indenting the lines. Example: + + " START_INDENT + " INDENT_EXE let g:vim_indent_cont = 6 + let cmd = + \ 'some ' + \ 'string' + " END_INDENT + + Note that the command is not undone, you may need to reverse the effect for + the next block of lines. + +- Alternatively to indenting all the lines between START_INDENT and + END_INDENT, use an INDENT_AT line, which specifies a pattern to find the + line to indent. Example: + + " START_INDENT + " INDENT_AT this-line + func Some() + let f = x " this-line + endfunc + " END_INDENT + + Alternatively you can use INDENT_NEXT to indent the line below the matching + pattern. Keep in mind that quite often it will indent relative to the + matching line: + + " START_INDENT + " INDENT_NEXT next-line + func Some() + " next-line + let f = x + endfunc + " END_INDENT + + Or use INDENT_PREV to indent the line above the matching pattern: + + " START_INDENT + " INDENT_PREV prev-line + func Some() + let f = x + " prev-line + endfunc + " END_INDENT + +It's best to keep the whole file valid for FILETYPE, so that syntax +highlighting works normally, and any indenting that depends on the syntax +highlighting also works. + + +RUNNING THE TEST + +Before running the test, create a FILETYPE.ok file. You can leave it empty at +first. + +Now run "make test" from the parent directory. After Vim has done the +indenting you will see a FILETYPE.fail file. This contains the actual result +of indenting, and it's different from the FILETYPE.ok file. + +Check the contents of the FILETYPE.fail file. If it is perfectly OK, then +rename it to overwrite the FILETYPE.ok file. If you now run "make test" again, +the test will pass and create a FILETYPE.out file, which is identical to the +FILETYPE.ok file. The FILETYPE.fail file will be deleted. + +If you try to run "make test" again you will notice that nothing happens, +because the FILETYPE.out file already exists. Delete it, or do "make clean", +so that the text runs again. If you edit the FILETYPE.in file, so that it's +newer than the FILETYPE.out file, the test will also run. diff --git a/runtime/indent/testdir/bitbake.in b/runtime/indent/testdir/bitbake.in new file mode 100644 index 0000000..afd19be --- /dev/null +++ b/runtime/indent/testdir/bitbake.in @@ -0,0 +1,19 @@ +# vim: set filetype=bitbake : + +# START_INDENT +FOO = " \ + bar \ + baz \ + qux \ + " + +do_configure() { +oe_conf +} + +python do_task() { +def foo(x): +if y: +print(x) +} +# END_INDENT diff --git a/runtime/indent/testdir/bitbake.ok b/runtime/indent/testdir/bitbake.ok new file mode 100644 index 0000000..1bc5a18 --- /dev/null +++ b/runtime/indent/testdir/bitbake.ok @@ -0,0 +1,19 @@ +# vim: set filetype=bitbake : + +# START_INDENT +FOO = " \ + bar \ + baz \ + qux \ +" + +do_configure() { + oe_conf +} + +python do_task() { + def foo(x): + if y: + print(x) +} +# END_INDENT diff --git a/runtime/indent/testdir/dts.in b/runtime/indent/testdir/dts.in new file mode 100644 index 0000000..64e56e9 --- /dev/null +++ b/runtime/indent/testdir/dts.in @@ -0,0 +1,46 @@ +/* vim: set ft=dts noet sw=8 : */ + +/* START_INDENT */ +/dts-v1/; +#include <dt-bindings/pinctrl/pinctrl-imx6q.h> + #include "imx6qdl.dtsi" +#include "imx6qdl-someboard.dtsi" + + /delete-node/ &{/memory@10000000}; + + / { +compatible = "some,board"; +/delete-node/ memory; + + chosen { +environment = &{usdhc4/partitions/partition@0}; +}; +} + + &iomuxc { +pinctrl-names = "default"; +pinctrl-0 = <&pinctrl_hog>; + +pinctrl_gpiohog: gpiohoggrp { +fsl,pins = < +MX6QDL_PAD_GPIO_9__GPIO1_IO09 0x130b0 +MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x130b0 +>; +}; +} + +&usdhc4 { +partitions { +compatible = "fixed-partitions"; + +partition@0 { +label = "environment"; +reg = <0x0 0xe0000>; +}; +}; +}; + +&{/aliases} { +usb0 = &usb; +}; +/* END_INDENT */ diff --git a/runtime/indent/testdir/dts.ok b/runtime/indent/testdir/dts.ok new file mode 100644 index 0000000..d249766 --- /dev/null +++ b/runtime/indent/testdir/dts.ok @@ -0,0 +1,46 @@ +/* vim: set ft=dts noet sw=8 : */ + +/* START_INDENT */ +/dts-v1/; +#include <dt-bindings/pinctrl/pinctrl-imx6q.h> +#include "imx6qdl.dtsi" +#include "imx6qdl-someboard.dtsi" + +/delete-node/ &{/memory@10000000}; + +/ { + compatible = "some,board"; + /delete-node/ memory; + + chosen { + environment = &{usdhc4/partitions/partition@0}; + }; +} + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + pinctrl_gpiohog: gpiohoggrp { + fsl,pins = < + MX6QDL_PAD_GPIO_9__GPIO1_IO09 0x130b0 + MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x130b0 + >; + }; +} + +&usdhc4 { + partitions { + compatible = "fixed-partitions"; + + partition@0 { + label = "environment"; + reg = <0x0 0xe0000>; + }; + }; +}; + +&{/aliases} { + usb0 = &usb; +}; +/* END_INDENT */ diff --git a/runtime/indent/testdir/html.in b/runtime/indent/testdir/html.in new file mode 100644 index 0000000..4783a09 --- /dev/null +++ b/runtime/indent/testdir/html.in @@ -0,0 +1,77 @@ +% vim: set ft=html sw=4 ts=8 : + + +% START_INDENT +<html> + <body> +<style> +div#d1 { color: red; } +div#d2 { color: green; } +</style> + <script> + var v1 = "v1"; +var v2 = "v2"; + </script> +<div> +<div> +text +</div> +</div> + +<div +class="foo bar"> +text +</div> + +<div class="foo bar" +data="something"> +text +</div> + +<div class="foo +bar"> +text +</div> + +<dl> +<dd> +dd text +</dd> +<dt> +dt text +</dt> +</dl> +<div +class="test" +style="color: yellow"> +text +</div> + + </body> +</html> + +% END_INDENT + +% START_INDENT +% INDENT_EXE let g:html_indent_style1 = "inc" +% INDENT_EXE let g:html_indent_script1 = "zero" +% INDENT_EXE let g:html_indent_attribute = 1 +% INDENT_EXE call HtmlIndent_CheckUserSettings() +<html> + <body> +<style> +div#d1 { color: red; } +div#d2 { color: green; } +</style> + <script> + var v1 = "v1"; +var v2 = "v2"; + </script> +<div +class="test" +style="color: yellow"> +text +</div> +</body> +</html> +% END_INDENT diff --git a/runtime/indent/testdir/html.ok b/runtime/indent/testdir/html.ok new file mode 100644 index 0000000..4963634 --- /dev/null +++ b/runtime/indent/testdir/html.ok @@ -0,0 +1,77 @@ +% vim: set ft=html sw=4 ts=8 : + + +% START_INDENT +<html> + <body> + <style> +div#d1 { color: red; } +div#d2 { color: green; } + </style> + <script> + var v1 = "v1"; + var v2 = "v2"; + </script> + <div> + <div> + text + </div> + </div> + + <div + class="foo bar"> + text + </div> + + <div class="foo bar" + data="something"> + text + </div> + + <div class="foo + bar"> + text + </div> + + <dl> + <dd> + dd text + </dd> + <dt> + dt text + </dt> + </dl> + <div + class="test" + style="color: yellow"> + text + </div> + + </body> +</html> + +% END_INDENT + +% START_INDENT +% INDENT_EXE let g:html_indent_style1 = "inc" +% INDENT_EXE let g:html_indent_script1 = "zero" +% INDENT_EXE let g:html_indent_attribute = 1 +% INDENT_EXE call HtmlIndent_CheckUserSettings() +<html> + <body> + <style> + div#d1 { color: red; } + div#d2 { color: green; } + </style> + <script> +var v1 = "v1"; +var v2 = "v2"; + </script> + <div + class="test" + style="color: yellow"> + text + </div> + </body> +</html> +% END_INDENT diff --git a/runtime/indent/testdir/krl.in b/runtime/indent/testdir/krl.in new file mode 100644 index 0000000..ec90feb --- /dev/null +++ b/runtime/indent/testdir/krl.in @@ -0,0 +1,148 @@ +; vim: set ft=krl : + +; START_INDENT + +def One() +int i +If i==1 then +While i>=1 +For i=1 to 5 step 2 +Loop +i = i+1 +EndLoop +EndFor +EndWhile +Else +Repeat +Switch i +Case 1 +Skip 123 +i = i+1 +EndSkip 123 +Spline with $acc=100, $vel.cp=3 +slin {x 100} +scirc {x 110, y 110}, {x 120, y 90} +slin {x 200} c_dis +Time_Block Start +slin {x 300} c_dis +Time_Block Part = 22.2 +slin {y 400} c_dis +Time_Block Part = 33.3 +Time_Block End = 10 +slin {y 200} c_dis +Const_Vel Start +100 OnStart +slin {y 300} c_dis +slin {x 100} +Const_Vel End -5.5 +slin {y 200} c_dis +EndSpline +Case 2,3 +PTP_Spline with $acc=100, $vel.ptp=100 +sptp {a1 0} c_ptp +sptp {a1 90} +EndSpline c_spl +Default +i = i+1 +EndSwitch +Continue +Until False +EndIf +end + +DEF Two() +int i +END + +global def Three() +int i +end + +GLOBAL DEF Four() +int i +END + +Global Def Five() +int i +End + +deffct bool fOne() +int i +endfct + +DEFFCT bool fTwo() +int i +ENDFCT + +global deffct bool fThree() +int i +endfct + +GLOBAL DEFFCT bool fFour() +int i +ENDFCT + +Global DefFct bool fFive() +int i +EndFct + +DefDat datfile() +global int i=1 +; don't indent column 1 comments unless g:krlCommentIndent is set +; global int o=2 +EndDat + +; END_INDENT + +; START_INDENT +; INDENT_EXE let g:krlSpaceIndent = 0 +; INDENT_EXE set shiftwidth=4 + +def bla() +int i +end + +; END_INDENT + +; START_INDENT +; INDENT_EXE let g:krlCommentIndent = 1 +def bla() +; indent this first column comment because of g:krlCommentIndent=1 +end +; END_INDENT + +; START_INDENT +; INDENT_EXE let g:krlIndentBetweenDef = 0 +def bla() +int i ; don't indent this line because of g:krlIndentBetweenDef=0 +end +; END_INDENT + +; START_INDENT +; INDENT_AT this-line +def Some() +int f +if true then +f = 1 ; this-line +endif +end +; END_INDENT + +; START_INDENT +; INDENT_NEXT next-line +def Some() + int i + ; next-line +i = 1 ; should get indent of line 'int i' above +end +; END_INDENT + +; START_INDENT +; INDENT_PREV prev-line +def Some() +int f +if true then +f = 1 +; prev-line +endif +end +; END_INDENT diff --git a/runtime/indent/testdir/krl.ok b/runtime/indent/testdir/krl.ok new file mode 100644 index 0000000..34dc0f5 --- /dev/null +++ b/runtime/indent/testdir/krl.ok @@ -0,0 +1,148 @@ +; vim: set ft=krl : + +; START_INDENT + +def One() + int i + If i==1 then + While i>=1 + For i=1 to 5 step 2 + Loop + i = i+1 + EndLoop + EndFor + EndWhile + Else + Repeat + Switch i + Case 1 + Skip 123 + i = i+1 + EndSkip 123 + Spline with $acc=100, $vel.cp=3 + slin {x 100} + scirc {x 110, y 110}, {x 120, y 90} + slin {x 200} c_dis + Time_Block Start + slin {x 300} c_dis + Time_Block Part = 22.2 + slin {y 400} c_dis + Time_Block Part = 33.3 + Time_Block End = 10 + slin {y 200} c_dis + Const_Vel Start +100 OnStart + slin {y 300} c_dis + slin {x 100} + Const_Vel End -5.5 + slin {y 200} c_dis + EndSpline + Case 2,3 + PTP_Spline with $acc=100, $vel.ptp=100 + sptp {a1 0} c_ptp + sptp {a1 90} + EndSpline c_spl + Default + i = i+1 + EndSwitch + Continue + Until False + EndIf +end + +DEF Two() + int i +END + +global def Three() + int i +end + +GLOBAL DEF Four() + int i +END + +Global Def Five() + int i +End + +deffct bool fOne() + int i +endfct + +DEFFCT bool fTwo() + int i +ENDFCT + +global deffct bool fThree() + int i +endfct + +GLOBAL DEFFCT bool fFour() + int i +ENDFCT + +Global DefFct bool fFive() + int i +EndFct + +DefDat datfile() + global int i=1 +; don't indent column 1 comments unless g:krlCommentIndent is set +; global int o=2 +EndDat + +; END_INDENT + +; START_INDENT +; INDENT_EXE let g:krlSpaceIndent = 0 +; INDENT_EXE set shiftwidth=4 + +def bla() + int i +end + +; END_INDENT + +; START_INDENT +; INDENT_EXE let g:krlCommentIndent = 1 +def bla() + ; indent this first column comment because of g:krlCommentIndent=1 +end +; END_INDENT + +; START_INDENT +; INDENT_EXE let g:krlIndentBetweenDef = 0 +def bla() +int i ; don't indent this line because of g:krlIndentBetweenDef=0 +end +; END_INDENT + +; START_INDENT +; INDENT_AT this-line +def Some() +int f +if true then + f = 1 ; this-line +endif +end +; END_INDENT + +; START_INDENT +; INDENT_NEXT next-line +def Some() + int i + ; next-line + i = 1 ; should get indent of line 'int i' above +end +; END_INDENT + +; START_INDENT +; INDENT_PREV prev-line +def Some() +int f +if true then + f = 1 +; prev-line +endif +end +; END_INDENT diff --git a/runtime/indent/testdir/matlab.in b/runtime/indent/testdir/matlab.in new file mode 100644 index 0000000..b997ec8 --- /dev/null +++ b/runtime/indent/testdir/matlab.in @@ -0,0 +1,89 @@ +% vim: set ft=matlab sw=4 : + +% START_INDENT +if true +disp foo +elseif false +disp bar +end +% END_INDENT + +% START_INDENT +try +statements +catch exception +statements +end +% END_INDENT + +% START_INDENT +if true, ... +if true +disp hello +end +end +% END_INDENT + +% START_INDENT +switch a +case expr +if true, foo; end +disp hello +otherwise +disp bar +end +% END_INDENT + +% START_INDENT +if true +A(1:end - 1) +C{1:end - 1} +disp foo +end +% END_INDENT + +% START_INDENT +A = [{ +} +] ... +disp foo +disp bar +% END_INDENT + +% START_INDENT +if true +% end +%% end +disp foo +end +% END_INDENT + +% START_INDENT +% INDENT_EXE let b:MATLAB_function_indent = 0 +function foo +disp foo +function nested +disp bar +end +end +% END_INDENT + +% START_INDENT +% INDENT_EXE let b:MATLAB_function_indent = 1 +function foo +disp foo +function nested +disp bar +end +end +% END_INDENT + +% START_INDENT +% INDENT_EXE let b:MATLAB_function_indent = 2 +function foo +disp foo +function nested +disp bar +end +end +% END_INDENT diff --git a/runtime/indent/testdir/matlab.ok b/runtime/indent/testdir/matlab.ok new file mode 100644 index 0000000..df4e7b2 --- /dev/null +++ b/runtime/indent/testdir/matlab.ok @@ -0,0 +1,89 @@ +% vim: set ft=matlab sw=4 : + +% START_INDENT +if true + disp foo +elseif false + disp bar +end +% END_INDENT + +% START_INDENT +try + statements +catch exception + statements +end +% END_INDENT + +% START_INDENT +if true, ... + if true + disp hello + end +end +% END_INDENT + +% START_INDENT +switch a + case expr + if true, foo; end + disp hello + otherwise + disp bar +end +% END_INDENT + +% START_INDENT +if true + A(1:end - 1) + C{1:end - 1} + disp foo +end +% END_INDENT + +% START_INDENT +A = [{ + } + ] ... + disp foo +disp bar +% END_INDENT + +% START_INDENT +if true + % end + %% end + disp foo +end +% END_INDENT + +% START_INDENT +% INDENT_EXE let b:MATLAB_function_indent = 0 +function foo +disp foo + function nested + disp bar + end +end +% END_INDENT + +% START_INDENT +% INDENT_EXE let b:MATLAB_function_indent = 1 +function foo +disp foo + function nested + disp bar + end +end +% END_INDENT + +% START_INDENT +% INDENT_EXE let b:MATLAB_function_indent = 2 +function foo + disp foo + function nested + disp bar + end +end +% END_INDENT diff --git a/runtime/indent/testdir/python.in b/runtime/indent/testdir/python.in new file mode 100644 index 0000000..57719ee --- /dev/null +++ b/runtime/indent/testdir/python.in @@ -0,0 +1,94 @@ +# vim: set ft=python sw=4 et: + +# START_INDENT +dict = { +'a': 1, +'b': 2, +'c': 3, +} +# END_INDENT + +# START_INDENT +# INDENT_EXE let [g:python_indent.open_paren, g:python_indent.closed_paren_align_last_line] = ['shiftwidth()', v:false] +dict = { +'a': 1, +'b': 2, +'c': 3, +} +# END_INDENT + +# START_INDENT +# INDENT_EXE let g:python_indent.open_paren = 'shiftwidth() * 2' +# INDENT_EXE syntax match pythonFoldMarkers /{{{\d*/ contained containedin=pythonComment +# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx {{{1 + +if True: +pass +# END_INDENT + +# START_INDENT +open_paren_not_at_EOL(100, +(200, +300), +400) + +open_paren_at_EOL( +100, 200, 300, 400) + +open_paren_not_at_EOL(100, +(200, +300), +400) + +open_paren_at_EOL( +100, 200, 300, 400) + +open_paren_not_at_EOL(100, +(200, +300), +400) + +open_paren_at_EOL( +100, 200, 300, 400) + +open_paren_not_at_EOL(100, +(200, +300), +400) + +open_paren_at_EOL( +100, 200, 300, 400) + +open_paren_not_at_EOL(100, +(200, +300), +400) + +open_paren_at_EOL( +100, 200, 300, 400) + +open_paren_not_at_EOL(100, +(200, +300), +400) + +open_paren_at_EOL( +100, 200, 300, 400) + +open_paren_not_at_EOL(100, +(200, +300), +400) + +open_paren_at_EOL( +100, 200, 300, 400) + +open_paren_not_at_EOL(100, +(200, +300), +400) + +open_paren_at_EOL( +100, 200, 300, 400) + +# END_INDENT diff --git a/runtime/indent/testdir/python.ok b/runtime/indent/testdir/python.ok new file mode 100644 index 0000000..f5ebbc2 --- /dev/null +++ b/runtime/indent/testdir/python.ok @@ -0,0 +1,94 @@ +# vim: set ft=python sw=4 et: + +# START_INDENT +dict = { + 'a': 1, + 'b': 2, + 'c': 3, + } +# END_INDENT + +# START_INDENT +# INDENT_EXE let [g:python_indent.open_paren, g:python_indent.closed_paren_align_last_line] = ['shiftwidth()', v:false] +dict = { + 'a': 1, + 'b': 2, + 'c': 3, +} +# END_INDENT + +# START_INDENT +# INDENT_EXE let g:python_indent.open_paren = 'shiftwidth() * 2' +# INDENT_EXE syntax match pythonFoldMarkers /{{{\d*/ contained containedin=pythonComment +# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx {{{1 + +if True: + pass +# END_INDENT + +# START_INDENT +open_paren_not_at_EOL(100, + (200, + 300), + 400) + +open_paren_at_EOL( + 100, 200, 300, 400) + +open_paren_not_at_EOL(100, + (200, + 300), + 400) + +open_paren_at_EOL( + 100, 200, 300, 400) + +open_paren_not_at_EOL(100, + (200, + 300), + 400) + +open_paren_at_EOL( + 100, 200, 300, 400) + +open_paren_not_at_EOL(100, + (200, + 300), + 400) + +open_paren_at_EOL( + 100, 200, 300, 400) + +open_paren_not_at_EOL(100, + (200, + 300), + 400) + +open_paren_at_EOL( + 100, 200, 300, 400) + +open_paren_not_at_EOL(100, + (200, + 300), + 400) + +open_paren_at_EOL( + 100, 200, 300, 400) + +open_paren_not_at_EOL(100, + (200, + 300), + 400) + +open_paren_at_EOL( + 100, 200, 300, 400) + +open_paren_not_at_EOL(100, + (200, + 300), + 400) + +open_paren_at_EOL( + 100, 200, 300, 400) + +# END_INDENT diff --git a/runtime/indent/testdir/rapid.in b/runtime/indent/testdir/rapid.in new file mode 100644 index 0000000..515912e --- /dev/null +++ b/runtime/indent/testdir/rapid.in @@ -0,0 +1,266 @@ +! vim: set ft=rapid : + +! START_INDENT + +%%% + VERSION:1 + LANGUAGE:ENGLISH +%%% + +module LowerCaseModule + +task pers num n1 := 0; +local pers num n2 := 1; +var bool b1 := false; +var intnum i1; + +! put some stuff in those strings that may confuse indentation +const string st1 := "endmodule ("; +pers string st_Appl_Info{3,3}:=[ +[ +"[" +, +"default" +, +"case" +], +[ +"else" +, +"then" +, +"endif" +], +[ +"do" +, +"}" +, +")" +], +]; + +pers tooldata tTool1:=[TRUE, +[ +[97.4, 0, 223.1], +[0.924, 0, 0.383 ,0] +], +[5, +[23, 0, 75], +[1, 0, 0, 0], 0, 0, 0 +] +]; +const robtarget p1:=[ +[600, 500, 225.3], +[1, 0, 0, 0], +[1, 1, 0, 0], +[ 11, 12.3, 9E9, 9E9, 9E9, 9E9] +]; + +record myRec +num nRecNum1 +bool bRecBool1 +endrecord + +proc proc1(num n1, +num n2) +var string st1; +n1 := n1+1; +MoveJSync p1, vmax, z30, tool1, "proc2"; +backward +MoveJSync p1, v100, fine, tool1, "proc2"; +undo +n1 := n1-1; +error +trynext; +endproc + +func num nFunc1( +switch s1 +|switch s2 +,num n1 +,bool b1) +var num nVar; +if not Present(s1) return; +if Present(s1) then +Incr n1;' +elseif Present(s2) then +b1:=false; +else +while n1>0 do +Decr n1; +test n1 + +case 1: +test1; +case 2: +test2; +default: +WaitUntil false; +endtest +endwhile +endif +for i from 1 to 10 step 2 do +for j from 1 to 10 do +st_Appl_Info{i,j} := ""; +endfor +endfor +! return 1; +return 0; +error +return -1; +endfunc + +trap Trap1 +Reset do1; +endtrap + +endmodule + +MODULE UpperCaseModule(SYSMODULE,NOSTEPIN) +TASK pers num n1 := 0; +LOCAL pers num n2 := 1; +VAR bool b1 := false; +VAR intnum i1; + +LOCAL FUNC num nFunc1( +switch s1 +|switch s2 +,num n1 +,bool b1) +VAR num nVar; +IF NOT PRESENT(s1) RETURN; +IF PRESENT(s1) THEN +INCR n1;' +ELSEIF PRESENT(s2) THEN +b1:=FALSE; +ELSE +WHILE n1>0 DO +DECR n1; +TEST n1 + +CASE 1: +test1; +CASE 2: +test2; +DEFAULT: +WAITUNTIL FALSE; +ENDTEST +ENDWHILE +ENDIF +FOR i FROM 1 TO 10 STEP 2 DO +FOR j FROM 1 TO 10 DO +st_Appl_Info{i,j} := ""; +ENDFOR +ENDFOR +! RETURN 1; +RETURN 0; +ERROR +RETURN -1; +ENDFUNC + +TRAP Trap1 +Reset do1; +ENDTRAP + +ENDMODULE + +Module MixedCaseModule(SysModule) +Task pers num n1 := 0; +Local pers num n2 := 1; +Var bool b1 := false; +Var intnum i1; + +Task Func num nFunc1( +switch s1 +|switch s2 +,num n1 +,bool b1) +Var num nVar; +If Not Present(s1) Return; +If Present(s1) Then +Incr n1;' +ElseIf Present(s2) Then +b1:=false; +Else +While n1>0 Do +Decr n1; +Test n1 + +Case 1: +test1; +Case 2: +test2; +Default: +WaitUntil false; +EndTest +EndWhile +EndIf +For i From 1 To 10 Step 2 Do +For j From 1 To 10 Do +st_Appl_Info{i,j} := ""; +EndFor +EndFor +! Return 1; +Return 0; +Error +Return -1; +EndFunc + +Trap Trap1 +Reset do1; +EndTrap + +EndModule + +! END_INDENT + +! START_INDENT +! INDENT_EXE let g:rapidSpaceIndent = 0 +! INDENT_EXE set shiftwidth=4 + +proc bla() +var num i; +Incr i; +endproc + +! END_INDENT + +! START_INDENT +! INDENT_EXE let g:rapidCommentIndent = 1 +! +proc bla() +! indent this first column comment because of g:rapidCommentIndent=1 +endproc +! END_INDENT + +! START_INDENT +! INDENT_EXE let g:rapidNewStyleIndent = 1 +pers string st_Appl_Info{3,3}:= +[ +[ +"[" +, +"default" +, +"case" +] +, +[ +"else" +, +"then" +, +"endif" +] +, +[ +"do" +, +"}" +, +")" +] +, +]; +! END_INDENT diff --git a/runtime/indent/testdir/rapid.ok b/runtime/indent/testdir/rapid.ok new file mode 100644 index 0000000..ce33682 --- /dev/null +++ b/runtime/indent/testdir/rapid.ok @@ -0,0 +1,266 @@ +! vim: set ft=rapid : + +! START_INDENT + +%%% +VERSION:1 +LANGUAGE:ENGLISH +%%% + +module LowerCaseModule + + task pers num n1 := 0; + local pers num n2 := 1; + var bool b1 := false; + var intnum i1; + +! put some stuff in those strings that may confuse indentation + const string st1 := "endmodule ("; + pers string st_Appl_Info{3,3}:=[ + [ + "[" + , + "default" + , + "case" + ], + [ + "else" + , + "then" + , + "endif" + ], + [ + "do" + , + "}" + , + ")" + ], + ]; + + pers tooldata tTool1:=[TRUE, + [ + [97.4, 0, 223.1], + [0.924, 0, 0.383 ,0] + ], + [5, + [23, 0, 75], + [1, 0, 0, 0], 0, 0, 0 + ] + ]; + const robtarget p1:=[ + [600, 500, 225.3], + [1, 0, 0, 0], + [1, 1, 0, 0], + [ 11, 12.3, 9E9, 9E9, 9E9, 9E9] + ]; + + record myRec + num nRecNum1 + bool bRecBool1 + endrecord + + proc proc1(num n1, + num n2) + var string st1; + n1 := n1+1; + MoveJSync p1, vmax, z30, tool1, "proc2"; + backward + MoveJSync p1, v100, fine, tool1, "proc2"; + undo + n1 := n1-1; + error + trynext; + endproc + + func num nFunc1( + switch s1 + |switch s2 + ,num n1 + ,bool b1) + var num nVar; + if not Present(s1) return; + if Present(s1) then + Incr n1;' + elseif Present(s2) then + b1:=false; + else + while n1>0 do + Decr n1; + test n1 + + case 1: + test1; + case 2: + test2; + default: + WaitUntil false; + endtest + endwhile + endif + for i from 1 to 10 step 2 do + for j from 1 to 10 do + st_Appl_Info{i,j} := ""; + endfor + endfor +! return 1; + return 0; + error + return -1; + endfunc + + trap Trap1 + Reset do1; + endtrap + +endmodule + +MODULE UpperCaseModule(SYSMODULE,NOSTEPIN) + TASK pers num n1 := 0; + LOCAL pers num n2 := 1; + VAR bool b1 := false; + VAR intnum i1; + + LOCAL FUNC num nFunc1( + switch s1 + |switch s2 + ,num n1 + ,bool b1) + VAR num nVar; + IF NOT PRESENT(s1) RETURN; + IF PRESENT(s1) THEN + INCR n1;' + ELSEIF PRESENT(s2) THEN + b1:=FALSE; + ELSE + WHILE n1>0 DO + DECR n1; + TEST n1 + + CASE 1: + test1; + CASE 2: + test2; + DEFAULT: + WAITUNTIL FALSE; + ENDTEST + ENDWHILE + ENDIF + FOR i FROM 1 TO 10 STEP 2 DO + FOR j FROM 1 TO 10 DO + st_Appl_Info{i,j} := ""; + ENDFOR + ENDFOR +! RETURN 1; + RETURN 0; + ERROR + RETURN -1; + ENDFUNC + + TRAP Trap1 + Reset do1; + ENDTRAP + +ENDMODULE + +Module MixedCaseModule(SysModule) + Task pers num n1 := 0; + Local pers num n2 := 1; + Var bool b1 := false; + Var intnum i1; + + Task Func num nFunc1( + switch s1 + |switch s2 + ,num n1 + ,bool b1) + Var num nVar; + If Not Present(s1) Return; + If Present(s1) Then + Incr n1;' + ElseIf Present(s2) Then + b1:=false; + Else + While n1>0 Do + Decr n1; + Test n1 + + Case 1: + test1; + Case 2: + test2; + Default: + WaitUntil false; + EndTest + EndWhile + EndIf + For i From 1 To 10 Step 2 Do + For j From 1 To 10 Do + st_Appl_Info{i,j} := ""; + EndFor + EndFor +! Return 1; + Return 0; + Error + Return -1; + EndFunc + + Trap Trap1 + Reset do1; + EndTrap + +EndModule + +! END_INDENT + +! START_INDENT +! INDENT_EXE let g:rapidSpaceIndent = 0 +! INDENT_EXE set shiftwidth=4 + +proc bla() + var num i; + Incr i; +endproc + +! END_INDENT + +! START_INDENT +! INDENT_EXE let g:rapidCommentIndent = 1 +! +proc bla() + ! indent this first column comment because of g:rapidCommentIndent=1 +endproc +! END_INDENT + +! START_INDENT +! INDENT_EXE let g:rapidNewStyleIndent = 1 +pers string st_Appl_Info{3,3}:= +[ + [ + "[" + , + "default" + , + "case" + ] + , + [ + "else" + , + "then" + , + "endif" + ] + , + [ + "do" + , + "}" + , + ")" + ] + , +]; +! END_INDENT diff --git a/runtime/indent/testdir/runtest.vim b/runtime/indent/testdir/runtest.vim new file mode 100644 index 0000000..882a140 --- /dev/null +++ b/runtime/indent/testdir/runtest.vim @@ -0,0 +1,146 @@ +" Runs all the indent tests for which there is no .out file. +" +" Current directory must be runtime/indent. + +" Only do this with the +eval feature +if 1 + +set nocp +filetype indent on +syn on +set nowrapscan +set report=9999 +set modeline +set debug=throw +set nomore + +au! SwapExists * call HandleSwapExists() +func HandleSwapExists() + " Ignore finding a swap file for the test input and output, the user might be + " editing them and that's OK. + if expand('<afile>') =~ '.*\.\(in\|out\|fail\|ok\)' + let v:swapchoice = 'e' + endif +endfunc + +let failed_count = 0 +for fname in glob('testdir/*.in', 1, 1) + let root = substitute(fname, '\.in', '', '') + + " Execute the test if the .out file does not exist of when the .in file is + " newer. + let in_time = getftime(fname) + let out_time = getftime(root . '.out') + if out_time < 0 || in_time > out_time + call delete(root . '.fail') + call delete(root . '.out') + + set sw& ts& filetype= + exe 'split ' . fname + + let did_some = 0 + let failed = 0 + let end = 1 + while 1 + " Indent all the lines between "START_INDENT" and "END_INDENT" + exe end + let start = search('\<START_INDENT\>') + let end = search('\<END_INDENT\>') + if start <= 0 || end <= 0 || end <= start + if did_some == 0 + call append(0, 'ERROR: START_INDENT and/or END_INDENT not found') + let failed = 1 + endif + break + else + let did_some = 1 + + " Execute all commands marked with INDENT_EXE and find any pattern. + let lnum = start + let pattern = '' + let at = '' + while 1 + exe lnum + 1 + let lnum_exe = search('\<INDENT_EXE\>') + exe lnum + 1 + let indent_at = search('\<INDENT_\(AT\|NEXT\|PREV\)\>') + if lnum_exe > 0 && lnum_exe < end && (indent_at <= 0 || lnum_exe < indent_at) + exe substitute(getline(lnum_exe), '.*INDENT_EXE', '', '') + let lnum = lnum_exe + let start = lnum + elseif indent_at > 0 && indent_at < end + if pattern != '' + call append(indent_at, 'ERROR: duplicate pattern') + let failed = 1 + break + endif + let text = getline(indent_at) + let pattern = substitute(text, '.*INDENT_\S*\s*', '', '') + let at = substitute(text, '.*INDENT_\(\S*\).*', '\1', '') + let lnum = indent_at + let start = lnum + else + break + endif + endwhile + + exe start + 1 + if pattern == '' + try + exe 'normal =' . (end - 1) . 'G' + catch + call append(indent_at, 'ERROR: ' . v:exception) + let failed = 1 + endtry + else + let lnum = search(pattern) + if lnum <= 0 + call append(indent_at, 'ERROR: pattern not found: ' . pattern) + let failed = 1 + break + endif + if at == 'AT' + exe lnum + elseif at == 'NEXT' + exe lnum + 1 + else + exe lnum - 1 + endif + try + normal == + catch + call append(indent_at, 'ERROR: ' . v:exception) + let failed = 1 + endtry + endif + endif + endwhile + + if !failed + " Check the resulting text equals the .ok file. + if getline(1, '$') != readfile(root . '.ok') + let failed = 1 + endif + endif + + if failed + let failed_count += 1 + exe 'write ' . root . '.fail' + echoerr 'Test ' . fname . ' FAILED!' + else + exe 'write ' . root . '.out' + echo "Test " . fname . " OK\n" + endif + + quit! " close the indented file + endif +endfor + +" Matching "if 1" at the start. +endif + +if failed_count > 0 + " have make report an error + cquit +endif +qall! diff --git a/runtime/indent/testdir/sshconfig.in b/runtime/indent/testdir/sshconfig.in new file mode 100644 index 0000000..87b998e --- /dev/null +++ b/runtime/indent/testdir/sshconfig.in @@ -0,0 +1,53 @@ +# vim: set filetype=sshconfig shiftwidth=4 expandtab : + +# START_INDENT +Host myhost +User myuser +PasswordAuthentication no +# END_INDENT + +# START_INDENT +Host aaa +User bbb +Host ccc +Host ddd +# END_INDENT + +# START_INDENT +host aaa +HOST bbb +hoSt ccc +match ddd +MATCH eee +MatCH fff +# END_INDENT + +# START_INDENT +Host aaa +User host +PasswordAuthentication no +Host * +User user +PasswordAuthentication no +Host match +User bbb +# END_INDENT + +# START_INDENT +Host tab +User myuser +# END_INDENT + +# START_INDENT +Host mix +User myuser +# END_INDENT + +# START_INDENT +Host aaa +User bbb +Match ccc +User ddd +HostKeyAlgorithms ssh-ed25519 +Match eee +# END_INDENT diff --git a/runtime/indent/testdir/sshconfig.ok b/runtime/indent/testdir/sshconfig.ok new file mode 100644 index 0000000..b24b7cf --- /dev/null +++ b/runtime/indent/testdir/sshconfig.ok @@ -0,0 +1,53 @@ +# vim: set filetype=sshconfig shiftwidth=4 expandtab : + +# START_INDENT +Host myhost + User myuser + PasswordAuthentication no +# END_INDENT + +# START_INDENT +Host aaa + User bbb +Host ccc +Host ddd +# END_INDENT + +# START_INDENT +host aaa +HOST bbb +hoSt ccc +match ddd +MATCH eee +MatCH fff +# END_INDENT + +# START_INDENT +Host aaa + User host + PasswordAuthentication no +Host * + User user + PasswordAuthentication no +Host match + User bbb +# END_INDENT + +# START_INDENT +Host tab + User myuser +# END_INDENT + +# START_INDENT +Host mix + User myuser +# END_INDENT + +# START_INDENT +Host aaa + User bbb +Match ccc + User ddd + HostKeyAlgorithms ssh-ed25519 +Match eee +# END_INDENT diff --git a/runtime/indent/testdir/tcl.in b/runtime/indent/testdir/tcl.in new file mode 100644 index 0000000..c769d5b --- /dev/null +++ b/runtime/indent/testdir/tcl.in @@ -0,0 +1,19 @@ +# vim: set filetype=tcl shiftwidth=4 tabstop=8 expandtab : + +# START_INDENT +proc abc {} { +set a 5 +if {[some_cmd]==1} { +foreach i [list {1 2 3}] { +# Does this comment affect anything? +puts $i +} +} +} + +command_with_a_long_time -arg1 "First" \ +-arg2 "Second" \ +-arg3 "Third" + +puts "Move indent back after line continuation is complete" +# END_INDENT
\ No newline at end of file diff --git a/runtime/indent/testdir/tcl.ok b/runtime/indent/testdir/tcl.ok new file mode 100644 index 0000000..77f24e9 --- /dev/null +++ b/runtime/indent/testdir/tcl.ok @@ -0,0 +1,19 @@ +# vim: set filetype=tcl shiftwidth=4 tabstop=8 expandtab : + +# START_INDENT +proc abc {} { + set a 5 + if {[some_cmd]==1} { + foreach i [list {1 2 3}] { + # Does this comment affect anything? + puts $i + } + } +} + +command_with_a_long_time -arg1 "First" \ + -arg2 "Second" \ + -arg3 "Third" + +puts "Move indent back after line continuation is complete" +# END_INDENT diff --git a/runtime/indent/testdir/vb.in b/runtime/indent/testdir/vb.in new file mode 100644 index 0000000..1653ae6 --- /dev/null +++ b/runtime/indent/testdir/vb.in @@ -0,0 +1,134 @@ +' vim: filetype=vb shiftwidth=4 expandtab +' +' START_INDENT +Public Type GEmployeeRecord ' Create user-defined type. +ID As Integer ' Define elements of data type. +Name As String * 20 +Address As String * 30 +Phone As Long +HireDate As Date +End Type + +Public Enum InterfaceColors +icMistyRose = &HE1E4FF& +icSlateGray = &H908070& +icDodgerBlue = &HFF901E& +icDeepSkyBlue = &HFFBF00& +icSpringGreen = &H7FFF00& +icForestGreen = &H228B22& +icGoldenrod = &H20A5DA& +icFirebrick = &H2222B2& +End Enum + +Enum SecurityLevel +IllegalEntry = -1 +SecurityLevel1 = 0 +SecurityLevel2 = 1 +End Enum + +Public Function TestConditional (number As Integer, ext As String) As Boolean +Dim inRange As Boolean + +Select Case number +Case <= 0 +inRange = False +Case > 10 +inRange = False +Case Else +inRange = True +End Select + +' This is a special case identified in the indent script. +Select Case number +End Select + +If ext = ".xlm" Then +If inRange Then +TestConditional = True +Else +TestConditional = False +End If +ElseIf ext = ".xlsx" Then +If inRange Then +TestConditional = False +Else +TestConditional = True +End If +Else +TestConditional = False +End If +End Function + +Private Sub TestIterators (lLimit As Integer, uLimit As Integer) +Dim a() As Variant +Dim elmt As Variant +Dim found As Boolean +Dim indx As Integer +Const specialValue As Integer = 5 + +If uLimit < lLimit Then +Exit Sub +End If + +ReDim a(lLimit To uLimit) +For indx=lLimit To Ulimit +a(indx) = 2 * indx +Next indx + +found = False +For Each elmt in a +If elmt = specialValue Then +found = True +End If +Next elmt + +If found then +indx = uLimit +Do While indx >= lLimit +indx = indx - 1 +Loop +End If + +End Sub + +Public Sub TestMultiline (cellAddr As String, rowNbr As Long) +Dim rng As Range + +Set rng = Range(cellAddr) +With rng +.Cells(1,1).Value = _ +"Line 1 of multiline string; " & _ +"Line 2 of multiline string; " & _ +"Line 3 of multiline string" +End With + +' The following lines have whitespace after the underscore character +' and therefore do not form a valid multiline statement. The indent +' script correctly treats them as four single line statements contrary +' to the author's obvious indent. +rng..Cells(1,1).Value = _ +"Line 1 of multiline string; " & _ +"Line 2 of multiline string; " & _ +"Line 3 of multiline string" + +End Sub + +Private Sub TestStmtLabel() +GoTo stmtLabel + +' Statement labels are never indented +stmtLabel: + +End Sub + +Sub TestTypeKeyword() +Type EmployeeRecord ' Create user-defined type. +ID As Integer ' Define elements of data type. +Name As String * 20 +Address As String * 30 +Phone As Long +HireDate As Date +End Type +Dim varType As EmployeeRecord +End Sub +' END_INDENT diff --git a/runtime/indent/testdir/vb.ok b/runtime/indent/testdir/vb.ok new file mode 100644 index 0000000..143c688 --- /dev/null +++ b/runtime/indent/testdir/vb.ok @@ -0,0 +1,134 @@ +' vim: filetype=vb shiftwidth=4 expandtab +' +' START_INDENT +Public Type GEmployeeRecord ' Create user-defined type. + ID As Integer ' Define elements of data type. + Name As String * 20 + Address As String * 30 + Phone As Long + HireDate As Date +End Type + +Public Enum InterfaceColors + icMistyRose = &HE1E4FF& + icSlateGray = &H908070& + icDodgerBlue = &HFF901E& + icDeepSkyBlue = &HFFBF00& + icSpringGreen = &H7FFF00& + icForestGreen = &H228B22& + icGoldenrod = &H20A5DA& + icFirebrick = &H2222B2& +End Enum + +Enum SecurityLevel + IllegalEntry = -1 + SecurityLevel1 = 0 + SecurityLevel2 = 1 +End Enum + +Public Function TestConditional (number As Integer, ext As String) As Boolean + Dim inRange As Boolean + + Select Case number + Case <= 0 + inRange = False + Case > 10 + inRange = False + Case Else + inRange = True + End Select + + ' This is a special case identified in the indent script. + Select Case number + End Select + + If ext = ".xlm" Then + If inRange Then + TestConditional = True + Else + TestConditional = False + End If + ElseIf ext = ".xlsx" Then + If inRange Then + TestConditional = False + Else + TestConditional = True + End If + Else + TestConditional = False + End If +End Function + +Private Sub TestIterators (lLimit As Integer, uLimit As Integer) + Dim a() As Variant + Dim elmt As Variant + Dim found As Boolean + Dim indx As Integer + Const specialValue As Integer = 5 + + If uLimit < lLimit Then + Exit Sub + End If + + ReDim a(lLimit To uLimit) + For indx=lLimit To Ulimit + a(indx) = 2 * indx + Next indx + + found = False + For Each elmt in a + If elmt = specialValue Then + found = True + End If + Next elmt + + If found then + indx = uLimit + Do While indx >= lLimit + indx = indx - 1 + Loop + End If + +End Sub + +Public Sub TestMultiline (cellAddr As String, rowNbr As Long) + Dim rng As Range + + Set rng = Range(cellAddr) + With rng + .Cells(1,1).Value = _ + "Line 1 of multiline string; " & _ + "Line 2 of multiline string; " & _ + "Line 3 of multiline string" + End With + + ' The following lines have whitespace after the underscore character + ' and therefore do not form a valid multiline statement. The indent + ' script correctly treats them as four single line statements contrary + ' to the author's obvious indent. + rng..Cells(1,1).Value = _ + "Line 1 of multiline string; " & _ + "Line 2 of multiline string; " & _ + "Line 3 of multiline string" + +End Sub + +Private Sub TestStmtLabel() + GoTo stmtLabel + + ' Statement labels are never indented +stmtLabel: + +End Sub + +Sub TestTypeKeyword() + Type EmployeeRecord ' Create user-defined type. + ID As Integer ' Define elements of data type. + Name As String * 20 + Address As String * 30 + Phone As Long + HireDate As Date + End Type + Dim varType As EmployeeRecord +End Sub +' END_INDENT diff --git a/runtime/indent/testdir/vim.in b/runtime/indent/testdir/vim.in new file mode 100644 index 0000000..c2e149a --- /dev/null +++ b/runtime/indent/testdir/vim.in @@ -0,0 +1,948 @@ +" vim: set ft=vim sw=4 : + +" START_INDENT +func Some() +let x = 1 +endfunc + +let cmd = +\ 'some ' +\ 'string' + +if 1 +let x = [ +\ ] +endif + +for x in [ +{key: 'value'}, +] +eval 0 +endfor + +let t = [ +\ { +\ 'k': 'val', +\ }, +\ ] + +def Func() +var d = dd +->extend({ +}) +eval 0 +enddef +" END_INDENT + +" START_INDENT +" INDENT_EXE let g:vim_indent_cont = 6 + +let cmd = +\ 'some ' +\ 'string' + +" END_INDENT + +" START_INDENT +" INDENT_EXE let g:vim_indent_cont = 5 + +let list = [ +\ 'one', +\ 'two'] + +" END_INDENT + +" START_INDENT +" INDENT_EXE unlet g:vim_indent_cont + +let list = [ +'one', +'two', +] +echo + +" END_INDENT + +" START_INDENT +" INDENT_AT this-line +func Some() +let f = x " this-line +endfunc +" END_INDENT + +" START_INDENT +" INDENT_NEXT next-line +func Some() + " next-line +let f = x +endfunc +" END_INDENT + +" START_INDENT +" INDENT_PREV prev-line +func Some() +let f = x +" prev-line +endfunc +" END_INDENT + +" START_INDENT +let a =<< END +nothing +END +" END_INDENT + +" START_INDENT +let a =<< trim END +nothing +END +" END_INDENT + +" START_INDENT +" INDENT_AT this-line +let a=<< trim END + blah + blah + blah this-line +END +" END_INDENT + +" START_INDENT +if v:true +echo 0 +end +" END_INDENT + +" START_INDENT +var result = Func( +arg1, +arg2 +) +" END_INDENT + +" START_INDENT +var result = Func(arg1, +arg2) +" END_INDENT + +" START_INDENT +filter(list, (k, v) => +v > 0) +" END_INDENT + +" START_INDENT +filter(list, (k, v) => { +const x = get(list, k, 0) +return x > 0 +}) +" END_INDENT + +" START_INDENT +if x > 0 +filter(list, (k, v) => { +const x = get(list, k, 1) +return x > 0 +}) +endif +" END_INDENT + +" START_INDENT +{ +var temp = 'temp' +} +" END_INDENT + +" START_INDENT +var text = lead +.. middle +.. end +" END_INDENT + +" START_INDENT +var text = lead .. +middle .. +end +" END_INDENT + +" START_INDENT +var total = start + +end - +correction +" END_INDENT + +" START_INDENT +var result = start +:+ print +" END_INDENT + +" START_INDENT +var result = positive +? PosFunc(arg) +: NegFunc(arg) +" END_INDENT + +" START_INDENT +var result = GetBuilder() +->BuilderSetWidth(333) +->BuilderSetHeight(777) +->BuilderBuild() +" END_INDENT + +" START_INDENT +var result = MyDict +.member +" END_INDENT + +" START_INDENT +autocmd BufNewFile *.match if condition +| echo 'match' +| endif +" END_INDENT + +" START_INDENT +set cpo+=C +var lines =<< trim END +| this works +END +set cpo-=C +" END_INDENT + +" START_INDENT +syn region Text +\ start='foo' +#\ comment +\ end='bar' +" END_INDENT + +" START_INDENT +au CursorHold * echom 'BEFORE bar' +#\ some comment +| echom 'AFTER bar' +" END_INDENT + +" START_INDENT +def MyFunc(text: string, +separator = '-' +): string +enddef +" END_INDENT + +" START_INDENT +def MyFunc( +text: string, +separator = '-' +): string +enddef +" END_INDENT + +" START_INDENT +[var1, var2] = +Func() +" END_INDENT + +" START_INDENT +const list = ['one', +'two'] +" END_INDENT + +" START_INDENT +const list = [ +'one', +'two', +] +" END_INDENT + +" START_INDENT +const dict = {one: 1, +two: 2 +} +" END_INDENT + +" START_INDENT +const dict = { +one: 1, +two: 2 +} +" END_INDENT + +" START_INDENT +if true +const dict = +{ +one: 1, +two: 2 +} +endif +" END_INDENT + +" START_INDENT +def Func() +return { +one: 1 +} +enddef +" END_INDENT + +" START_INDENT +echo { +a: 0, +# b +# c +} +" END_INDENT + +" START_INDENT +echo search( +# comment +'1' +.. '2' +) +" END_INDENT + +" START_INDENT +if true +var v = ( # trailing "(" starts line continuation +3 + 4 # nothing special +) # end of expression indicates continued line +var x: number # needs to align with previous "var" +endif +" END_INDENT + +" START_INDENT +def Func() # {{{ +# comment +if true +return +endif +enddef +" END_INDENT + +" START_INDENT +echo { +key: +'value', +} +" END_INDENT + +" START_INDENT +var id = time +->timer_start((_) => { +n = 0 +}) +" END_INDENT + +" START_INDENT +augroup Name +autocmd! +augroup END +" END_INDENT + +" START_INDENT +var n = +# comment +1 ++ 2 + +var s = '' +" END_INDENT + +" START_INDENT +var keys = { +J: 'j', +"\<Home>": '1G', +"\<End>": 'G', +z: 'zz' +} +" END_INDENT + +" START_INDENT +export def Func( +n: number, +s: string, +...l: list<bool> +) +enddef +" END_INDENT + +" START_INDENT +var heredoc =<< trim ENDD +var nested_heredoc =<< trim END +END +ENDD +" END_INDENT + +" START_INDENT +if true +else " comment +endif +" END_INDENT + +" START_INDENT +if true | echo 'one' | endif +if true | echo 'two' | endif +if true | echo 'three' | endif +" END_INDENT + +" START_INDENT +if true +:'<-1 mark < +else +echo '' +endif +" END_INDENT + +" START_INDENT +substitute/pat /rep / +echo +" END_INDENT + +" START_INDENT +try +echo 1 +catch /pat / # comment +echo 2 +endtry +" END_INDENT + +" START_INDENT +def Func() +Cmd % +enddef +" END_INDENT + +" START_INDENT +if end == 'xxx' || end == 'yyy' +echo +endif +" END_INDENT + +" START_INDENT +if true +popup_move(id, {col: 1, +line: 2}) +endif +setwinvar(id, 'name', 3) +" END_INDENT + +" START_INDENT +var d = [ +{a: 'x', +b: 'y'}, +FuncA(), +FuncB(), +] +" END_INDENT + +" START_INDENT +var ll = [[ +1, +2, +3], [ +4, +5, +6], [ +7, +8, +9]] +" END_INDENT + +" START_INDENT +var ld = [{ +a: 'xxx', +b: 'yyy'}, { +c: 'xxx', +d: 'yyy'}, { +e: 'xxx', +f: 'yyy'}, { +}] +" END_INDENT + +" START_INDENT +var d = { +a: { +b: { +c: [{ +d: 'e', +f: 'g', +h: 'i' +}], +j: 'k', +}, +}, +} +" END_INDENT + +" START_INDENT +if true +var end: any +if true +end = 0 +elseif true +echo +endif +endif +" END_INDENT + +" START_INDENT +if true +var d = { +end: 0} +endif +" END_INDENT + +" START_INDENT +nunmap <buffer> ( +nunmap <buffer> ) +inoremap [ { +inoremap ] } +silent! xunmap i{ +silent! xunmap a{ +" END_INDENT + +" START_INDENT +def Func( +s: string, +n = 1, +m = 2 +) +enddef +" END_INDENT + +" START_INDENT +var h =<< END +text +END + +def Func() +echo +enddef +" END_INDENT + +" START_INDENT +def Func() +var h =<< END +text +END +echo 'test' +enddef +" END_INDENT + +" START_INDENT +def Foo() +lcd - +enddef +def Bar() +echo +enddef +" END_INDENT + +" START_INDENT +if true +n = Func(1, 2, +3) +endif +" END_INDENT + +" START_INDENT +def Func(s: string, +n: number): bool +if true +return false +endif +enddef +" END_INDENT + +" START_INDENT +def Func( +n: number) +# +echo +enddef +" END_INDENT + +" START_INDENT +" INDENT_AT this-line +def Func( + n: number) + # +echo # this-line +enddef +" END_INDENT + +" START_INDENT +if true +if true +normal! == +endif +endif +" END_INDENT + +" START_INDENT +var d = { +a: () => true, +b: () => true +&& true +&& Foo(), +c: () => Bar(), +e: () => Baz(), +} +" END_INDENT + +" START_INDENT +def Select(Cont: func(func(any)), Pred: func(any): bool): func(func(any)) +return (Emit: func(any)) => { +Cont((t: any) => { +if Pred(t) +Emit(t) +endif +}) +} +enddef +" END_INDENT + +" START_INDENT +" INDENT_EXE let g:vim_indent = {'more_in_bracket_block': v:true} +def Select(Cont: func(func(any)), Pred: func(any): bool): func(func(any)) +return (Emit: func(any)) => { +Cont((t: any) => { +if Pred(t) +Emit(t) +endif +}) +} +enddef +" END_INDENT + +" START_INDENT +" INDENT_EXE unlet! g:vim_indent +" END_INDENT + +" START_INDENT +g:lightline = { +'active': { +'left': [ [ 'mode', 'paste' ], [ 'readonly', 'relativepath', 'modified' ] ], +}, +'inactive': { +'left': [ [ 'readonly', 'relativepath', 'modified' ] ], +} +} +" END_INDENT + +" START_INDENT +if getline(1, 10) +->map((_, v: string): number => strcharlen(v)) +->max() > 1'000 +&l:breakindent = false +&l:linebreak = false +else +&l:breakindent = true +&l:linebreak = true +endif +" END_INDENT + +" START_INDENT +var ext2cmd: dict<string> = { +doc: $'antiword {fname}', +docx: $'pandoc --from=docx --to=markdown {fname}', +epub: $'pandoc --from=epub --to=markdown {fname}', +odp: $'odt2txt {fname}', +odt: $'odt2txt {fname}', +pdf: $'pdftotext -nopgbrk -layout -q -eol unix {fname} -', +rtf: 'unrtf --text', +} +" END_INDENT + +" START_INDENT +const ptybuf: number = term_start(&shell, { +hidden: true, +exit_cb: (_, _) => { +if true +close +else +help +endif +} +}) +" END_INDENT + +" START_INDENT +var d = { +a: 0, +# a ' quote {{{ +#}}} +b: 0, +} +" END_INDENT + +" START_INDENT +echo printf('%s () %s', +1, +2 +) +" END_INDENT + +" START_INDENT +prop_add(1, col('.'), { +length: 2, +type: 'test' +}) +" END_INDENT + +" START_INDENT +echo (() => " string starting with space")() +echo +" END_INDENT + +" START_INDENT +var variables = deepcopy(g:) +->filter((k: string, _): bool => +k =~ '\c\V' .. keyword->escape('\') +&& k !~ '\%(loaded\|did_plugin_\)') +->items() +->map((_, v): string => v[0] .. ' = ' .. string(v[1])) +new +" END_INDENT + +" START_INDENT +var d = freq +->map((_, v) => +v * ( +1 ++ 2 +)) +for item in d +->items() +->sort((a, b) => b[1] - a[1]) +echo +endfor +" END_INDENT + +" START_INDENT +make_job = job_start([&shell, &shellcmdflag, make_cmd], { +callback: function(MakeProcessOutput, [qfid]), +close_cb: function(MakeCloseCb, [qfid]), +exit_cb: MakeCompleted, +in_io: 'null' +}) +" END_INDENT + +" START_INDENT +var matching_abbrev: list<dict<string>> = copy(ABBREV) +->filter((_, v: dict<string>): bool => +stridx(v.lhs, word_to_complete) == 0) +->map((_, v: dict<string>) => ({ +word: v.lhs, +menu: AbbrevRhs(v.rhs)->stridx('expand_') >= 0 +? AbbrevRhs(v.rhs)->matchstr('.*,\s*''\zs.*\ze'')') +: AbbrevRhs(v.rhs) +})) +" END_INDENT + +" START_INDENT +def Func() +if true +vimgrep /^\C\s*\%(fu\%[nction]\|def\)\s\+/ file +endif +enddef +" END_INDENT + +" START_INDENT +setlocal iskeyword+=[ +cword = expand('<cword>') +" END_INDENT + +" START_INDENT +silent if true +echo +endif +" END_INDENT + +" START_INDENT +def Func() +sort :^.*[\/]: +enddef +" END_INDENT + +" START_INDENT +def Func() +d = { +} +hd =<< trim END +[' +]' +END +enddef +" END_INDENT + +" START_INDENT +def Func() +if true +var hd =<< trim END +if get(b:, 'current_syntax', '') +endif +END +elseif true +echo +endif +enddef +" END_INDENT + +" START_INDENT +# test for control-flow keyword followed by commented fold marker {{{ +if true +echo +endif #}}} +" END_INDENT + +" START_INDENT +if winsz == 0|let winsz= ""|endif +exe "noswapfile ".winsz."wincmd s" +" END_INDENT + +" START_INDENT +if true +if true +windo if true | echo | endif +augroup Name +autocmd WinLeave * if true | eval 1 + 2 | endif +augroup END +endif +endif +" END_INDENT + +" START_INDENT +if true +echo ' =<< trim END' +->len() +endif +" END_INDENT + +" START_INDENT +function Func() +if true +if true +if true | echo com | endif +if true | echo com | endif +endif +else +endif +endfunction +" END_INDENT + +" START_INDENT +function Func() +if v:true ++ +echo +- +endif +endfunction +" END_INDENT + +" START_INDENT +var matchpairs: string = &matchpairs +var pairs: dict<list<string>> +for [opening: string, closing: string] +in matchpairs +->split(',') +->map((_, v: string): list<string> => split(v, ':')) +pairs[opening] = [escape(opening, '[]'), escape(closing, '[]'), 'nW', 'w$'] +pairs[closing] = [escape(opening, '[]'), escape(closing, '[]'), 'bnW', 'w0'] +endfor +" END_INDENT + +" START_INDENT +{ +echo [] ++ [] ++ [{a: 1, +b: 2}] +} +" END_INDENT + +" START_INDENT +silent! argdel * +edit file +" END_INDENT + +" START_INDENT +def Foo() +Bar(1, +[]->filter((_, v) => { +return true +}), +() => { +echo +}) +enddef +" END_INDENT + +" START_INDENT +echo { +k: () => { +if true +echo +popup_setoptions(id, +{title: 'title'}) +endif +} +} +" END_INDENT + +" START_INDENT +if true +elseif +endif +" END_INDENT + +" START_INDENT +if ( +true) +&& true +echo +endif +" END_INDENT + +" START_INDENT +abstract class Shape +this.color = Color.Black +this.thickness = 10 +endclass +" END_INDENT + +" START_INDENT +class OtherThing +this.size: number +static totalSize: number + +static def ClearTotalSize(): number +var prev = totalSize +totalSize = 0 +return prev +enddef +endclass +" END_INDENT + +" START_INDENT +interface HasSurface +this.size: number +def Surface(): number +endinterface +" END_INDENT + +" START_INDENT +interface EnterExit +def Enter(): void +def Exit(): void +endinterface +" END_INDENT + +" START_INDENT +enum Color +White +Red +Green +Blue +Black +endenum +" END_INDENT diff --git a/runtime/indent/testdir/vim.ok b/runtime/indent/testdir/vim.ok new file mode 100644 index 0000000..b10e081 --- /dev/null +++ b/runtime/indent/testdir/vim.ok @@ -0,0 +1,948 @@ +" vim: set ft=vim sw=4 : + +" START_INDENT +func Some() + let x = 1 +endfunc + +let cmd = + \ 'some ' + \ 'string' + +if 1 + let x = [ + \ ] +endif + +for x in [ + {key: 'value'}, + ] + eval 0 +endfor + +let t = [ + \ { + \ 'k': 'val', + \ }, + \ ] + +def Func() + var d = dd + ->extend({ + }) + eval 0 +enddef +" END_INDENT + +" START_INDENT +" INDENT_EXE let g:vim_indent_cont = 6 + +let cmd = + \ 'some ' + \ 'string' + +" END_INDENT + +" START_INDENT +" INDENT_EXE let g:vim_indent_cont = 5 + +let list = [ + \ 'one', + \ 'two'] + +" END_INDENT + +" START_INDENT +" INDENT_EXE unlet g:vim_indent_cont + +let list = [ + 'one', + 'two', +] +echo + +" END_INDENT + +" START_INDENT +" INDENT_AT this-line +func Some() + let f = x " this-line +endfunc +" END_INDENT + +" START_INDENT +" INDENT_NEXT next-line +func Some() + " next-line + let f = x +endfunc +" END_INDENT + +" START_INDENT +" INDENT_PREV prev-line +func Some() + let f = x +" prev-line +endfunc +" END_INDENT + +" START_INDENT +let a =<< END +nothing +END +" END_INDENT + +" START_INDENT +let a =<< trim END + nothing +END +" END_INDENT + +" START_INDENT +" INDENT_AT this-line +let a=<< trim END + blah + blah + blah this-line +END +" END_INDENT + +" START_INDENT +if v:true + echo 0 +end +" END_INDENT + +" START_INDENT +var result = Func( + arg1, + arg2 +) +" END_INDENT + +" START_INDENT +var result = Func(arg1, + arg2) +" END_INDENT + +" START_INDENT +filter(list, (k, v) => + v > 0) +" END_INDENT + +" START_INDENT +filter(list, (k, v) => { + const x = get(list, k, 0) + return x > 0 +}) +" END_INDENT + +" START_INDENT +if x > 0 + filter(list, (k, v) => { + const x = get(list, k, 1) + return x > 0 + }) +endif +" END_INDENT + +" START_INDENT +{ + var temp = 'temp' +} +" END_INDENT + +" START_INDENT +var text = lead + .. middle + .. end +" END_INDENT + +" START_INDENT +var text = lead .. + middle .. + end +" END_INDENT + +" START_INDENT +var total = start + + end - + correction +" END_INDENT + +" START_INDENT +var result = start +:+ print +" END_INDENT + +" START_INDENT +var result = positive + ? PosFunc(arg) + : NegFunc(arg) +" END_INDENT + +" START_INDENT +var result = GetBuilder() + ->BuilderSetWidth(333) + ->BuilderSetHeight(777) + ->BuilderBuild() +" END_INDENT + +" START_INDENT +var result = MyDict + .member +" END_INDENT + +" START_INDENT +autocmd BufNewFile *.match if condition + | echo 'match' + | endif +" END_INDENT + +" START_INDENT +set cpo+=C +var lines =<< trim END + | this works +END +set cpo-=C +" END_INDENT + +" START_INDENT +syn region Text + \ start='foo' + #\ comment + \ end='bar' +" END_INDENT + +" START_INDENT +au CursorHold * echom 'BEFORE bar' + #\ some comment + | echom 'AFTER bar' +" END_INDENT + +" START_INDENT +def MyFunc(text: string, + separator = '-' + ): string +enddef +" END_INDENT + +" START_INDENT +def MyFunc( + text: string, + separator = '-' + ): string +enddef +" END_INDENT + +" START_INDENT +[var1, var2] = + Func() +" END_INDENT + +" START_INDENT +const list = ['one', + 'two'] +" END_INDENT + +" START_INDENT +const list = [ + 'one', + 'two', +] +" END_INDENT + +" START_INDENT +const dict = {one: 1, + two: 2 +} +" END_INDENT + +" START_INDENT +const dict = { + one: 1, + two: 2 +} +" END_INDENT + +" START_INDENT +if true + const dict = + { + one: 1, + two: 2 + } +endif +" END_INDENT + +" START_INDENT +def Func() + return { + one: 1 + } +enddef +" END_INDENT + +" START_INDENT +echo { + a: 0, + # b + # c +} +" END_INDENT + +" START_INDENT +echo search( + # comment + '1' + .. '2' +) +" END_INDENT + +" START_INDENT +if true + var v = ( # trailing "(" starts line continuation + 3 + 4 # nothing special + ) # end of expression indicates continued line + var x: number # needs to align with previous "var" +endif +" END_INDENT + +" START_INDENT +def Func() # {{{ + # comment + if true + return + endif +enddef +" END_INDENT + +" START_INDENT +echo { + key: + 'value', +} +" END_INDENT + +" START_INDENT +var id = time + ->timer_start((_) => { + n = 0 + }) +" END_INDENT + +" START_INDENT +augroup Name + autocmd! +augroup END +" END_INDENT + +" START_INDENT +var n = + # comment + 1 + + 2 + +var s = '' +" END_INDENT + +" START_INDENT +var keys = { + J: 'j', + "\<Home>": '1G', + "\<End>": 'G', + z: 'zz' +} +" END_INDENT + +" START_INDENT +export def Func( + n: number, + s: string, + ...l: list<bool> + ) +enddef +" END_INDENT + +" START_INDENT +var heredoc =<< trim ENDD + var nested_heredoc =<< trim END + END +ENDD +" END_INDENT + +" START_INDENT +if true +else " comment +endif +" END_INDENT + +" START_INDENT +if true | echo 'one' | endif +if true | echo 'two' | endif +if true | echo 'three' | endif +" END_INDENT + +" START_INDENT +if true + :'<-1 mark < +else + echo '' +endif +" END_INDENT + +" START_INDENT +substitute/pat /rep / +echo +" END_INDENT + +" START_INDENT +try + echo 1 +catch /pat / # comment + echo 2 +endtry +" END_INDENT + +" START_INDENT +def Func() + Cmd % +enddef +" END_INDENT + +" START_INDENT +if end == 'xxx' || end == 'yyy' + echo +endif +" END_INDENT + +" START_INDENT +if true + popup_move(id, {col: 1, + line: 2}) +endif +setwinvar(id, 'name', 3) +" END_INDENT + +" START_INDENT +var d = [ + {a: 'x', + b: 'y'}, + FuncA(), + FuncB(), +] +" END_INDENT + +" START_INDENT +var ll = [[ + 1, + 2, + 3], [ + 4, + 5, + 6], [ + 7, + 8, + 9]] +" END_INDENT + +" START_INDENT +var ld = [{ + a: 'xxx', + b: 'yyy'}, { + c: 'xxx', + d: 'yyy'}, { + e: 'xxx', + f: 'yyy'}, { + }] +" END_INDENT + +" START_INDENT +var d = { + a: { + b: { + c: [{ + d: 'e', + f: 'g', + h: 'i' + }], + j: 'k', + }, + }, +} +" END_INDENT + +" START_INDENT +if true + var end: any + if true + end = 0 + elseif true + echo + endif +endif +" END_INDENT + +" START_INDENT +if true + var d = { + end: 0} +endif +" END_INDENT + +" START_INDENT +nunmap <buffer> ( +nunmap <buffer> ) +inoremap [ { +inoremap ] } +silent! xunmap i{ +silent! xunmap a{ +" END_INDENT + +" START_INDENT +def Func( + s: string, + n = 1, + m = 2 + ) +enddef +" END_INDENT + +" START_INDENT +var h =<< END +text +END + +def Func() + echo +enddef +" END_INDENT + +" START_INDENT +def Func() + var h =<< END +text +END + echo 'test' +enddef +" END_INDENT + +" START_INDENT +def Foo() + lcd - +enddef +def Bar() + echo +enddef +" END_INDENT + +" START_INDENT +if true + n = Func(1, 2, + 3) +endif +" END_INDENT + +" START_INDENT +def Func(s: string, + n: number): bool + if true + return false + endif +enddef +" END_INDENT + +" START_INDENT +def Func( + n: number) + # + echo +enddef +" END_INDENT + +" START_INDENT +" INDENT_AT this-line +def Func( + n: number) + # + echo # this-line +enddef +" END_INDENT + +" START_INDENT +if true + if true + normal! == + endif +endif +" END_INDENT + +" START_INDENT +var d = { + a: () => true, + b: () => true + && true + && Foo(), + c: () => Bar(), + e: () => Baz(), +} +" END_INDENT + +" START_INDENT +def Select(Cont: func(func(any)), Pred: func(any): bool): func(func(any)) + return (Emit: func(any)) => { + Cont((t: any) => { + if Pred(t) + Emit(t) + endif + }) + } +enddef +" END_INDENT + +" START_INDENT +" INDENT_EXE let g:vim_indent = {'more_in_bracket_block': v:true} +def Select(Cont: func(func(any)), Pred: func(any): bool): func(func(any)) + return (Emit: func(any)) => { + Cont((t: any) => { + if Pred(t) + Emit(t) + endif + }) + } +enddef +" END_INDENT + +" START_INDENT +" INDENT_EXE unlet! g:vim_indent +" END_INDENT + +" START_INDENT +g:lightline = { + 'active': { + 'left': [ [ 'mode', 'paste' ], [ 'readonly', 'relativepath', 'modified' ] ], + }, + 'inactive': { + 'left': [ [ 'readonly', 'relativepath', 'modified' ] ], + } +} +" END_INDENT + +" START_INDENT +if getline(1, 10) + ->map((_, v: string): number => strcharlen(v)) + ->max() > 1'000 + &l:breakindent = false + &l:linebreak = false +else + &l:breakindent = true + &l:linebreak = true +endif +" END_INDENT + +" START_INDENT +var ext2cmd: dict<string> = { + doc: $'antiword {fname}', + docx: $'pandoc --from=docx --to=markdown {fname}', + epub: $'pandoc --from=epub --to=markdown {fname}', + odp: $'odt2txt {fname}', + odt: $'odt2txt {fname}', + pdf: $'pdftotext -nopgbrk -layout -q -eol unix {fname} -', + rtf: 'unrtf --text', +} +" END_INDENT + +" START_INDENT +const ptybuf: number = term_start(&shell, { + hidden: true, + exit_cb: (_, _) => { + if true + close + else + help + endif + } +}) +" END_INDENT + +" START_INDENT +var d = { + a: 0, + # a ' quote {{{ + #}}} + b: 0, +} +" END_INDENT + +" START_INDENT +echo printf('%s () %s', + 1, + 2 +) +" END_INDENT + +" START_INDENT +prop_add(1, col('.'), { + length: 2, + type: 'test' +}) +" END_INDENT + +" START_INDENT +echo (() => " string starting with space")() +echo +" END_INDENT + +" START_INDENT +var variables = deepcopy(g:) + ->filter((k: string, _): bool => + k =~ '\c\V' .. keyword->escape('\') + && k !~ '\%(loaded\|did_plugin_\)') + ->items() + ->map((_, v): string => v[0] .. ' = ' .. string(v[1])) +new +" END_INDENT + +" START_INDENT +var d = freq + ->map((_, v) => + v * ( + 1 + + 2 + )) +for item in d + ->items() + ->sort((a, b) => b[1] - a[1]) + echo +endfor +" END_INDENT + +" START_INDENT +make_job = job_start([&shell, &shellcmdflag, make_cmd], { + callback: function(MakeProcessOutput, [qfid]), + close_cb: function(MakeCloseCb, [qfid]), + exit_cb: MakeCompleted, + in_io: 'null' +}) +" END_INDENT + +" START_INDENT +var matching_abbrev: list<dict<string>> = copy(ABBREV) + ->filter((_, v: dict<string>): bool => + stridx(v.lhs, word_to_complete) == 0) + ->map((_, v: dict<string>) => ({ + word: v.lhs, + menu: AbbrevRhs(v.rhs)->stridx('expand_') >= 0 + ? AbbrevRhs(v.rhs)->matchstr('.*,\s*''\zs.*\ze'')') + : AbbrevRhs(v.rhs) + })) +" END_INDENT + +" START_INDENT +def Func() + if true + vimgrep /^\C\s*\%(fu\%[nction]\|def\)\s\+/ file + endif +enddef +" END_INDENT + +" START_INDENT +setlocal iskeyword+=[ +cword = expand('<cword>') +" END_INDENT + +" START_INDENT +silent if true + echo +endif +" END_INDENT + +" START_INDENT +def Func() + sort :^.*[\/]: +enddef +" END_INDENT + +" START_INDENT +def Func() + d = { + } + hd =<< trim END + [' + ]' + END +enddef +" END_INDENT + +" START_INDENT +def Func() + if true + var hd =<< trim END + if get(b:, 'current_syntax', '') + endif + END + elseif true + echo + endif +enddef +" END_INDENT + +" START_INDENT +# test for control-flow keyword followed by commented fold marker {{{ +if true + echo +endif #}}} +" END_INDENT + +" START_INDENT +if winsz == 0|let winsz= ""|endif +exe "noswapfile ".winsz."wincmd s" +" END_INDENT + +" START_INDENT +if true + if true + windo if true | echo | endif + augroup Name + autocmd WinLeave * if true | eval 1 + 2 | endif + augroup END + endif +endif +" END_INDENT + +" START_INDENT +if true + echo ' =<< trim END' + ->len() +endif +" END_INDENT + +" START_INDENT +function Func() + if true + if true + if true | echo com | endif + if true | echo com | endif + endif + else + endif +endfunction +" END_INDENT + +" START_INDENT +function Func() + if v:true + + + echo + - + endif +endfunction +" END_INDENT + +" START_INDENT +var matchpairs: string = &matchpairs +var pairs: dict<list<string>> +for [opening: string, closing: string] + in matchpairs + ->split(',') + ->map((_, v: string): list<string> => split(v, ':')) + pairs[opening] = [escape(opening, '[]'), escape(closing, '[]'), 'nW', 'w$'] + pairs[closing] = [escape(opening, '[]'), escape(closing, '[]'), 'bnW', 'w0'] +endfor +" END_INDENT + +" START_INDENT +{ + echo [] + + [] + + [{a: 1, + b: 2}] +} +" END_INDENT + +" START_INDENT +silent! argdel * +edit file +" END_INDENT + +" START_INDENT +def Foo() + Bar(1, + []->filter((_, v) => { + return true + }), + () => { + echo + }) +enddef +" END_INDENT + +" START_INDENT +echo { + k: () => { + if true + echo + popup_setoptions(id, + {title: 'title'}) + endif + } +} +" END_INDENT + +" START_INDENT +if true +elseif +endif +" END_INDENT + +" START_INDENT +if ( + true) + && true + echo +endif +" END_INDENT + +" START_INDENT +abstract class Shape + this.color = Color.Black + this.thickness = 10 +endclass +" END_INDENT + +" START_INDENT +class OtherThing + this.size: number + static totalSize: number + + static def ClearTotalSize(): number + var prev = totalSize + totalSize = 0 + return prev + enddef +endclass +" END_INDENT + +" START_INDENT +interface HasSurface + this.size: number + def Surface(): number +endinterface +" END_INDENT + +" START_INDENT +interface EnterExit + def Enter(): void + def Exit(): void +endinterface +" END_INDENT + +" START_INDENT +enum Color + White + Red + Green + Blue + Black +endenum +" END_INDENT diff --git a/runtime/indent/testdir/xml.in b/runtime/indent/testdir/xml.in new file mode 100644 index 0000000..88ad51e --- /dev/null +++ b/runtime/indent/testdir/xml.in @@ -0,0 +1,32 @@ +<!-- vim: set ft=xml ts=8 sw=0 sts=-1 et : --> +<!-- START_INDENT --> +<?xml version="1.0" encoding="utf-8"?> +<tag0> + <tag1> +<!-- comment --> +<tag2> + <tag3/> +</tag2> +<!-- text comment --> + +<!-- +text comment +--> +</tag1> +<!-- +text comment +end comment --> +</tag0> +<!-- END_INDENT --> + +<!-- START_INDENT --> +<?xml version="1.0" encoding="utf-8"?> +<tag0> + <tag1> +<!-- comment --> +<tag2> + <tag3/> +</tag2> +</tag1> +</tag0> +<!-- END_INDENT --> diff --git a/runtime/indent/testdir/xml.ok b/runtime/indent/testdir/xml.ok new file mode 100644 index 0000000..d5e2289 --- /dev/null +++ b/runtime/indent/testdir/xml.ok @@ -0,0 +1,32 @@ +<!-- vim: set ft=xml ts=8 sw=0 sts=-1 et : --> +<!-- START_INDENT --> +<?xml version="1.0" encoding="utf-8"?> +<tag0> + <tag1> + <!-- comment --> + <tag2> + <tag3/> + </tag2> + <!-- text comment --> + + <!-- + text comment + --> + </tag1> + <!-- + text comment + end comment --> +</tag0> +<!-- END_INDENT --> + +<!-- START_INDENT --> +<?xml version="1.0" encoding="utf-8"?> +<tag0> + <tag1> + <!-- comment --> + <tag2> + <tag3/> + </tag2> + </tag1> +</tag0> +<!-- END_INDENT --> diff --git a/runtime/indent/testdir/yaml.in b/runtime/indent/testdir/yaml.in new file mode 100644 index 0000000..bf99668 --- /dev/null +++ b/runtime/indent/testdir/yaml.in @@ -0,0 +1,20 @@ +# vim: set ft=yaml sw=2 et : + +# START_INDENT +map1: +sub1: +- list item +map2: +- another list +# END_INDENT + +# START_INDENT +map: &anchor +map: val +# END_INDENT + +# START_INDENT +map: | +line1 +line2 +# END_INDENT diff --git a/runtime/indent/testdir/yaml.ok b/runtime/indent/testdir/yaml.ok new file mode 100644 index 0000000..8b38633 --- /dev/null +++ b/runtime/indent/testdir/yaml.ok @@ -0,0 +1,20 @@ +# vim: set ft=yaml sw=2 et : + +# START_INDENT +map1: + sub1: + - list item +map2: + - another list +# END_INDENT + +# START_INDENT +map: &anchor +map: val +# END_INDENT + +# START_INDENT +map: | + line1 + line2 +# END_INDENT diff --git a/runtime/indent/tex.vim b/runtime/indent/tex.vim new file mode 100644 index 0000000..68d13fb --- /dev/null +++ b/runtime/indent/tex.vim @@ -0,0 +1,425 @@ +" Vim indent file +" Language: LaTeX +" Maintainer: Yichao Zhou <broken.zhou AT gmail.com> +" Created: Sat, 16 Feb 2002 16:50:19 +0100 +" Version: 1.0.0 +" Please email me if you found something I can do. Comments, bug report and +" feature request are welcome. + +" Last Update: {{{ +" 25th Sep 2002, by LH : +" (*) better support for the option +" (*) use some regex instead of several '||'. +" Oct 9th, 2003, by JT: +" (*) don't change indentation of lines starting with '%' +" 2005/06/15, Moshe Kaminsky <kaminsky AT math.huji.ac.il> +" (*) New variables: +" g:tex_items, g:tex_itemize_env, g:tex_noindent_env +" 2011/3/6, by Yichao Zhou <broken.zhou AT gmail.com> +" (*) Don't change indentation of lines starting with '%' +" I don't see any code with '%' and it doesn't work properly +" so I add some code. +" (*) New features: Add smartindent-like indent for "{}" and "[]". +" (*) New variables: g:tex_indent_brace +" 2011/9/25, by Yichao Zhou <broken.zhou AT gmail.com> +" (*) Bug fix: smartindent-like indent for "[]" +" (*) New features: Align with "&". +" (*) New variable: g:tex_indent_and. +" 2011/10/23 by Yichao Zhou <broken.zhou AT gmail.com> +" (*) Bug fix: improve the smartindent-like indent for "{}" and +" "[]". +" 2012/02/27 by Yichao Zhou <broken.zhou AT gmail.com> +" (*) Bug fix: support default folding marker. +" (*) Indent with "&" is not very handy. Make it not enable by +" default. +" 2012/03/06 by Yichao Zhou <broken.zhou AT gmail.com> +" (*) Modify "&" behavior and make it default again. Now "&" +" won't align when there are more then one "&" in the previous +" line. +" (*) Add indent "\left(" and "\right)" +" (*) Trust user when in "verbatim" and "lstlisting" +" 2012/03/11 by Yichao Zhou <broken.zhou AT gmail.com> +" (*) Modify "&" so that only indent when current line start with +" "&". +" 2012/03/12 by Yichao Zhou <broken.zhou AT gmail.com> +" (*) Modify indentkeys. +" 2012/03/18 by Yichao Zhou <broken.zhou AT gmail.com> +" (*) Add &cpo +" 2013/05/02 by Yichao Zhou <broken.zhou AT gmail.com> +" (*) Fix problem about GetTeXIndent checker. Thank Albert Netymk +" for reporting this. +" 2014/06/23 by Yichao Zhou <broken.zhou AT gmail.com> +" (*) Remove the feature g:tex_indent_and because it is buggy. +" (*) If there is not any obvious indentation hints, we do not +" alert our user's current indentation. +" (*) g:tex_indent_brace now only works if the open brace is the +" last character of that line. +" 2014/08/03 by Yichao Zhou <broken.zhou AT gmail.com> +" (*) Indent current line if last line has larger indentation +" 2016/11/08 by Yichao Zhou <broken.zhou AT gmail.com> +" (*) Fix problems for \[ and \]. Thanks Bruno for reporting. +" 2017/04/30 by Yichao Zhou <broken.zhou AT gmail.com> +" (*) Fix a bug between g:tex_noindent_env and g:tex_indent_items +" Now g:tex_noindent_env='document\|verbatim\|itemize' (Emacs +" style) is supported. Thanks Miles Wheeler for reporting. +" 2018/02/07 by Yichao Zhou <broken.zhou AT gmail.com> +" (*) Make indentation more smart in the normal mode +" 2020/04/26 by Yichao Zhou <broken.zhou AT gmail.com> +" (*) Fix a bug related to \[ & \]. Thanks Manuel Boni for +" reporting. +" 2023/08/28 by Vim Project +" (*) Set b:undo_indent. +" }}} + +" Document: {{{ +" +" For proper latex experience, please put +" let g:tex_flavor = "latex" +" into your vimrc. +" +" * g:tex_indent_brace +" +" If this variable is unset or non-zero, it will use smartindent-like style +" for "{}" and "[]". Now this only works if the open brace is the last +" character of that line. +" +" % Example 1 +" \usetikzlibrary{ +" external +" } +" +" % Example 2 +" \tikzexternalize[ +" prefix=tikz] +" +" * g:tex_indent_items +" +" If this variable is set, item-environments are indented like Emacs does +" it, i.e., continuation lines are indented with a shiftwidth. +" +" set unset +" ------------------------------------------------------ +" \begin{itemize} \begin{itemize} +" \item blablabla \item blablabla +" bla bla bla bla bla bla +" \item blablabla \item blablabla +" bla bla bla bla bla bla +" \end{itemize} \end{itemize} +" +" +" * g:tex_items +" +" A list of tokens to be considered as commands for the beginning of an item +" command. The tokens should be separated with '\|'. The initial '\' should +" be escaped. The default is '\\bibitem\|\\item'. +" +" * g:tex_itemize_env +" +" A list of environment names, separated with '\|', where the items (item +" commands matching g:tex_items) may appear. The default is +" 'itemize\|description\|enumerate\|thebibliography'. +" +" * g:tex_noindent_env +" +" A list of environment names. separated with '\|', where no indentation is +" required. The default is 'document\|verbatim'. +" }}} + +" Only define the function once +if exists("b:did_indent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +" Define global variable {{{ + +let b:did_indent = 1 + +if !exists("g:tex_indent_items") + let g:tex_indent_items = 1 +endif +if !exists("g:tex_indent_brace") + let g:tex_indent_brace = 1 +endif +if !exists("g:tex_max_scan_line") + let g:tex_max_scan_line = 60 +endif +if g:tex_indent_items + if !exists("g:tex_itemize_env") + let g:tex_itemize_env = 'itemize\|description\|enumerate\|thebibliography' + endif + if !exists('g:tex_items') + let g:tex_items = '\\bibitem\|\\item' + endif +else + let g:tex_items = '' +endif + +if !exists("g:tex_noindent_env") + let g:tex_noindent_env = 'document\|verbatim\|lstlisting' +endif "}}} + +" VIM Setting " {{{ +setlocal autoindent +setlocal nosmartindent +setlocal indentexpr=GetTeXIndent() +setlocal indentkeys& +exec 'setlocal indentkeys+=[,(,{,),},],\&' . substitute(g:tex_items, '^\|\(\\|\)', ',=', 'g') +let g:tex_items = '^\s*' . substitute(g:tex_items, '^\(\^\\s\*\)*', '', '') +let b:undo_indent = "setlocal autoindent< indentexpr< indentkeys< smartindent<" +" }}} + +function! GetTeXIndent() " {{{ + " Find a non-blank line above the current line. + let lnum = prevnonblank(v:lnum - 1) + let cnum = v:lnum + + " Comment line is not what we need. + while lnum != 0 && getline(lnum) =~ '^\s*%' + let lnum = prevnonblank(lnum - 1) + endwhile + + " At the start of the file use zero indent. + if lnum == 0 + return 0 + endif + + let line = substitute(getline(lnum), '\s*%.*', '','g') " last line + let cline = substitute(getline(v:lnum), '\s*%.*', '', 'g') " current line + + let ccol = 1 + while cline[ccol] =~ '\s' + let ccol += 1 + endwhile + + " We are in verbatim, so do what our user what. + if synIDattr(synID(v:lnum, ccol, 1), "name") == "texZone" + if empty(cline) + return indent(lnum) + else + return indent(v:lnum) + endif + endif + + if lnum == 0 + return 0 + endif + + let ind = indent(lnum) + let stay = 1 + + " New code for comment: retain the indent of current line + if cline =~ '^\s*%' + return indent(v:lnum) + endif + + " Add a 'shiftwidth' after beginning of environments. + " Don't add it for \begin{document} and \begin{verbatim} + " if line =~ '^\s*\\begin{\(.*\)}' && line !~ 'verbatim' + " LH modification : \begin does not always start a line + " ZYC modification : \end after \begin won't cause wrong indent anymore + if line =~ '\\begin{.*}' + if line !~ g:tex_noindent_env + let ind = ind + shiftwidth() + let stay = 0 + endif + + if g:tex_indent_items + " Add another sw for item-environments + if line =~ g:tex_itemize_env + let ind = ind + shiftwidth() + let stay = 0 + endif + endif + endif + + if cline =~ '\\end{.*}' + let retn = s:GetEndIndentation(v:lnum) + if retn != -1 + return retn + endif + end + " Subtract a 'shiftwidth' when an environment ends + if cline =~ '\\end{.*}' + \ && cline !~ g:tex_noindent_env + \ && cline !~ '\\begin{.*}.*\\end{.*}' + if g:tex_indent_items + " Remove another sw for item-environments + if cline =~ g:tex_itemize_env + let ind = ind - shiftwidth() + let stay = 0 + endif + endif + + let ind = ind - shiftwidth() + let stay = 0 + endif + + if g:tex_indent_brace + if line =~ '[[{]$' + let ind += shiftwidth() + let stay = 0 + endif + + if cline =~ '^\s*\\\?[\]}]' && s:CheckPairedIsLastCharacter(v:lnum, ccol) + let ind -= shiftwidth() + let stay = 0 + endif + + if line !~ '^\s*\\\?[\]}]' + for i in range(1, strlen(line)-1) + let char = line[i] + if char == ']' || char == '}' + if s:CheckPairedIsLastCharacter(lnum, i) + let ind -= shiftwidth() + let stay = 0 + endif + endif + endfor + endif + endif + + " Special treatment for 'item' + " ---------------------------- + + if g:tex_indent_items + " '\item' or '\bibitem' itself: + if cline =~ g:tex_items + let ind = ind - shiftwidth() + let stay = 0 + endif + " lines following to '\item' are indented once again: + if line =~ g:tex_items + let ind = ind + shiftwidth() + let stay = 0 + endif + endif + + if stay && mode() == 'i' + " If there is no obvious indentation hint, and indentation is triggered + " in insert mode, we trust our user. + if empty(cline) + return ind + else + return max([indent(v:lnum), s:GetLastBeginIndentation(v:lnum)]) + endif + else + return ind + endif +endfunction "}}} + +function! s:GetLastBeginIndentation(lnum) " {{{ + let matchend = 1 + for lnum in range(a:lnum-1, max([a:lnum - g:tex_max_scan_line, 1]), -1) + let line = getline(lnum) + if line =~ '\\end{.*}' + let matchend += 1 + endif + if line =~ '\\begin{.*}' + let matchend -= 1 + endif + if matchend == 0 + if line =~ g:tex_noindent_env + return indent(lnum) + endif + if line =~ g:tex_itemize_env + return indent(lnum) + 2 * shiftwidth() + endif + return indent(lnum) + shiftwidth() + endif + endfor + return -1 +endfunction + +function! s:GetEndIndentation(lnum) " {{{ + if getline(a:lnum) =~ '\\begin{.*}.*\\end{.*}' + return -1 + endif + + let min_indent = 100 + let matchend = 1 + for lnum in range(a:lnum-1, max([a:lnum-g:tex_max_scan_line, 1]), -1) + let line = getline(lnum) + if line =~ '\\end{.*}' + let matchend += 1 + endif + if line =~ '\\begin{.*}' + let matchend -= 1 + endif + if matchend == 0 + return indent(lnum) + endif + if !empty(line) + let min_indent = min([min_indent, indent(lnum)]) + endif + endfor + return min_indent - shiftwidth() +endfunction + +" Most of the code is from matchparen.vim +function! s:CheckPairedIsLastCharacter(lnum, col) "{{{ + let c_lnum = a:lnum + let c_col = a:col+1 + + let line = getline(c_lnum) + if line[c_col-1] == '\' + let c_col = c_col + 1 + endif + let c = line[c_col-1] + + let plist = split(&matchpairs, '.\zs[:,]') + let i = index(plist, c) + if i < 0 + return 0 + endif + + " Figure out the arguments for searchpairpos(). + if i % 2 == 0 + let s_flags = 'nW' + let c2 = plist[i + 1] + else + let s_flags = 'nbW' + let c2 = c + let c = plist[i - 1] + endif + if c == '[' + let c = '\[' + let c2 = '\]' + endif + + " Find the match. When it was just before the cursor move it there for a + " moment. + let save_cursor = winsaveview() + call cursor(c_lnum, c_col) + + " When not in a string or comment ignore matches inside them. + " We match "escape" for special items, such as lispEscapeSpecial. + let s_skip ='synIDattr(synID(line("."), col("."), 0), "name") ' . + \ '=~? "string\\|character\\|singlequote\\|escape\\|comment"' + execute 'if' s_skip '| let s_skip = 0 | endif' + + let stopline = max([0, c_lnum - g:tex_max_scan_line]) + + " Limit the search time to 300 msec to avoid a hang on very long lines. + " This fails when a timeout is not supported. + try + let [m_lnum, m_col] = searchpairpos(c, '', c2, s_flags, s_skip, stopline, 100) + catch /E118/ + endtry + + call winrestview(save_cursor) + + if m_lnum > 0 + let line = getline(m_lnum) + return strlen(line) == m_col + endif + + return 0 +endfunction "}}} + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim: set sw=4 textwidth=80: diff --git a/runtime/indent/tf.vim b/runtime/indent/tf.vim new file mode 100644 index 0000000..bb40bf6 --- /dev/null +++ b/runtime/indent/tf.vim @@ -0,0 +1,74 @@ +" Vim indent file +" Language: tf (TinyFugue) +" Maintainer: Christian J. Robinson <heptite@gmail.com> +" URL: http://www.vim.org/scripts/script.php?script_id=174 +" Last Change: 2022 Apr 25 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetTFIndent() +setlocal indentkeys-=0{,0} indentkeys-=0# indentkeys-=: +setlocal indentkeys+==/endif,=/then,=/else,=/done,0; + +let b:undo_indent = "setlocal indentexpr< indentkeys<" + +" Only define the function once: +if exists("*GetTFIndent") + finish +endif + +function GetTFIndent() + " Find a non-blank line above the current line: + let lnum = prevnonblank(v:lnum - 1) + + " No indent for the start of the file: + if lnum == 0 + return 0 + endif + + let ind = indent(lnum) + let line = getline(lnum) + + " No indentation if the previous line didn't end with "\": + " (Could be annoying, but it lets you know if you made a mistake.) + if line !~ '\\$' + return 0 + endif + + if line =~ '\(/def.*\\\|/for.*\(%;\s*\)\@\<!\\\)$' + let ind = ind + shiftwidth() + elseif line =~ '\(/if\|/else\|/then\)' + if line !~ '/endif' + let ind = ind + shiftwidth() + endif + elseif line =~ '/while' + if line !~ '/done' + let ind = ind + shiftwidth() + endif + endif + + let line = getline(v:lnum) + + if line =~ '\(/else\|/endif\|/then\)' + if line !~ '/if' + let ind = ind - shiftwidth() + endif + elseif line =~ '/done' + if line !~ '/while' + let ind = ind - shiftwidth() + endif + endif + + " Comments at the beginning of a line: + if line =~ '^\s*;' + let ind = 0 + endif + + + return ind + +endfunction diff --git a/runtime/indent/tilde.vim b/runtime/indent/tilde.vim new file mode 100644 index 0000000..8658035 --- /dev/null +++ b/runtime/indent/tilde.vim @@ -0,0 +1,39 @@ +"Description: Indent scheme for the tilde weblanguage +"Author: Tobias Rundström <tobi@tobi.nu> (Invalid email address) +"URL: http://tilde.tildesoftware.net +"Last Change: May 8 09:15:09 CEST 2002 +" 2022 April: b:undo_indent added by Doug Kearns + +if exists ("b:did_indent") + finish +endif + +let b:did_indent = 1 + +setlocal autoindent +setlocal indentexpr=GetTildeIndent(v:lnum) +setlocal indentkeys=o,O,) + +let b:undo_indent = "setl ai< inde< indk<" + +if exists("*GetTildeIndent") + finish +endif + +function GetTildeIndent(lnum) + let plnum = prevnonblank(v:lnum-1) + + if plnum == 0 + return 0 + endif + + if getline(v:lnum) =~ '^\s*\~\(endif\|else\|elseif\|end\)\>' + return indent(v:lnum) - shiftwidth() + endif + + if getline(plnum) =~ '^\s*\~\(if\|foreach\|foreach_row\|xml_loop\|file_loop\|file_write\|file_append\|imap_loopsections\|imap_index\|imap_list\|ldap_search\|post_loopall\|post_loop\|file_loop\|sql_loop_num\|sql_dbmsselect\|search\|sql_loop\|post\|for\|function_define\|silent\|while\|setvalbig\|mail_create\|systempipe\|mail_send\|dual\|elseif\|else\)\>' + return indent(plnum) + shiftwidth() + else + return -1 + endif +endfunction diff --git a/runtime/indent/treetop.vim b/runtime/indent/treetop.vim new file mode 100644 index 0000000..6c39c69 --- /dev/null +++ b/runtime/indent/treetop.vim @@ -0,0 +1,41 @@ +" Vim indent file +" Language: Treetop +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Last Change: 2022 April 25 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetTreetopIndent() +setlocal indentkeys=0{,0},!^F,o,O,=end +setlocal nosmartindent + +let b:undo_indent = "setl inde< indk< si<" + +if exists("*GetTreetopIndent") + finish +endif + +function GetTreetopIndent() + let pnum = prevnonblank(v:lnum - 1) + if pnum == 0 + return 0 + endif + + let ind = indent(pnum) + let line = getline(pnum) + + if line =~ '^\s*\%(grammar\|module\|rule\)\>' + let ind += shiftwidth() + endif + + let line = getline(v:lnum) + if line =~ '^\s*end\>' + let ind -= shiftwidth() + end + + return ind +endfunction diff --git a/runtime/indent/typescript.vim b/runtime/indent/typescript.vim new file mode 100644 index 0000000..e26750b --- /dev/null +++ b/runtime/indent/typescript.vim @@ -0,0 +1,506 @@ +" Vim indent file +" Language: TypeScript +" Maintainer: See https://github.com/HerringtonDarkholme/yats.vim +" Last Change: 2019 Oct 18 +" 2023 Aug 28 by Vim Project (undo_indent) +" Acknowledgement: Based off of vim-ruby maintained by Nikolai Weibull http://vim-ruby.rubyforge.org + +" 0. Initialization {{{1 +" ================= + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal nosmartindent + +" Now, set up our indentation expression and keys that trigger it. +setlocal indentexpr=GetTypescriptIndent() +setlocal formatexpr=Fixedgq(v:lnum,v:count) +setlocal indentkeys=0{,0},0),0],0\,,!^F,o,O,e + +let b:undo_indent = "setlocal formatexpr< indentexpr< indentkeys< smartindent<" + +" Only define the function once. +if exists("*GetTypescriptIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +" 1. Variables {{{1 +" ============ + +let s:js_keywords = '^\s*\(break\|case\|catch\|continue\|debugger\|default\|delete\|do\|else\|finally\|for\|function\|if\|in\|instanceof\|new\|return\|switch\|this\|throw\|try\|typeof\|var\|void\|while\|with\)' + +" Regex of syntax group names that are or delimit string or are comments. +let s:syng_strcom = 'string\|regex\|comment\c' + +" Regex of syntax group names that are strings. +let s:syng_string = 'regex\c' + +" Regex of syntax group names that are strings or documentation. +let s:syng_multiline = 'comment\c' + +" Regex of syntax group names that are line comment. +let s:syng_linecom = 'linecomment\c' + +" Expression used to check whether we should skip a match with searchpair(). +let s:skip_expr = "synIDattr(synID(line('.'),col('.'),1),'name') =~ '".s:syng_strcom."'" + +let s:line_term = '\s*\%(\%(\/\/\).*\)\=$' + +" Regex that defines continuation lines, not including (, {, or [. +let s:continuation_regex = '\%([\\*+/.:]\|\%(<%\)\@<![=-]\|\W[|&?]\|||\|&&\|[^=]=[^=].*,\)' . s:line_term + +" Regex that defines continuation lines. +" TODO: this needs to deal with if ...: and so on +let s:msl_regex = s:continuation_regex + +let s:one_line_scope_regex = '\<\%(if\|else\|for\|while\)\>[^{;]*' . s:line_term + +" Regex that defines blocks. +let s:block_regex = '\%([{[]\)\s*\%(|\%([*@]\=\h\w*,\=\s*\)\%(,\s*[*@]\=\h\w*\)*|\)\=' . s:line_term + +let s:var_stmt = '^\s*var' + +let s:comma_first = '^\s*,' +let s:comma_last = ',\s*$' + +let s:ternary = '^\s\+[?|:]' +let s:ternary_q = '^\s\+?' + +" 2. Auxiliary Functions {{{1 +" ====================== + +" Check if the character at lnum:col is inside a string, comment, or is ascii. +function s:IsInStringOrComment(lnum, col) + return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_strcom +endfunction + +" Check if the character at lnum:col is inside a string. +function s:IsInString(lnum, col) + return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_string +endfunction + +" Check if the character at lnum:col is inside a multi-line comment. +function s:IsInMultilineComment(lnum, col) + return !s:IsLineComment(a:lnum, a:col) && synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_multiline +endfunction + +" Check if the character at lnum:col is a line comment. +function s:IsLineComment(lnum, col) + return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_linecom +endfunction + +" Find line above 'lnum' that isn't empty, in a comment, or in a string. +function s:PrevNonBlankNonString(lnum) + let in_block = 0 + let lnum = prevnonblank(a:lnum) + while lnum > 0 + " Go in and out of blocks comments as necessary. + " If the line isn't empty (with opt. comment) or in a string, end search. + let line = getline(lnum) + if line =~ '/\*' + if in_block + let in_block = 0 + else + break + endif + elseif !in_block && line =~ '\*/' + let in_block = 1 + elseif !in_block && line !~ '^\s*\%(//\).*$' && !(s:IsInStringOrComment(lnum, 1) && s:IsInStringOrComment(lnum, strlen(line))) + break + endif + let lnum = prevnonblank(lnum - 1) + endwhile + return lnum +endfunction + +" Find line above 'lnum' that started the continuation 'lnum' may be part of. +function s:GetMSL(lnum, in_one_line_scope) + " Start on the line we're at and use its indent. + let msl = a:lnum + let lnum = s:PrevNonBlankNonString(a:lnum - 1) + while lnum > 0 + " If we have a continuation line, or we're in a string, use line as MSL. + " Otherwise, terminate search as we have found our MSL already. + let line = getline(lnum) + let col = match(line, s:msl_regex) + 1 + if (col > 0 && !s:IsInStringOrComment(lnum, col)) || s:IsInString(lnum, strlen(line)) + let msl = lnum + else + " Don't use lines that are part of a one line scope as msl unless the + " flag in_one_line_scope is set to 1 + " + if a:in_one_line_scope + break + end + let msl_one_line = s:Match(lnum, s:one_line_scope_regex) + if msl_one_line == 0 + break + endif + endif + let lnum = s:PrevNonBlankNonString(lnum - 1) + endwhile + return msl +endfunction + +function s:RemoveTrailingComments(content) + let single = '\/\/\(.*\)\s*$' + let multi = '\/\*\(.*\)\*\/\s*$' + return substitute(substitute(a:content, single, '', ''), multi, '', '') +endfunction + +" Find if the string is inside var statement (but not the first string) +function s:InMultiVarStatement(lnum) + let lnum = s:PrevNonBlankNonString(a:lnum - 1) + +" let type = synIDattr(synID(lnum, indent(lnum) + 1, 0), 'name') + + " loop through previous expressions to find a var statement + while lnum > 0 + let line = getline(lnum) + + " if the line is a js keyword + if (line =~ s:js_keywords) + " check if the line is a var stmt + " if the line has a comma first or comma last then we can assume that we + " are in a multiple var statement + if (line =~ s:var_stmt) + return lnum + endif + + " other js keywords, not a var + return 0 + endif + + let lnum = s:PrevNonBlankNonString(lnum - 1) + endwhile + + " beginning of program, not a var + return 0 +endfunction + +" Find line above with beginning of the var statement or returns 0 if it's not +" this statement +function s:GetVarIndent(lnum) + let lvar = s:InMultiVarStatement(a:lnum) + let prev_lnum = s:PrevNonBlankNonString(a:lnum - 1) + + if lvar + let line = s:RemoveTrailingComments(getline(prev_lnum)) + + " if the previous line doesn't end in a comma, return to regular indent + if (line !~ s:comma_last) + return indent(prev_lnum) - shiftwidth() + else + return indent(lvar) + shiftwidth() + endif + endif + + return -1 +endfunction + + +" Check if line 'lnum' has more opening brackets than closing ones. +function s:LineHasOpeningBrackets(lnum) + let open_0 = 0 + let open_2 = 0 + let open_4 = 0 + let line = getline(a:lnum) + let pos = match(line, '[][(){}]', 0) + while pos != -1 + if !s:IsInStringOrComment(a:lnum, pos + 1) + let idx = stridx('(){}[]', line[pos]) + if idx % 2 == 0 + let open_{idx} = open_{idx} + 1 + else + let open_{idx - 1} = open_{idx - 1} - 1 + endif + endif + let pos = match(line, '[][(){}]', pos + 1) + endwhile + return (open_0 > 0) . (open_2 > 0) . (open_4 > 0) +endfunction + +function s:Match(lnum, regex) + let col = match(getline(a:lnum), a:regex) + 1 + return col > 0 && !s:IsInStringOrComment(a:lnum, col) ? col : 0 +endfunction + +function s:IndentWithContinuation(lnum, ind, width) + " Set up variables to use and search for MSL to the previous line. + let p_lnum = a:lnum + let lnum = s:GetMSL(a:lnum, 1) + let line = getline(lnum) + + " If the previous line wasn't a MSL and is continuation return its indent. + " TODO: the || s:IsInString() thing worries me a bit. + if p_lnum != lnum + if s:Match(p_lnum,s:continuation_regex)||s:IsInString(p_lnum,strlen(line)) + return a:ind + endif + endif + + " Set up more variables now that we know we aren't continuation bound. + let msl_ind = indent(lnum) + + " If the previous line ended with [*+/.-=], start a continuation that + " indents an extra level. + if s:Match(lnum, s:continuation_regex) + if lnum == p_lnum + return msl_ind + a:width + else + return msl_ind + endif + endif + + return a:ind +endfunction + +function s:InOneLineScope(lnum) + let msl = s:GetMSL(a:lnum, 1) + if msl > 0 && s:Match(msl, s:one_line_scope_regex) + return msl + endif + return 0 +endfunction + +function s:ExitingOneLineScope(lnum) + let msl = s:GetMSL(a:lnum, 1) + if msl > 0 + " if the current line is in a one line scope .. + if s:Match(msl, s:one_line_scope_regex) + return 0 + else + let prev_msl = s:GetMSL(msl - 1, 1) + if s:Match(prev_msl, s:one_line_scope_regex) + return prev_msl + endif + endif + endif + return 0 +endfunction + +" 3. GetTypescriptIndent Function {{{1 +" ========================= + +function GetTypescriptIndent() + " 3.1. Setup {{{2 + " ---------- + + " Set up variables for restoring position in file. Could use v:lnum here. + let vcol = col('.') + + " 3.2. Work on the current line {{{2 + " ----------------------------- + + let ind = -1 + " Get the current line. + let line = getline(v:lnum) + " previous nonblank line number + let prevline = prevnonblank(v:lnum - 1) + + " If we got a closing bracket on an empty line, find its match and indent + " according to it. For parentheses we indent to its column - 1, for the + " others we indent to the containing line's MSL's level. Return -1 if fail. + let col = matchend(line, '^\s*[],})]') + if col > 0 && !s:IsInStringOrComment(v:lnum, col) + call cursor(v:lnum, col) + + let lvar = s:InMultiVarStatement(v:lnum) + if lvar + let prevline_contents = s:RemoveTrailingComments(getline(prevline)) + + " check for comma first + if (line[col - 1] =~ ',') + " if the previous line ends in comma or semicolon don't indent + if (prevline_contents =~ '[;,]\s*$') + return indent(s:GetMSL(line('.'), 0)) + " get previous line indent, if it's comma first return prevline indent + elseif (prevline_contents =~ s:comma_first) + return indent(prevline) + " otherwise we indent 1 level + else + return indent(lvar) + shiftwidth() + endif + endif + endif + + + let bs = strpart('(){}[]', stridx(')}]', line[col - 1]) * 2, 2) + if searchpair(escape(bs[0], '\['), '', bs[1], 'bW', s:skip_expr) > 0 + if line[col-1]==')' && col('.') != col('$') - 1 + let ind = virtcol('.')-1 + else + let ind = indent(s:GetMSL(line('.'), 0)) + endif + endif + return ind + endif + + " If the line is comma first, dedent 1 level + if (getline(prevline) =~ s:comma_first) + return indent(prevline) - shiftwidth() + endif + + if (line =~ s:ternary) + if (getline(prevline) =~ s:ternary_q) + return indent(prevline) + else + return indent(prevline) + shiftwidth() + endif + endif + + " If we are in a multi-line comment, cindent does the right thing. + if s:IsInMultilineComment(v:lnum, 1) && !s:IsLineComment(v:lnum, 1) + return cindent(v:lnum) + endif + + " Check for multiple var assignments +" let var_indent = s:GetVarIndent(v:lnum) +" if var_indent >= 0 +" return var_indent +" endif + + " 3.3. Work on the previous line. {{{2 + " ------------------------------- + + " If the line is empty and the previous nonblank line was a multi-line + " comment, use that comment's indent. Deduct one char to account for the + " space in ' */'. + if line =~ '^\s*$' && s:IsInMultilineComment(prevline, 1) + return indent(prevline) - 1 + endif + + " Find a non-blank, non-multi-line string line above the current line. + let lnum = s:PrevNonBlankNonString(v:lnum - 1) + + " If the line is empty and inside a string, use the previous line. + if line =~ '^\s*$' && lnum != prevline + return indent(prevnonblank(v:lnum)) + endif + + " At the start of the file use zero indent. + if lnum == 0 + return 0 + endif + + " Set up variables for current line. + let line = getline(lnum) + let ind = indent(lnum) + + " If the previous line ended with a block opening, add a level of indent. + if s:Match(lnum, s:block_regex) + return indent(s:GetMSL(lnum, 0)) + shiftwidth() + endif + + " If the previous line contained an opening bracket, and we are still in it, + " add indent depending on the bracket type. + if line =~ '[[({]' + let counts = s:LineHasOpeningBrackets(lnum) + if counts[0] == '1' && searchpair('(', '', ')', 'bW', s:skip_expr) > 0 + if col('.') + 1 == col('$') + return ind + shiftwidth() + else + return virtcol('.') + endif + elseif counts[1] == '1' || counts[2] == '1' + return ind + shiftwidth() + else + call cursor(v:lnum, vcol) + end + endif + + " 3.4. Work on the MSL line. {{{2 + " -------------------------- + + let ind_con = ind + let ind = s:IndentWithContinuation(lnum, ind_con, shiftwidth()) + + " }}}2 + " + " + let ols = s:InOneLineScope(lnum) + if ols > 0 + let ind = ind + shiftwidth() + else + let ols = s:ExitingOneLineScope(lnum) + while ols > 0 && ind > 0 + let ind = ind - shiftwidth() + let ols = s:InOneLineScope(ols - 1) + endwhile + endif + + return ind +endfunction + +" }}}1 + +let &cpo = s:cpo_save +unlet s:cpo_save + +function! Fixedgq(lnum, count) + let l:tw = &tw ? &tw : 80 + + let l:count = a:count + let l:first_char = indent(a:lnum) + 1 + + if mode() == 'i' " gq was not pressed, but tw was set + return 1 + endif + + " This gq is only meant to do code with strings, not comments + if s:IsLineComment(a:lnum, l:first_char) || s:IsInMultilineComment(a:lnum, l:first_char) + return 1 + endif + + if len(getline(a:lnum)) < l:tw && l:count == 1 " No need for gq + return 1 + endif + + " Put all the lines on one line and do normal splitting after that + if l:count > 1 + while l:count > 1 + let l:count -= 1 + normal J + endwhile + endif + + let l:winview = winsaveview() + + call cursor(a:lnum, l:tw + 1) + let orig_breakpoint = searchpairpos(' ', '', '\.', 'bcW', '', a:lnum) + call cursor(a:lnum, l:tw + 1) + let breakpoint = searchpairpos(' ', '', '\.', 'bcW', s:skip_expr, a:lnum) + + " No need for special treatment, normal gq handles edgecases better + if breakpoint[1] == orig_breakpoint[1] + call winrestview(l:winview) + return 1 + endif + + " Try breaking after string + if breakpoint[1] <= indent(a:lnum) + call cursor(a:lnum, l:tw + 1) + let breakpoint = searchpairpos('\.', '', ' ', 'cW', s:skip_expr, a:lnum) + endif + + + if breakpoint[1] != 0 + call feedkeys("r\<CR>") + else + let l:count = l:count - 1 + endif + + " run gq on new lines + if l:count == 1 + call feedkeys("gqq") + endif + + return 0 +endfunction diff --git a/runtime/indent/typescriptreact.vim b/runtime/indent/typescriptreact.vim new file mode 100644 index 0000000..052bddd --- /dev/null +++ b/runtime/indent/typescriptreact.vim @@ -0,0 +1,2 @@ +" Placeholder for backwards compatilibity: .tsx used to stand for TypeScript. +runtime! indent/typescript.vim diff --git a/runtime/indent/vb.vim b/runtime/indent/vb.vim new file mode 100644 index 0000000..bc7142f --- /dev/null +++ b/runtime/indent/vb.vim @@ -0,0 +1,155 @@ +" Vim indent file +" Language: VisualBasic (ft=vb) / Basic (ft=basic) / SaxBasic (ft=vb) +" Author: Johannes Zellner <johannes@zellner.org> +" Maintainer: Michael Soyka (mssr953@gmail.com) +" Last Change: Fri, 18 Jun 2004 07:22:42 CEST +" Small update 2010 Jul 28 by Maxim Kim +" 2022/12/15: add support for multiline statements. +" 2022/12/21: move VbGetIndent from global to script-local scope +" 2022/12/26: recognize "Type" keyword + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal autoindent +setlocal indentexpr=s:VbGetIndent(v:lnum) +setlocal indentkeys& +setlocal indentkeys+==~else,=~elseif,=~end,=~wend,=~case,=~next,=~select,=~loop + +let b:undo_indent = "set ai< indentexpr< indentkeys<" + +" Only define the function once. +if exists("*s:VbGetIndent") + finish +endif + +function s:VbGetIndent(lnum) + let this_lnum = a:lnum + let this_line = getline(this_lnum) + + " labels and preprocessor get zero indent immediately + let LABELS_OR_PREPROC = '^\s*\(\<\k\+\>:\s*$\|#.*\)' + if this_line =~? LABELS_OR_PREPROC + return 0 + endif + + " Get the current value of "shiftwidth" + let bShiftwidth = shiftwidth() + + " Find a non-blank line above the current line. + " Skip over labels and preprocessor directives. + let lnum = this_lnum + while lnum > 0 + let lnum = prevnonblank(lnum - 1) + let previous_line = getline(lnum) + if previous_line !~? LABELS_OR_PREPROC + break + endif + endwhile + + " Hit the start of the file, use zero indent. + if lnum == 0 + return 0 + endif + + " Variable "previous_line" now contains the text in buffer line "lnum". + + " Multi-line statements have the underscore character at end-of-line: + " + " object.method(arguments, _ + " arguments, _ + " arguments) + " + " and require extra logic to determine the correct indentation. + " + " Case 1: Line "lnum" is the first line of a multiline statement. + " Line "lnum" will have a trailing underscore character + " but the preceding non-blank line does not. + " Line "this_lnum" will be indented relative to "lnum". + " + " Case 2: Line "lnum" is the last line of a multiline statement. + " Line "lnum" will not have a trailing underscore character + " but the preceding non-blank line will. + " Line "this_lnum" will have the same indentation as the starting + " line of the multiline statement. + " + " Case 3: Line "lnum" is neither the first nor last line. + " Lines "lnum" and "lnum-1" will have a trailing underscore + " character. + " Line "this_lnum" will have the same indentation as the preceding + " line. + " + " No matter which case it is, the starting line of the statement must be + " found. It will be assumed that multiline statements cannot have + " intermingled comments, statement labels, preprocessor directives or + " blank lines. + " + let lnum_is_continued = (previous_line =~ '_$') + if lnum > 1 + let before_lnum = prevnonblank(lnum-1) + let before_previous_line = getline(before_lnum) + else + let before_lnum = 0 + let before_previous_line = "" + endif + + if before_previous_line !~ '_$' + " Variable "previous_line" contains the start of a statement. + " + let ind = indent(lnum) + if lnum_is_continued + let ind += bShiftwidth + endif + elseif ! lnum_is_continued + " Line "lnum" contains the last line of a multiline statement. + " Need to find where this multiline statement begins + " + while before_lnum > 0 + let before_lnum -= 1 + if getline(before_lnum) !~ '_$' + let before_lnum += 1 + break + endif + endwhile + if before_lnum == 0 + let before_lnum = 1 + endif + let previous_line = getline(before_lnum) + let ind = indent(before_lnum) + else + " Line "lnum" is not the first or last line of a multiline statement. + " + let ind = indent(lnum) + endif + + " Add + if previous_line =~? '^\s*\<\(begin\|\%(\%(private\|public\|friend\)\s\+\)\=\%(function\|sub\|property\|enum\|type\)\|select\|case\|default\|if\|else\|elseif\|do\|for\|while\|with\)\>' + let ind = ind + bShiftwidth + endif + + " Subtract + if this_line =~? '^\s*\<end\>\s\+\<select\>' + if previous_line !~? '^\s*\<select\>' + let ind = ind - 2 * bShiftwidth + else + " this case is for an empty 'select' -- 'end select' + " (w/o any case statements) like: + " + " select case readwrite + " end select + let ind = ind - bShiftwidth + endif + elseif this_line =~? '^\s*\<\(end\|else\|elseif\|until\|loop\|next\|wend\)\>' + let ind = ind - bShiftwidth + elseif this_line =~? '^\s*\<\(case\|default\)\>' + if previous_line !~? '^\s*\<select\>' + let ind = ind - bShiftwidth + endif + endif + + return ind +endfunction + +" vim:sw=4 diff --git a/runtime/indent/verilog.vim b/runtime/indent/verilog.vim new file mode 100644 index 0000000..377615c --- /dev/null +++ b/runtime/indent/verilog.vim @@ -0,0 +1,232 @@ +" Language: Verilog HDL +" Maintainer: Chih-Tsun Huang <cthuang@cs.nthu.edu.tw> +" Last Change: 2017 Aug 25 by Chih-Tsun Huang +" 2023 Aug 28 by Vim Project (undo_indent) +" URL: http://www.cs.nthu.edu.tw/~cthuang/vim/indent/verilog.vim +" +" Credits: +" Suggestions for improvement, bug reports by +" Takuya Fujiwara <tyru.exe@gmail.com> +" Thilo Six <debian@Xk2c.de> +" Leo Butlero <lbutler@brocade.com> +" +" Buffer Variables: +" b:verilog_indent_modules : indenting after the declaration +" of module blocks +" b:verilog_indent_width : indenting width +" b:verilog_indent_verbose : verbose to each indenting +" + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetVerilogIndent() +setlocal indentkeys=!^F,o,O,0),=begin,=end,=join,=endcase +setlocal indentkeys+==endmodule,=endfunction,=endtask,=endspecify +setlocal indentkeys+==endconfig,=endgenerate,=endprimitive,=endtable +setlocal indentkeys+==`else,=`elsif,=`endif + +let b:undo_indent = "setlocal indentexpr< indentkeys<" + +" Only define the function once. +if exists("*GetVerilogIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +function GetVerilogIndent() + + if exists('b:verilog_indent_width') + let offset = b:verilog_indent_width + else + let offset = shiftwidth() + endif + if exists('b:verilog_indent_modules') + let indent_modules = offset + else + let indent_modules = 0 + endif + + " Find a non-blank line above the current line. + let lnum = prevnonblank(v:lnum - 1) + + " At the start of the file use zero indent. + if lnum == 0 + return 0 + endif + + let lnum2 = prevnonblank(lnum - 1) + let curr_line = getline(v:lnum) + let last_line = getline(lnum) + let last_line2 = getline(lnum2) + let ind = indent(lnum) + let ind2 = indent(lnum - 1) + let offset_comment1 = 1 + " Define the condition of an open statement + " Exclude the match of //, /* or */ + let vlog_openstat = '\(\<or\>\|\([*/]\)\@<![*(,{><+-/%^&|!=?:]\([*/]\)\@!\)' + " Define the condition when the statement ends with a one-line comment + let vlog_comment = '\(//.*\|/\*.*\*/\s*\)' + if exists('b:verilog_indent_verbose') + let vverb_str = 'INDENT VERBOSE:' + let vverb = 1 + else + let vverb = 0 + endif + + " Indent according to last line + " End of multiple-line comment + if last_line =~ '\*/\s*$' && last_line !~ '/\*.\{-}\*/' + let ind = ind - offset_comment1 + if vverb + echo vverb_str "De-indent after a multiple-line comment." + endif + + " Indent after if/else/for/case/always/initial/specify/fork blocks + " Note: We exclude '`if' or '`else' and consider 'end else' + " 'end if' is redundant here + elseif last_line =~ '^\s*\(end\)\=\s*`\@<!\<\(if\|else\)\>' || + \ last_line =~ '^\s*\<\(for\|case\%[[zx]]\)\>' || + \ last_line =~ '^\s*\<\(always\|initial\)\>' || + \ last_line =~ '^\s*\<\(specify\|fork\)\>' + if last_line !~ '\(;\|\<end\>\)\s*' . vlog_comment . '*$' || + \ last_line =~ '\(//\|/\*\).*\(;\|\<end\>\)\s*' . vlog_comment . '*$' + let ind = ind + offset + if vverb | echo vverb_str "Indent after a block statement." | endif + endif + " Indent after function/task/config/generate/primitive/table blocks + elseif last_line =~ '^\s*\<\(function\|task\|config\|generate\|primitive\|table\)\>' + if last_line !~ '\<end\>\s*' . vlog_comment . '*$' || + \ last_line =~ '\(//\|/\*\).*\(;\|\<end\>\)\s*' . vlog_comment . '*$' + let ind = ind + offset + if vverb + echo vverb_str "Indent after function/task block statement." + endif + endif + + " Indent after module/function/task/specify/fork blocks + elseif last_line =~ '^\s*\<module\>' + let ind = ind + indent_modules + if vverb && indent_modules + echo vverb_str "Indent after module statement." + endif + if last_line =~ '[(,]\s*' . vlog_comment . '*$' && + \ last_line !~ '\(//\|/\*\).*[(,]\s*' . vlog_comment . '*$' + let ind = ind + offset + if vverb + echo vverb_str "Indent after a multiple-line module statement." + endif + endif + + " Indent after a 'begin' statement + elseif last_line =~ '\(\<begin\>\)\(\s*:\s*\w\+\)*' . vlog_comment . '*$' && + \ last_line !~ '\(//\|/\*\).*\(\<begin\>\)' && + \ ( last_line2 !~ vlog_openstat . '\s*' . vlog_comment . '*$' || + \ last_line2 =~ '^\s*[^=!]\+\s*:\s*' . vlog_comment . '*$' ) + let ind = ind + offset + if vverb | echo vverb_str "Indent after begin statement." | endif + + " De-indent for the end of one-line block + elseif ( last_line !~ '\<begin\>' || + \ last_line =~ '\(//\|/\*\).*\<begin\>' ) && + \ last_line2 =~ '\<\(`\@<!if\|`\@<!else\|for\|always\|initial\)\>.*' . + \ vlog_comment . '*$' && + \ last_line2 !~ + \ '\(//\|/\*\).*\<\(`\@<!if\|`\@<!else\|for\|always\|initial\)\>' && + \ last_line2 !~ vlog_openstat . '\s*' . vlog_comment . '*$' && + \ ( last_line2 !~ '\<begin\>' || + \ last_line2 =~ '\(//\|/\*\).*\<begin\>' ) + let ind = ind - offset + if vverb + echo vverb_str "De-indent after the end of one-line statement." + endif + + " Multiple-line statement (including case statement) + " Open statement + " Ident the first open line + elseif last_line =~ vlog_openstat . '\s*' . vlog_comment . '*$' && + \ last_line !~ '\(//\|/\*\).*' . vlog_openstat . '\s*$' && + \ last_line2 !~ vlog_openstat . '\s*' . vlog_comment . '*$' + let ind = ind + offset + if vverb | echo vverb_str "Indent after an open statement." | endif + + " Close statement + " De-indent for an optional close parenthesis and a semicolon, and only + " if there exists precedent non-whitespace char + elseif last_line =~ ')*\s*;\s*' . vlog_comment . '*$' && + \ last_line !~ '^\s*)*\s*;\s*' . vlog_comment . '*$' && + \ last_line !~ '\(//\|/\*\).*\S)*\s*;\s*' . vlog_comment . '*$' && + \ ( last_line2 =~ vlog_openstat . '\s*' . vlog_comment . '*$' && + \ last_line2 !~ ';\s*//.*$') && + \ last_line2 !~ '^\s*' . vlog_comment . '$' + let ind = ind - offset + if vverb | echo vverb_str "De-indent after a close statement." | endif + + " `ifdef or `ifndef or `elsif or `else + elseif last_line =~ '^\s*`\<\(ifn\?def\|elsif\|else\)\>' + let ind = ind + offset + if vverb + echo vverb_str "Indent after a `ifdef or `ifndef or `elsif or `else statement." + endif + + endif + + " Re-indent current line + + " De-indent on the end of the block + " join/end/endcase/endfunction/endtask/endspecify + if curr_line =~ '^\s*\<\(join\|end\|endcase\)\>' || + \ curr_line =~ '^\s*\<\(endfunction\|endtask\|endspecify\)\>' || + \ curr_line =~ '^\s*\<\(endconfig\|endgenerate\|endprimitive\|endtable\)\>' + let ind = ind - offset + if vverb | echo vverb_str "De-indent the end of a block." | endif + elseif curr_line =~ '^\s*\<endmodule\>' + let ind = ind - indent_modules + if vverb && indent_modules + echo vverb_str "De-indent the end of a module." + endif + + " De-indent on a stand-alone 'begin' + elseif curr_line =~ '^\s*\<begin\>' + if last_line !~ '^\s*\<\(function\|task\|specify\|module\|config\|generate\|primitive\|table\)\>' && + \ last_line !~ '^\s*\()*\s*;\|)\+\)\s*' . vlog_comment . '*$' && + \ ( last_line =~ + \ '\<\(`\@<!if\|`\@<!else\|for\|case\%[[zx]]\|always\|initial\)\>' || + \ last_line =~ ')\s*' . vlog_comment . '*$' || + \ last_line =~ vlog_openstat . '\s*' . vlog_comment . '*$' ) + let ind = ind - offset + if vverb + echo vverb_str "De-indent a stand alone begin statement." + endif + endif + + " De-indent after the end of multiple-line statement + elseif curr_line =~ '^\s*)' && + \ ( last_line =~ vlog_openstat . '\s*' . vlog_comment . '*$' || + \ last_line !~ vlog_openstat . '\s*' . vlog_comment . '*$' && + \ last_line2 =~ vlog_openstat . '\s*' . vlog_comment . '*$' ) + let ind = ind - offset + if vverb + echo vverb_str "De-indent the end of a multiple statement." + endif + + " De-indent `elsif or `else or `endif + elseif curr_line =~ '^\s*`\<\(elsif\|else\|endif\)\>' + let ind = ind - offset + if vverb | echo vverb_str "De-indent `elsif or `else or `endif statement." | endif + + endif + + " Return the indentation + return ind +endfunction + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim:sw=2 diff --git a/runtime/indent/vhdl.vim b/runtime/indent/vhdl.vim new file mode 100644 index 0000000..b01d115 --- /dev/null +++ b/runtime/indent/vhdl.vim @@ -0,0 +1,438 @@ +" VHDL indent ('93 syntax) +" Language: VHDL +" Maintainer: Gerald Lai <laigera+vim?gmail.com> +" Version: 1.62 +" Last Change: 2017 Oct 17 +" 2023 Aug 28 by Vim Project (undo_indent) +" URL: http://www.vim.org/scripts/script.php?script_id=1450 + +" only load this indent file when no other was loaded +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" setup indent options for local VHDL buffer +setlocal indentexpr=GetVHDLindent() +setlocal indentkeys=!^F,o,O,0(,0) +setlocal indentkeys+==~begin,=~end\ ,=~end\ ,=~is,=~select,=~when +setlocal indentkeys+==~if,=~then,=~elsif,=~else +setlocal indentkeys+==~case,=~loop,=~for,=~generate,=~record,=~units,=~process,=~block,=~function,=~component,=~procedure +setlocal indentkeys+==~architecture,=~configuration,=~entity,=~package + +let b:undo_indent = "setlocal indentexpr< indentkeys<" + +" constants +" not a comment +let s:NC = '\%(--.*\)\@<!' +" end of string +let s:ES = '\s*\%(--.*\)\=$' +" no "end" keyword in front +let s:NE = '\%(\<end\s\+\)\@<!' + +" option to disable alignment of generic/port mappings +if !exists("g:vhdl_indent_genportmap") + let g:vhdl_indent_genportmap = 1 +endif + +" option to disable alignment of right-hand side assignment "<=" statements +if !exists("g:vhdl_indent_rhsassign") + let g:vhdl_indent_rhsassign = 1 +endif + +" only define indent function once +if exists("*GetVHDLindent") + finish +endif + +function GetVHDLindent() + " store current line & string + let curn = v:lnum + let curs = getline(curn) + + " find previous line that is not a comment + let prevn = prevnonblank(curn - 1) + let prevs = getline(prevn) + while prevn > 0 && prevs =~ '^\s*--' + let prevn = prevnonblank(prevn - 1) + let prevs = getline(prevn) + endwhile + let prevs_noi = substitute(prevs, '^\s*', '', '') + + " default indent starts as previous non-comment line's indent + let ind = prevn > 0 ? indent(prevn) : 0 + " backup default + let ind2 = ind + + " indent: special; kill string so it would not affect other filters + " keywords: "report" + string + " where: anywhere in current or previous line + let s0 = s:NC.'\<report\>\s*".*"' + if curs =~? s0 + let curs = "" + endif + if prevs =~? s0 + let prevs = "" + endif + + " indent: previous line's comment position, otherwise follow next non-comment line if possible + " keyword: "--" + " where: start of current line + if curs =~ '^\s*--' + let pn = curn - 1 + let ps = getline(pn) + if curs =~ '^\s*--\s' && ps =~ '--' + return indent(pn) + stridx(substitute(ps, '^\s*', '', ''), '--') + else + " find nextnonblank line that is not a comment + let nn = nextnonblank(curn + 1) + let ns = getline(nn) + while nn > 0 && ns =~ '^\s*--' + let nn = nextnonblank(nn + 1) + let ns = getline(nn) + endwhile + let n = indent(nn) + return n != -1 ? n : ind + endif + endif + + " **************************************************************************************** + " indent: align generic variables & port names + " keywords: "procedure" + name, "generic", "map", "port" + "(", provided current line is part of mapping + " where: anywhere in previous 2 lines + " find following previous non-comment line + let pn = prevnonblank(prevn - 1) + let ps = getline(pn) + while pn > 0 && ps =~ '^\s*--' + let pn = prevnonblank(pn - 1) + let ps = getline(pn) + endwhile + if (curs =~ '^\s*)' || curs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*\((.*)\)*\s*\%(=>\s*\S\+\|:[^=]\@=\s*\%(\%(in\|out\|inout\|buffer\|linkage\)\>\|\s\+\)\)') && (prevs =~? s:NC.'\<\%(procedure\s\+\S\+\|generic\|map\|port\)\s*(\%(\s*\w\)\=' || (ps =~? s:NC.'\<\%(procedure\|generic\|map\|port\)'.s:ES && prevs =~ '^\s*(')) + " align closing ")" with opening "(" + if curs =~ '^\s*)' + return ind2 + stridx(prevs_noi, '(') + endif + let m = matchend(prevs_noi, '(\s*\ze\w') + if m != -1 + return ind2 + m + else + if g:vhdl_indent_genportmap + return ind2 + stridx(prevs_noi, '(') + shiftwidth() + else + return ind2 + shiftwidth() + endif + endif + endif + + " indent: align conditional/select statement + " keywords: variable + "<=" without ";" ending + " where: start of previous line + if prevs =~? '^\s*\S\+\s*<=[^;]*'.s:ES + if g:vhdl_indent_rhsassign + return ind2 + matchend(prevs_noi, '<=\s*\ze.') + else + return ind2 + shiftwidth() + endif + endif + + " indent: backtrace previous non-comment lines for next smaller or equal size indent + " keywords: "end" + "record", "units" + " where: start of previous line + " keyword: ")" + " where: start of previous line + " keyword: without "<=" + ";" ending + " where: anywhere in previous line + " keyword: "=>" + ")" ending, provided current line does not begin with ")" + " where: anywhere in previous line + " _note_: indent allowed to leave this filter + let m = 0 + if prevs =~? '^\s*end\s\+\%(record\|units\)\>' + let m = 3 + elseif prevs =~ '^\s*)' + let m = 1 + elseif prevs =~ s:NC.'\%(<=.*\)\@<!;'.s:ES || (curs !~ '^\s*)' && prevs =~ s:NC.'=>.*'.s:NC.')'.s:ES) + let m = 2 + endif + + if m > 0 + let pn = prevnonblank(prevn - 1) + let ps = getline(pn) + while pn > 0 + let t = indent(pn) + if ps !~ '^\s*--' && (t < ind || (t == ind && m == 3)) + " make sure one of these is true + " keywords: variable + "<=" without ";" ending + " where: start of previous non-comment line + " keywords: "procedure", "generic", "map", "port" + " where: anywhere in previous non-comment line + " keyword: "(" + " where: start of previous non-comment line + if m < 3 && ps !~? '^\s*\S\+\s*<=[^;]*'.s:ES + if ps =~? s:NC.'\<\%(procedure\|generic\|map\|port\)\>' || ps =~ '^\s*(' + let ind = t + endif + break + endif + let ind = t + if m > 1 + " find following previous non-comment line + let ppn = prevnonblank(pn - 1) + let pps = getline(ppn) + while ppn > 0 && pps =~ '^\s*--' + let ppn = prevnonblank(ppn - 1) + let pps = getline(ppn) + endwhile + " indent: follow + " keyword: "select" + " where: end of following previous non-comment line + " keyword: "type" + " where: start of following previous non-comment line + if m == 2 + let s1 = s:NC.'\<select'.s:ES + if ps !~? s1 && pps =~? s1 + let ind = indent(ppn) + endif + elseif m == 3 + let s1 = '^\s*type\>' + if ps !~? s1 && pps =~? s1 + let ind = indent(ppn) + endif + endif + endif + break + endif + let pn = prevnonblank(pn - 1) + let ps = getline(pn) + endwhile + endif + + " indent: follow indent of previous opening statement, otherwise -sw + " keyword: "begin" + " where: anywhere in current line + if curs =~? s:NC.'\<begin\>' + " find previous opening statement of + " keywords: "architecture", "block", "entity", "function", "generate", "procedure", "process" + let s2 = s:NC.s:NE.'\<\%(architecture\|block\|entity\|function\|generate\|procedure\|process\)\>' + + let pn = prevnonblank(curn - 1) + let ps = getline(pn) + while pn > 0 && (ps =~ '^\s*--' || ps !~? s2) + let pn = prevnonblank(pn - 1) + let ps = getline(pn) + + if (ps =~? s:NC.'\<begin\>') + return indent(pn) - shiftwidth() + endif + endwhile + + if (pn == 0) + return ind - shiftwidth() + else + return indent(pn) + endif + endif + + " indent: +sw if previous line is previous opening statement + " keywords: "record", "units" + " where: anywhere in current line + if curs =~? s:NC.s:NE.'\<\%(record\|units\)\>' + " find previous opening statement of + " keyword: "type" + let s3 = s:NC.s:NE.'\<type\>' + if curs !~? s3.'.*'.s:NC.'\<\%(record\|units\)\>.*'.s:ES && prevs =~? s3 + let ind = ind + shiftwidth() + endif + return ind + endif + + " **************************************************************************************** + " indent: 0 + " keywords: "architecture", "configuration", "entity", "library", "package" + " where: start of current line + if curs =~? '^\s*\%(architecture\|configuration\|entity\|library\|package\)\>' + return 0 + endif + + " indent: maintain indent of previous opening statement + " keyword: "is" + " where: start of current line + " find previous opening statement of + " keywords: "architecture", "block", "configuration", "entity", "function", "package", "procedure", "process", "type" + if curs =~? '^\s*\<is\>' && prevs =~? s:NC.s:NE.'\<\%(architecture\|block\|configuration\|entity\|function\|package\|procedure\|process\|type\)\>' + return ind2 + endif + + " indent: maintain indent of previous opening statement + " keyword: "then" + " where: start of current line + " find previous opening statement of + " keywords: "elsif", "if" + if curs =~? '^\s*\<then\>' && prevs =~? s:NC.'\%(\<elsif\>\|'.s:NE.'\<if\>\)' + return ind2 + endif + + " indent: maintain indent of previous opening statement + " keyword: "generate" + " where: start of current line + " find previous opening statement of + " keywords: "for", "if" + if curs =~? '^\s*\<generate\>' && prevs =~? s:NC.s:NE.'\%(\%(\<wait\s\+\)\@<!\<for\|\<if\)\>' + return ind2 + endif + + " indent: +sw + " keywords: "block", "process" + " removed: "begin", "case", "elsif", "if", "loop", "record", "units", "while" + " where: anywhere in previous line + if prevs =~? s:NC.s:NE.'\<\%(block\|process\)\>' + return ind + shiftwidth() + endif + + " indent: +sw + " keywords: "architecture", "configuration", "entity", "package" + " removed: "component", "for", "when", "with" + " where: start of previous line + if prevs =~? '^\s*\%(architecture\|configuration\|entity\|package\)\>' + return ind + shiftwidth() + endif + + " indent: +sw + " keyword: "select" + " removed: "generate", "is", "=>" + " where: end of previous line + if prevs =~? s:NC.'\<select'.s:ES + return ind + shiftwidth() + endif + + " indent: +sw + " keyword: "begin", "loop", "record", "units" + " where: anywhere in previous line + " keyword: "component", "else", "for" + " where: start of previous line + " keyword: "generate", "is", "then", "=>" + " where: end of previous line + " _note_: indent allowed to leave this filter + if prevs =~? s:NC.'\%(\<begin\>\|'.s:NE.'\<\%(loop\|record\|units\)\>\)' || prevs =~? '^\s*\%(component\|else\|for\)\>' || prevs =~? s:NC.'\%('.s:NE.'\<generate\|\<\%(is\|then\)\|=>\)'.s:ES + let ind = ind + shiftwidth() + endif + + " **************************************************************************************** + " indent: -sw + " keywords: "when", provided previous line does not begin with "when", does not end with "is" + " where: start of current line + let s4 = '^\s*when\>' + if curs =~? s4 + if prevs =~? s:NC.'\<is'.s:ES + return ind + elseif prevs !~? s4 + return ind - shiftwidth() + else + return ind2 + endif + endif + + " indent: -sw + " keywords: "else", "elsif", "end" + "block", "for", "function", "generate", "if", "loop", "procedure", "process", "record", "units" + " where: start of current line + let s5 = 'block\|for\|function\|generate\|if\|loop\|procedure\|process\|record\|units' + if curs =~? '^\s*\%(else\|elsif\|end\s\+\%('.s5.'\)\)\>' + if prevs =~? '^\s*\%(elsif\|'.s5.'\)' + return ind + else + return ind - shiftwidth() + endif + endif + + " indent: backtrace previous non-comment lines + " keyword: "end" + "case", "component" + " where: start of current line + let m = 0 + if curs =~? '^\s*end\s\+case\>' + let m = 1 + elseif curs =~? '^\s*end\s\+component\>' + let m = 2 + endif + + if m > 0 + " find following previous non-comment line + let pn = prevn + let ps = getline(pn) + while pn > 0 + if ps !~ '^\s*--' + "indent: -2sw + "keywords: "end" + "case" + "where: start of previous non-comment line + "indent: -sw + "keywords: "when" + "where: start of previous non-comment line + "indent: follow + "keywords: "case" + "where: start of previous non-comment line + if m == 1 + if ps =~? '^\s*end\s\+case\>' + return indent(pn) - 2 * shiftwidth() + elseif ps =~? '^\s*when\>' + return indent(pn) - shiftwidth() + elseif ps =~? '^\s*case\>' + return indent(pn) + endif + "indent: follow + "keyword: "component" + "where: start of previous non-comment line + elseif m == 2 + if ps =~? '^\s*component\>' + return indent(pn) + endif + endif + endif + let pn = prevnonblank(pn - 1) + let ps = getline(pn) + endwhile + return ind - shiftwidth() + endif + + " indent: -sw + " keyword: ")" + " where: start of current line + if curs =~ '^\s*)' + return ind - shiftwidth() + endif + + " indent: 0 + " keywords: "end" + "architecture", "configuration", "entity", "package" + " where: start of current line + if curs =~? '^\s*end\s\+\%(architecture\|configuration\|entity\|package\)\>' + return 0 + endif + + " indent: -sw + " keywords: "end" + identifier, ";" + " where: start of current line + "if curs =~? '^\s*end\s\+\w\+\>' + if curs =~? '^\s*end\%(\s\|;'.s:ES.'\)' + return ind - shiftwidth() + endif + + " **************************************************************************************** + " indent: maintain indent of previous opening statement + " keywords: without "procedure", "generic", "map", "port" + ":" but not ":=" + "in", "out", "inout", "buffer", "linkage", variable & ":=" + " where: start of current line + if curs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*:[^=]\@=\s*\%(\%(in\|out\|inout\|buffer\|linkage\)\>\|\w\+\s\+:=\)' + return ind2 + endif + + " **************************************************************************************** + " indent: maintain indent of previous opening statement, corner case which + " does not end in ;, but is part of a mapping + " keywords: without "procedure", "generic", "map", "port" + ":" but not ":=", never + ;$ and + " prevline without "procedure", "generic", "map", "port" + ":" but not ":=" + eventually ;$ + " where: start of current line + if curs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*:[^=].*[^;].*$' + if prevs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*:[^=].*;.*$' + return ind2 + endif + endif + + " return leftover filtered indent + return ind +endfunction diff --git a/runtime/indent/vim.vim b/runtime/indent/vim.vim new file mode 100644 index 0000000..97a4a36 --- /dev/null +++ b/runtime/indent/vim.vim @@ -0,0 +1,23 @@ +vim9script + +# Vim indent file +# Language: Vim script +# Maintainer: The Vim Project <https://github.com/vim/vim> +# Last Change: 2023 Aug 10 +# Former Maintainer: Bram Moolenaar <Bram@vim.org> + +# Only load this indent file when no other was loaded. +if exists('b:did_indent') + finish +endif + +b:did_indent = true +b:undo_indent = 'setlocal indentkeys< indentexpr<' + +import autoload '../autoload/dist/vimindent.vim' + +setlocal indentexpr=vimindent.Expr() +setlocal indentkeys+==endif,=enddef,=endfu,=endfor,=endwh,=endtry,=endclass,=endinterface,=endenum,=},=else,=cat,=finall,=END,0\\ +execute('setlocal indentkeys+=0=\"\\\ ,0=#\\\ ') +setlocal indentkeys-=0# +setlocal indentkeys-=: diff --git a/runtime/indent/vroom.vim b/runtime/indent/vroom.vim new file mode 100644 index 0000000..d333e2c --- /dev/null +++ b/runtime/indent/vroom.vim @@ -0,0 +1,21 @@ +" Vim indent file +" Language: Vroom (vim testing and executable documentation) +" Maintainer: David Barnett (https://github.com/google/vim-ft-vroom) +" Last Change: 2014 Jul 23 + +if exists('b:did_indent') + finish +endif +let b:did_indent = 1 + +let s:cpo_save = &cpo +set cpo-=C + + +let b:undo_indent = 'setlocal autoindent<' + +setlocal autoindent + + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/indent/vue.vim b/runtime/indent/vue.vim new file mode 100644 index 0000000..f6fe350 --- /dev/null +++ b/runtime/indent/vue.vim @@ -0,0 +1,14 @@ +" Vim indent file placeholder +" Language: Vue +" Maintainer: None, please volunteer if you have a real Vue indent script +" Last Change: 2022 Dec 24 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +" don't set b:did_indent, otherwise html indenting won't be activated +" let b:did_indent = 1 + +" Html comes closest +runtime! indent/html.vim diff --git a/runtime/indent/wat.vim b/runtime/indent/wat.vim new file mode 100644 index 0000000..08997f1 --- /dev/null +++ b/runtime/indent/wat.vim @@ -0,0 +1,17 @@ +" Vim indent file +" Language: WebAssembly +" Maintainer: rhysd <lin90162@yahoo.co.jp> +" Last Change: Nov 14, 2023 +" For bugs, patches and license go to https://github.com/rhysd/vim-wasm + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" WebAssembly text format is S-expression. We can reuse LISP indentation +" logic. +setlocal indentexpr=lispindent('.') +setlocal noautoindent nosmartindent + +let b:undo_indent = "setl lisp< indentexpr<" diff --git a/runtime/indent/xf86conf.vim b/runtime/indent/xf86conf.vim new file mode 100644 index 0000000..834eb00 --- /dev/null +++ b/runtime/indent/xf86conf.vim @@ -0,0 +1,40 @@ +" Vim indent file +" Language: XFree86 Configuration File +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Last Change: 2022 April 25 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetXF86ConfIndent() +setlocal indentkeys=!^F,o,O,=End +setlocal nosmartindent + +let b:undo_indent = "setl inde< indk< si<" + +if exists("*GetXF86ConfIndent") + finish +endif + +function GetXF86ConfIndent() + let lnum = prevnonblank(v:lnum - 1) + + if lnum == 0 + return 0 + endif + + let ind = indent(lnum) + + if getline(lnum) =~? '^\s*\(Sub\)\=Section\>' + let ind = ind + shiftwidth() + endif + + if getline(v:lnum) =~? '^\s*End\(Sub\)\=Section\>' + let ind = ind - shiftwidth() + endif + + return ind +endfunction diff --git a/runtime/indent/xhtml.vim b/runtime/indent/xhtml.vim new file mode 100644 index 0000000..e5c9cc3 --- /dev/null +++ b/runtime/indent/xhtml.vim @@ -0,0 +1,13 @@ +" Vim indent file +" Language: XHTML +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Last Change: 2023 Aug 10 +" Former Maintainer: Bram Moolenaar <Bram@vim.org> + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +" Handled like HTML for now. +runtime! indent/html.vim diff --git a/runtime/indent/xinetd.vim b/runtime/indent/xinetd.vim new file mode 100644 index 0000000..21000b7 --- /dev/null +++ b/runtime/indent/xinetd.vim @@ -0,0 +1,58 @@ +" Vim indent file +" Language: xinetd.conf(5) configuration file +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Last Change: 2022 April 25 + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetXinetdIndent() +setlocal indentkeys=0{,0},!^F,o,O +setlocal nosmartindent + +let b:undo_indent = "setl inde< indk< si<" + +if exists("*GetXinetdIndent") + finish +endif +let s:keepcpo= &cpo +set cpo&vim + +function s:count_braces(lnum, count_open) + let n_open = 0 + let n_close = 0 + let line = getline(a:lnum) + let pattern = '[{}]' + let i = match(line, pattern) + while i != -1 + if synIDattr(synID(a:lnum, i + 1, 0), 'name') !~ 'ld\%(Comment\|String\)' + if line[i] == '{' + let n_open += 1 + elseif line[i] == '}' + if n_open > 0 + let n_open -= 1 + else + let n_close += 1 + endif + endif + endif + let i = match(line, pattern, i + 1) + endwhile + return a:count_open ? n_open : n_close +endfunction + +function GetXinetdIndent() + let pnum = prevnonblank(v:lnum - 1) + if pnum == 0 + return 0 + endif + + return indent(pnum) + s:count_braces(pnum, 1) * shiftwidth() + \ - s:count_braces(v:lnum, 0) * shiftwidth() +endfunction + +let &cpo = s:keepcpo +unlet s:keepcpo diff --git a/runtime/indent/xml.vim b/runtime/indent/xml.vim new file mode 100644 index 0000000..5bf53ad --- /dev/null +++ b/runtime/indent/xml.vim @@ -0,0 +1,218 @@ +" Language: XML +" Maintainer: Christian Brabandt <cb@256bit.org> +" Repository: https://github.com/chrisbra/vim-xml-ftplugin +" Previous Maintainer: Johannes Zellner <johannes@zellner.org> +" Last Changed: 2020 Nov 4th +" Last Change: +" 20200529 - Handle empty closing tags correctly +" 20191202 - Handle docbk filetype +" 20190726 - Correctly handle non-tagged data +" 20190204 - correctly handle wrap tags +" https://github.com/chrisbra/vim-xml-ftplugin/issues/5 +" 20190128 - Make sure to find previous tag +" https://github.com/chrisbra/vim-xml-ftplugin/issues/4 +" 20181116 - Fix indentation when tags start with a colon or an underscore +" https://github.com/vim/vim/pull/926 +" 20181022 - Do not overwrite indentkeys setting +" https://github.com/chrisbra/vim-xml-ftplugin/issues/1 +" 20180724 - Correctly indent xml comments https://github.com/vim/vim/issues/3200 +" +" Notes: +" 1) does not indent pure non-xml code (e.g. embedded scripts) +" 2) will be confused by unbalanced tags in comments +" or CDATA sections. +" 2009-05-26 patch by Nikolai Weibull +" TODO: implement pre-like tags, see xml_indent_open / xml_indent_close + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 +let s:keepcpo= &cpo +set cpo&vim + +" [-- local settings (must come before aborting the script) --] +" Attention: Parameter use_syntax_check is used by the docbk.vim indent script +setlocal indentexpr=XmlIndentGet(v:lnum,1) +setlocal indentkeys=o,O,*<Return>,<>>,<<>,/,{,},!^F +" autoindent: used when the indentexpr returns -1 +setlocal autoindent + +let b:undo_indent = "setl ai< inde< indk<" + +if !exists('b:xml_indent_open') + let b:xml_indent_open = '.\{-}<[:A-Z_a-z]' + " pre tag, e.g. <address> + " let b:xml_indent_open = '.\{-}<[/]\@!\(address\)\@!' +endif + +if !exists('b:xml_indent_close') + let b:xml_indent_close = '.\{-}</\|/>.\{-}' + " end pre tag, e.g. </address> + " let b:xml_indent_close = '.\{-}</\(address\)\@!' +endif + +if !exists('b:xml_indent_continuation_filetype') + let b:xml_indent_continuation_filetype = 'xml' +endif + +let &cpo = s:keepcpo +unlet s:keepcpo + +" [-- finish, if the function already exists --] +if exists('*XmlIndentGet') + finish +endif + +let s:keepcpo= &cpo +set cpo&vim + +fun! <SID>XmlIndentWithPattern(line, pat) + let s = substitute('x'.a:line, a:pat, "\1", 'g') + return strlen(substitute(s, "[^\1].*$", '', '')) +endfun + +" [-- check if it's xml --] +fun! <SID>XmlIndentSynCheck(lnum) + if &syntax != '' + let syn1 = synIDattr(synID(a:lnum, 1, 1), 'name') + let syn2 = synIDattr(synID(a:lnum, strlen(getline(a:lnum)) - 1, 1), 'name') + if syn1 != '' && syn1 !~ 'xml' && syn2 != '' && syn2 !~ 'xml' + " don't indent pure non-xml code + return 0 + endif + endif + return 1 +endfun + +" [-- return the sum of indents of a:lnum --] +fun! <SID>XmlIndentSum(line, style, add) + if <SID>IsXMLContinuation(a:line) && a:style == 0 && !<SID>IsXMLEmptyClosingTag(a:line) + " no complete tag, add one additional indent level + " but only for the current line + return a:add + shiftwidth() + elseif <SID>HasNoTagEnd(a:line) + " no complete tag, return initial indent + return a:add + endif + if a:style == match(a:line, '^\s*</') + return (shiftwidth() * + \ (<SID>XmlIndentWithPattern(a:line, b:xml_indent_open) + \ - <SID>XmlIndentWithPattern(a:line, b:xml_indent_close) + \ - <SID>XmlIndentWithPattern(a:line, '.\{-}/>'))) + a:add + else + return a:add + endif +endfun + +" Main indent function +fun! XmlIndentGet(lnum, use_syntax_check) + " Find a non-empty line above the current line. + if prevnonblank(a:lnum - 1) == 0 + " Hit the start of the file, use zero indent. + return 0 + endif + " Find previous line with a tag (regardless whether open or closed, + " but always restrict the match to a line before the current one + " Note: xml declaration: <?xml version="1.0"?> + " won't be found, as it is not a legal tag name + let ptag_pattern = '\%(.\{-}<[/:A-Z_a-z]\)'. '\%(\&\%<'. a:lnum .'l\)' + let ptag = search(ptag_pattern, 'bnW') + " no previous tag + if ptag == 0 + return 0 + endif + + let pline = getline(ptag) + let pind = indent(ptag) + + let syn_name_start = '' " Syntax element at start of line (excluding whitespace) + let syn_name_end = '' " Syntax element at end of line + let curline = getline(a:lnum) + if a:use_syntax_check + let check_lnum = <SID>XmlIndentSynCheck(ptag) + let check_alnum = <SID>XmlIndentSynCheck(a:lnum) + if check_lnum == 0 || check_alnum == 0 + return indent(a:lnum) + endif + let syn_name_end = synIDattr(synID(a:lnum, strlen(curline) - 1, 1), 'name') + let syn_name_start = synIDattr(synID(a:lnum, match(curline, '\S') + 1, 1), 'name') + let prev_syn_name_end = synIDattr(synID(ptag, strlen(pline) - 1, 1), 'name') + " not needed (yet?) + " let prev_syn_name_start = synIDattr(synID(ptag, match(pline, '\S') + 1, 1), 'name') + endif + + if syn_name_end =~ 'Comment' && syn_name_start =~ 'Comment' + return <SID>XmlIndentComment(a:lnum) + elseif empty(syn_name_start) && empty(syn_name_end) && a:use_syntax_check + " non-xml tag content: use indent from 'autoindent' + if pline =~ b:xml_indent_close + return pind + elseif !empty(prev_syn_name_end) + " only indent by an extra shiftwidth, if the previous line ends + " with an XML like tag + return pind + shiftwidth() + else + " no extra indent, looks like a text continuation line + return pind + endif + endif + + " Get indent from previous tag line + let ind = <SID>XmlIndentSum(pline, -1, pind) + " Determine indent from current line + let ind = <SID>XmlIndentSum(curline, 0, ind) + return ind +endfun + +func! <SID>IsXMLContinuation(line) + " Checks, whether or not the line matches a start-of-tag + return a:line !~ '^\s*<' && &ft =~# b:xml_indent_continuation_filetype +endfunc + +func! <SID>HasNoTagEnd(line) + " Checks whether or not the line matches '>' (so finishes a tag) + return a:line !~ '>\s*$' +endfunc + +func! <SID>IsXMLEmptyClosingTag(line) + " Checks whether the line ends with an empty closing tag such as <lb/> + return a:line =~? '<[^>]*/>\s*$' +endfunc + +" return indent for a commented line, +" the middle part might be indented one additional level +func! <SID>XmlIndentComment(lnum) + let ptagopen = search('.\{-}<[:A-Z_a-z]\_[^/]\{-}>.\{-}', 'bnW') + let ptagclose = search(b:xml_indent_close, 'bnW') + if getline(a:lnum) =~ '<!--' + " if previous tag was a closing tag, do not add + " one additional level of indent + if ptagclose > ptagopen && a:lnum > ptagclose + " If the previous tag was closed on the same line as it was + " declared, we should indent with its indent level. + if !<SID>IsXMLContinuation(getline(ptagclose)) + return indent(ptagclose) + else + return indent(ptagclose) - shiftwidth() + endif + elseif ptagclose == ptagopen + return indent(ptagclose) + else + " start of comment, add one indentation level + return indent(ptagopen) + shiftwidth() + endif + elseif getline(a:lnum) =~ '-->' + " end of comment, same as start of comment + return indent(search('<!--', 'bnW')) + else + " middle part of comment, add one additional level + return indent(search('<!--', 'bnW')) + shiftwidth() + endif +endfunc + +let &cpo = s:keepcpo +unlet s:keepcpo + +" vim:ts=4 et sts=-1 sw=0 diff --git a/runtime/indent/xsd.vim b/runtime/indent/xsd.vim new file mode 100644 index 0000000..59e0b60 --- /dev/null +++ b/runtime/indent/xsd.vim @@ -0,0 +1,13 @@ +" Vim indent file +" Language: .xsd files (XML Schema) +" Maintainer: Nobody +" Last Change: 2005 Jun 09 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +" Use XML formatting rules +runtime! indent/xml.vim + diff --git a/runtime/indent/xslt.vim b/runtime/indent/xslt.vim new file mode 100644 index 0000000..ff93d69 --- /dev/null +++ b/runtime/indent/xslt.vim @@ -0,0 +1,13 @@ +" Vim indent file +" Language: XSLT .xslt files +" Maintainer: David Fishburn <fishburn@ianywhere.com> +" Last Change: Wed May 14 2003 8:48:41 PM + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +" Use XML formatting rules +runtime! indent/xml.vim + diff --git a/runtime/indent/yacc.vim b/runtime/indent/yacc.vim new file mode 100644 index 0000000..253ccc5 --- /dev/null +++ b/runtime/indent/yacc.vim @@ -0,0 +1,44 @@ +" Vim indent file +" Language: YACC input file +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Last Change: 2022 April 25 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif + +let b:did_indent = 1 + +setlocal indentexpr=GetYaccIndent() +setlocal indentkeys=!^F,o,O +setlocal nosmartindent + +let b:undo_indent = "setl inde< indk< si<" + +" Only define the function once. +if exists("*GetYaccIndent") + finish +endif + +function GetYaccIndent() + if v:lnum == 1 + return 0 + endif + + let ind = indent(v:lnum - 1) + let line = getline(v:lnum - 1) + + if line == '' + let ind = 0 + elseif line =~ '^\w\+\s*:' + let ind = ind + matchend(line, '^\w\+\s*') + elseif line =~ '^\s*;' + let ind = 0 + else + let ind = indent(v:lnum) + endif + + return ind +endfunction diff --git a/runtime/indent/yaml.vim b/runtime/indent/yaml.vim new file mode 100644 index 0000000..93fd8ea --- /dev/null +++ b/runtime/indent/yaml.vim @@ -0,0 +1,156 @@ +" Vim indent file +" Language: YAML +" Maintainer: Nikolai Pavlov <zyx.vim@gmail.com> +" Last Updates: Lukas Reineke, "lacygoill" +" Last Change: 2022 Jun 17 + +" Only load this indent file when no other was loaded. +if exists('b:did_indent') + finish +endif + +let b:did_indent = 1 + +setlocal indentexpr=GetYAMLIndent(v:lnum) +setlocal indentkeys=!^F,o,O,0#,0},0],<:>,0- +setlocal nosmartindent + +let b:undo_indent = 'setlocal indentexpr< indentkeys< smartindent<' + +" Only define the function once. +if exists('*GetYAMLIndent') + finish +endif + +let s:save_cpo = &cpo +set cpo&vim + +function s:FindPrevLessIndentedLine(lnum, ...) + let prevlnum = prevnonblank(a:lnum-1) + let curindent = a:0 ? a:1 : indent(a:lnum) + while prevlnum + \ && indent(prevlnum) >= curindent + \ && getline(prevlnum) !~# '^\s*#' + let prevlnum = prevnonblank(prevlnum-1) + endwhile + return prevlnum +endfunction + +function s:FindPrevLEIndentedLineMatchingRegex(lnum, regex) + let plilnum = s:FindPrevLessIndentedLine(a:lnum, indent(a:lnum)+1) + while plilnum && getline(plilnum) !~# a:regex + let plilnum = s:FindPrevLessIndentedLine(plilnum) + endwhile + return plilnum +endfunction + +let s:mapkeyregex = '\v^\s*\#@!\S@=%(\''%([^'']|\''\'')*\''' .. + \ '|\"%([^"\\]|\\.)*\"' .. + \ '|%(%(\:\ )@!.)*)\:%(\ |$)' +let s:liststartregex = '\v^\s*%(\-%(\ |$))' + +let s:c_ns_anchor_char = '\v%([\n\r\uFEFF \t,[\]{}]@!\p)' +let s:c_ns_anchor_name = s:c_ns_anchor_char .. '+' +let s:c_ns_anchor_property = '\v\&' .. s:c_ns_anchor_name + +let s:ns_word_char = '\v[[:alnum:]_\-]' +let s:ns_tag_char = '\v%(\x\x|' .. s:ns_word_char .. '|[#/;?:@&=+$.~*''()])' +let s:c_named_tag_handle = '\v\!' .. s:ns_word_char .. '+\!' +let s:c_secondary_tag_handle = '\v\!\!' +let s:c_primary_tag_handle = '\v\!' +let s:c_tag_handle = '\v%(' .. s:c_named_tag_handle. + \ '|' .. s:c_secondary_tag_handle. + \ '|' .. s:c_primary_tag_handle .. ')' +let s:c_ns_shorthand_tag = '\v' .. s:c_tag_handle .. s:ns_tag_char .. '+' +let s:c_non_specific_tag = '\v\!' +let s:ns_uri_char = '\v%(\x\x|' .. s:ns_word_char .. '\v|[#/;?:@&=+$,.!~*''()[\]])' +let s:c_verbatim_tag = '\v\!\<' .. s:ns_uri_char.. '+\>' +let s:c_ns_tag_property = '\v' .. s:c_verbatim_tag. + \ '\v|' .. s:c_ns_shorthand_tag. + \ '\v|' .. s:c_non_specific_tag + +let s:block_scalar_header = '\v[|>]%([+-]?[1-9]|[1-9]?[+-])?' + +function GetYAMLIndent(lnum) + if a:lnum == 1 || !prevnonblank(a:lnum-1) + return 0 + endif + + let prevlnum = prevnonblank(a:lnum-1) + let previndent = indent(prevlnum) + + let line = getline(a:lnum) + if line =~# '^\s*#' && getline(a:lnum-1) =~# '^\s*#' + " Comment blocks should have identical indent + return previndent + elseif line =~# '^\s*[\]}]' + " Lines containing only closing braces should have previous indent + return indent(s:FindPrevLessIndentedLine(a:lnum)) + endif + + " Ignore comment lines when calculating indent + while getline(prevlnum) =~# '^\s*#' + let prevlnum = prevnonblank(prevlnum-1) + if !prevlnum + return previndent + endif + endwhile + + let prevline = getline(prevlnum) + let previndent = indent(prevlnum) + + " Any examples below assume that shiftwidth=2 + if prevline =~# '\v[{[:]$|[:-]\ [|>][+\-]?%(\s+\#.*|\s*)$' + " Mapping key: + " nested mapping: ... + " + " - { + " key: [ + " list value + " ] + " } + " + " - |- + " Block scalar without indentation indicator + return previndent+shiftwidth() + elseif prevline =~# '\v[:-]\ [|>]%(\d+[+\-]?|[+\-]?\d+)%(\#.*|\s*)$' + " - |+2 + " block scalar with indentation indicator + "#^^ indent+2, not indent+shiftwidth + return previndent + str2nr(matchstr(prevline, + \'\v([:-]\ [|>])@<=[+\-]?\d+%([+\-]?%(\s+\#.*|\s*)$)@=')) + elseif prevline =~# '\v\"%([^"\\]|\\.)*\\$' + " "Multiline string \ + " with escaped end" + let qidx = match(prevline, '\v\"%([^"\\]|\\.)*\\') + return virtcol([prevlnum, qidx+1]) + elseif line =~# s:liststartregex + " List line should have indent equal to previous list line unless it was + " caught by one of the previous rules + return indent(s:FindPrevLEIndentedLineMatchingRegex(a:lnum, + \ s:liststartregex)) + elseif line =~# s:mapkeyregex + " Same for line containing mapping key + let prevmapline = s:FindPrevLEIndentedLineMatchingRegex(a:lnum, + \ s:mapkeyregex) + if getline(prevmapline) =~# '^\s*- ' + return indent(prevmapline) + 2 + else + return indent(prevmapline) + endif + elseif prevline =~# '^\s*- ' + " - List with + " multiline scalar + return previndent+2 + elseif prevline =~# s:mapkeyregex .. '\v\s*%(%(' .. s:c_ns_tag_property .. + \ '\v|' .. s:c_ns_anchor_property .. + \ '\v|' .. s:block_scalar_header .. + \ '\v)%(\s+|\s*%(\#.*)?$))*' + " Mapping with: value + " that is multiline scalar + return previndent+shiftwidth() + endif + return previndent +endfunction + +let &cpo = s:save_cpo diff --git a/runtime/indent/zig.vim b/runtime/indent/zig.vim new file mode 100644 index 0000000..e3ce8aa --- /dev/null +++ b/runtime/indent/zig.vim @@ -0,0 +1,80 @@ +" Vim filetype indent file +" Language: Zig +" Upstream: https://github.com/ziglang/zig.vim + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +if (!has("cindent") || !has("eval")) + finish +endif + +setlocal cindent + +" L0 -> 0 indent for jump labels (i.e. case statement in c). +" j1 -> indenting for "javascript object declarations" +" J1 -> see j1 +" w1 -> starting a new line with `(` at the same indent as `(` +" m1 -> if `)` starts a line, match its indent with the first char of its +" matching `(` line +" (s -> use one indent, when starting a new line after a trailing `(` +setlocal cinoptions=L0,m1,(s,j1,J1,l1 + +" cinkeys: controls what keys trigger indent formatting +" 0{ -> { +" 0} -> } +" 0) -> ) +" 0] -> ] +" !^F -> make CTRL-F (^F) reindent the current line when typed +" o -> when <CR> or `o` is used +" O -> when the `O` command is used +setlocal cinkeys=0{,0},0),0],!^F,o,O + +setlocal indentexpr=GetZigIndent(v:lnum) + +let b:undo_indent = "setlocal cindent< cinkeys< cinoptions< indentexpr<" + +function! GetZigIndent(lnum) + let curretLineNum = a:lnum + let currentLine = getline(a:lnum) + + " cindent doesn't handle multi-line strings properly, so force no indent + if currentLine =~ '^\s*\\\\.*' + return -1 + endif + + let prevLineNum = prevnonblank(a:lnum-1) + let prevLine = getline(prevLineNum) + + " for lines that look like + " }, + " }; + " try treating them the same as a } + if prevLine =~ '\v^\s*},$' + if currentLine =~ '\v^\s*};$' || currentLine =~ '\v^\s*}$' + return indent(prevLineNum) - 4 + endif + return indent(prevLineNum-1) - 4 + endif + if currentLine =~ '\v^\s*},$' + return indent(prevLineNum) - 4 + endif + if currentLine =~ '\v^\s*};$' + return indent(prevLineNum) - 4 + endif + + + " cindent doesn't handle this case correctly: + " switch (1): { + " 1 => true, + " ~ + " ^---- indents to here + if prevLine =~ '.*=>.*,$' && currentLine !~ '.*}$' + return indent(prevLineNum) + endif + + return cindent(a:lnum) +endfunction diff --git a/runtime/indent/zimbu.vim b/runtime/indent/zimbu.vim new file mode 100644 index 0000000..08369e4 --- /dev/null +++ b/runtime/indent/zimbu.vim @@ -0,0 +1,129 @@ +" Vim indent file +" Language: Zimbu +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Last Change: 2023 Aug 10 +" Former Maintainer: Bram Moolenaar <Bram@vim.org> + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal ai nolisp nocin +setlocal indentexpr=GetZimbuIndent(v:lnum) +setlocal indentkeys=0{,0},!^F,o,O,0=ELSE,0=ELSEIF,0=CASE,0=DEFAULT,0=FINALLY + +" We impose recommended defaults: no Tabs, 'shiftwidth' = 2 +setlocal sw=2 et + +let b:undo_indent = "setl ai< cin< et< indentkeys< indentexpr< lisp< sw<" + +" Only define the function once. +if exists("*GetZimbuIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +" Come here when loading the script the first time. + +let s:maxoff = 50 " maximum number of lines to look backwards for () + +func GetZimbuIndent(lnum) + let prevLnum = prevnonblank(a:lnum - 1) + if prevLnum == 0 + " This is the first non-empty line, use zero indent. + return 0 + endif + + " Taken from Python indenting: + " If the previous line is inside parenthesis, use the indent of the starting + " line. + " Trick: use the non-existing "dummy" variable to break out of the loop when + " going too far back. + call cursor(prevLnum, 1) + let parlnum = searchpair('(\|{\|\[', '', ')\|}\|\]', 'nbW', + \ "line('.') < " . (prevLnum - s:maxoff) . " ? dummy :" + \ . " synIDattr(synID(line('.'), col('.'), 1), 'name')" + \ . " =~ '\\(Comment\\|String\\|Char\\)$'") + if parlnum > 0 + let plindent = indent(parlnum) + let plnumstart = parlnum + else + let plindent = indent(prevLnum) + let plnumstart = prevLnum + endif + + + " When inside parenthesis: If at the first line below the parenthesis add + " two 'shiftwidth', otherwise same as previous line. + " i = (a + " + b + " + c) + call cursor(a:lnum, 1) + let p = searchpair('(\|{\|\[', '', ')\|}\|\]', 'bW', + \ "line('.') < " . (a:lnum - s:maxoff) . " ? dummy :" + \ . " synIDattr(synID(line('.'), col('.'), 1), 'name')" + \ . " =~ '\\(Comment\\|String\\|Char\\)$'") + if p > 0 + if p == prevLnum + " When the start is inside parenthesis, only indent one 'shiftwidth'. + let pp = searchpair('(\|{\|\[', '', ')\|}\|\]', 'bW', + \ "line('.') < " . (a:lnum - s:maxoff) . " ? dummy :" + \ . " synIDattr(synID(line('.'), col('.'), 1), 'name')" + \ . " =~ '\\(Comment\\|String\\|Char\\)$'") + if pp > 0 + return indent(prevLnum) + shiftwidth() + endif + return indent(prevLnum) + shiftwidth() * 2 + endif + if plnumstart == p + return indent(prevLnum) + endif + return plindent + endif + + let prevline = getline(prevLnum) + let thisline = getline(a:lnum) + + " If this line is not a comment and the previous one is then move the + " previous line further back. + if thisline !~ '^\s*#' + while prevline =~ '^\s*#' + let prevLnum = prevnonblank(prevLnum - 1) + if prevLnum == 0 + " Only comment lines before this, no indent + return 0 + endif + let prevline = getline(prevLnum) + let plindent = indent(prevLnum) + endwhile + endif + + if prevline =~ '^\s*\(IF\|\|ELSEIF\|ELSE\|GENERATE_IF\|\|GENERATE_ELSEIF\|GENERATE_ELSE\|WHILE\|REPEAT\|TRY\|CATCH\|FINALLY\|FOR\|DO\|SWITCH\|CASE\|DEFAULT\|FUNC\|VIRTUAL\|ABSTRACT\|DEFINE\|REPLACE\|FINAL\|PROC\|MAIN\|NEW\|ENUM\|CLASS\|INTERFACE\|BITS\|MODULE\|SHARED\)\>' + let plindent += shiftwidth() + endif + if thisline =~ '^\s*\(}\|ELSEIF\>\|ELSE\>\|CATCH\|FINALLY\|GENERATE_ELSEIF\>\|GENERATE_ELSE\>\|UNTIL\>\)' + let plindent -= shiftwidth() + endif + if thisline =~ '^\s*\(CASE\>\|DEFAULT\>\)' && prevline !~ '^\s*SWITCH\>' + let plindent -= shiftwidth() + endif + + " line up continued comment that started after some code + " String something # comment comment + " # comment + if a:lnum == prevLnum + 1 && thisline =~ '^\s*#' && prevline !~ '^\s*#' + let n = match(prevline, '#') + if n > 1 + let plindent = n + endif + endif + + return plindent +endfunc + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/indent/zsh.vim b/runtime/indent/zsh.vim new file mode 100644 index 0000000..8e30c65 --- /dev/null +++ b/runtime/indent/zsh.vim @@ -0,0 +1,14 @@ +" Vim indent file +" Language: Zsh shell script +" Maintainer: Christian Brabandt <cb@256bit.org> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Latest Revision: 2015-05-29 +" License: Vim (see :h license) +" Repository: https://github.com/chrisbra/vim-zsh + +if exists("b:did_indent") + finish +endif + +" Same as sh indenting for now. +runtime! indent/sh.vim |