1071d4279SBram Moolenaar" Vim indent file 2071d4279SBram Moolenaar" Language: Vim script 3071d4279SBram Moolenaar" Maintainer: Bram Moolenaar <[email protected]> 4*113cb513SBram Moolenaar" Last Change: 2021 Nov 03 5071d4279SBram Moolenaar 6071d4279SBram Moolenaar" Only load this indent file when no other was loaded. 7071d4279SBram Moolenaarif exists("b:did_indent") 8071d4279SBram Moolenaar finish 9071d4279SBram Moolenaarendif 10071d4279SBram Moolenaarlet b:did_indent = 1 11071d4279SBram Moolenaar 12071d4279SBram Moolenaarsetlocal indentexpr=GetVimIndent() 13d58a3bf7SBram Moolenaarsetlocal indentkeys+==end,=},=else,=cat,=finall,=END,0\\,0=\"\\\ 142547aa93SBram Moolenaarsetlocal indentkeys-=0# 15071d4279SBram Moolenaar 16c873442bSBram Moolenaarlet b:undo_indent = "setl indentkeys< indentexpr<" 17c873442bSBram Moolenaar 18071d4279SBram Moolenaar" Only define the function once. 19071d4279SBram Moolenaarif exists("*GetVimIndent") 20071d4279SBram Moolenaar finish 21071d4279SBram Moolenaarendif 228e52a593SBram Moolenaarlet s:keepcpo= &cpo 238e52a593SBram Moolenaarset cpo&vim 24071d4279SBram Moolenaar 25071d4279SBram Moolenaarfunction GetVimIndent() 269b451250SBram Moolenaar let ignorecase_save = &ignorecase 279b451250SBram Moolenaar try 289b451250SBram Moolenaar let &ignorecase = 0 299b451250SBram Moolenaar return GetVimIndentIntern() 309b451250SBram Moolenaar finally 319b451250SBram Moolenaar let &ignorecase = ignorecase_save 329b451250SBram Moolenaar endtry 339b451250SBram Moolenaarendfunc 349b451250SBram Moolenaar 3567f8ab82SBram Moolenaarlet s:lineContPat = '^\s*\(\\\|"\\ \)' 3667f8ab82SBram Moolenaar 379b451250SBram Moolenaarfunction GetVimIndentIntern() 38071d4279SBram Moolenaar " Find a non-blank line above the current line. 39071d4279SBram Moolenaar let lnum = prevnonblank(v:lnum - 1) 40071d4279SBram Moolenaar 41e0e39175SBram Moolenaar " The previous line, ignoring line continuation 42e0e39175SBram Moolenaar let prev_text_end = lnum > 0 ? getline(lnum) : '' 43e0e39175SBram Moolenaar 4467f8ab82SBram Moolenaar " If the current line doesn't start with '\' or '"\ ' and below a line that 4567f8ab82SBram Moolenaar " starts with '\' or '"\ ', use the indent of the line above it. 4691e15e13SBram Moolenaar let cur_text = getline(v:lnum) 4767f8ab82SBram Moolenaar if cur_text !~ s:lineContPat 4867f8ab82SBram Moolenaar while lnum > 0 && getline(lnum) =~ s:lineContPat 49071d4279SBram Moolenaar let lnum = lnum - 1 50071d4279SBram Moolenaar endwhile 51071d4279SBram Moolenaar endif 52071d4279SBram Moolenaar 53071d4279SBram Moolenaar " At the start of the file use zero indent. 54071d4279SBram Moolenaar if lnum == 0 55071d4279SBram Moolenaar return 0 56071d4279SBram Moolenaar endif 57e0e39175SBram Moolenaar 58e0e39175SBram Moolenaar " the start of the previous line, skipping over line continuation 5991e15e13SBram Moolenaar let prev_text = getline(lnum) 6082be4849SBram Moolenaar let found_cont = 0 61071d4279SBram Moolenaar 62071d4279SBram Moolenaar " Add a 'shiftwidth' after :if, :while, :try, :catch, :finally, :function 6367f8ab82SBram Moolenaar " and :else. Add it three times for a line that starts with '\' or '"\ ' 6467f8ab82SBram Moolenaar " after a line that doesn't (or g:vim_indent_cont if it exists). 65071d4279SBram Moolenaar let ind = indent(lnum) 661ff14ba2SBram Moolenaar 671ff14ba2SBram Moolenaar " In heredoc indenting works completely differently. 681ff14ba2SBram Moolenaar if has('syntax_items') 691ff14ba2SBram Moolenaar let syn_here = synIDattr(synID(v:lnum, 1, 1), "name") 701ff14ba2SBram Moolenaar if syn_here =~ 'vimLetHereDocStop' 711ff14ba2SBram Moolenaar " End of heredoc: use indent of matching start line 721ff14ba2SBram Moolenaar let lnum = v:lnum - 1 731ff14ba2SBram Moolenaar while lnum > 0 7411e3c5baSBram Moolenaar let attr = synIDattr(synID(lnum, 1, 1), "name") 7511e3c5baSBram Moolenaar if attr != '' && attr !~ 'vimLetHereDoc' 761ff14ba2SBram Moolenaar return indent(lnum) 771ff14ba2SBram Moolenaar endif 781ff14ba2SBram Moolenaar let lnum -= 1 791ff14ba2SBram Moolenaar endwhile 801ff14ba2SBram Moolenaar return 0 811ff14ba2SBram Moolenaar endif 821ff14ba2SBram Moolenaar if syn_here =~ 'vimLetHereDoc' 831ff14ba2SBram Moolenaar if synIDattr(synID(lnum, 1, 1), "name") !~ 'vimLetHereDoc' 841ff14ba2SBram Moolenaar " First line in heredoc: increase indent 851ff14ba2SBram Moolenaar return ind + shiftwidth() 861ff14ba2SBram Moolenaar endif 871ff14ba2SBram Moolenaar " Heredoc continues: no change in indent 881ff14ba2SBram Moolenaar return ind 891ff14ba2SBram Moolenaar endif 901ff14ba2SBram Moolenaar endif 911ff14ba2SBram Moolenaar 9267f8ab82SBram Moolenaar if cur_text =~ s:lineContPat && v:lnum > 1 && prev_text !~ s:lineContPat 9382be4849SBram Moolenaar let found_cont = 1 94d4755bb0SBram Moolenaar if exists("g:vim_indent_cont") 95d4755bb0SBram Moolenaar let ind = ind + g:vim_indent_cont 96d4755bb0SBram Moolenaar else 97705ada1aSBram Moolenaar let ind = ind + shiftwidth() * 3 98d4755bb0SBram Moolenaar endif 99e18dbe86SBram Moolenaar elseif prev_text =~ '^\s*aug\%[roup]\s\+' && prev_text !~ '^\s*aug\%[roup]\s\+[eE][nN][dD]\>' 100705ada1aSBram Moolenaar let ind = ind + shiftwidth() 101cab49dffSBram Moolenaar else 10291e15e13SBram Moolenaar " A line starting with :au does not increment/decrement indent. 103942db23cSBram Moolenaar " A { may start a block or a dict. Assume that when a } follows it's a 104942db23cSBram Moolenaar " terminated dict. 105942db23cSBram Moolenaar if prev_text !~ '^\s*au\%[tocmd]' && prev_text !~ '^\s*{.*}' 106d58a3bf7SBram Moolenaar let i = match(prev_text, '\(^\||\)\s*\(export\s\+\)\?\({\|\(if\|wh\%[ile]\|for\|try\|cat\%[ch]\|fina\|finall\%[y]\|fu\%[nction]\|def\|el\%[seif]\)\>\)') 107cab49dffSBram Moolenaar if i >= 0 108705ada1aSBram Moolenaar let ind += shiftwidth() 10991e15e13SBram Moolenaar if strpart(prev_text, i, 1) == '|' && has('syntax_items') 110*113cb513SBram Moolenaar \ && synIDattr(synID(lnum, i, 1), "name") =~ '\(Comment\|String\|PatSep\)$' 111705ada1aSBram Moolenaar let ind -= shiftwidth() 112cab49dffSBram Moolenaar endif 113cab49dffSBram Moolenaar endif 114071d4279SBram Moolenaar endif 11591e15e13SBram Moolenaar endif 116071d4279SBram Moolenaar 117071d4279SBram Moolenaar " If the previous line contains an "end" after a pipe, but not in an ":au" 118520470a9SBram Moolenaar " command. And not when there is a backslash before the pipe. 119dc27ac1cSBram Moolenaar " And when syntax HL is enabled avoid a match inside a string. 12091e15e13SBram Moolenaar let i = match(prev_text, '[^\\]|\s*\(ene\@!\)') 12191e15e13SBram Moolenaar if i > 0 && prev_text !~ '^\s*au\%[tocmd]' 122dc27ac1cSBram Moolenaar if !has('syntax_items') || synIDattr(synID(lnum, i + 2, 1), "name") !~ '\(Comment\|String\)$' 123705ada1aSBram Moolenaar let ind = ind - shiftwidth() 124071d4279SBram Moolenaar endif 125dc27ac1cSBram Moolenaar endif 126071d4279SBram Moolenaar 12782be4849SBram Moolenaar " For a line starting with "}" find the matching "{". If it is at the start 12882be4849SBram Moolenaar " of the line align with it, probably end of a block. 12982be4849SBram Moolenaar " Use the mapped "%" from matchit to find the match, otherwise we may match 13082be4849SBram Moolenaar " a { inside a comment or string. 13182be4849SBram Moolenaar if cur_text =~ '^\s*}' 13282be4849SBram Moolenaar if maparg('%') != '' 13382be4849SBram Moolenaar exe v:lnum 13482be4849SBram Moolenaar silent! normal % 13582be4849SBram Moolenaar if line('.') < v:lnum && getline('.') =~ '^\s*{' 13682be4849SBram Moolenaar let ind = indent('.') 13782be4849SBram Moolenaar endif 13882be4849SBram Moolenaar else 13982be4849SBram Moolenaar " todo: use searchpair() to find a match 14082be4849SBram Moolenaar endif 14182be4849SBram Moolenaar endif 14282be4849SBram Moolenaar 14382be4849SBram Moolenaar " Below a line starting with "}" find the matching "{". If it is at the 14482be4849SBram Moolenaar " end of the line we must be below the end of a dictionary. 14582be4849SBram Moolenaar if prev_text =~ '^\s*}' 14682be4849SBram Moolenaar if maparg('%') != '' 14782be4849SBram Moolenaar exe lnum 14882be4849SBram Moolenaar silent! normal % 14982be4849SBram Moolenaar if line('.') == lnum || getline('.') !~ '^\s*{' 15082be4849SBram Moolenaar let ind = ind - shiftwidth() 15182be4849SBram Moolenaar endif 15282be4849SBram Moolenaar else 15382be4849SBram Moolenaar " todo: use searchpair() to find a match 15482be4849SBram Moolenaar endif 15582be4849SBram Moolenaar endif 15682be4849SBram Moolenaar 15782be4849SBram Moolenaar " Below a line starting with "]" we must be below the end of a list. 158942db23cSBram Moolenaar " Include a "}" and "},} in case a dictionary ends too. 159942db23cSBram Moolenaar if prev_text_end =~ '^\s*\(},\=\s*\)\=]' 16082be4849SBram Moolenaar let ind = ind - shiftwidth() 16182be4849SBram Moolenaar endif 16282be4849SBram Moolenaar 163942db23cSBram Moolenaar let ends_in_comment = has('syntax_items') 1649faec4e3SBram Moolenaar \ && synIDattr(synID(lnum, len(getline(lnum)), 1), "name") =~ '\(Comment\|String\)$' 165942db23cSBram Moolenaar 1669faec4e3SBram Moolenaar " A line ending in "{" or "[" is most likely the start of a dict/list literal, 167942db23cSBram Moolenaar " indent the next line more. Not for a continuation line or {{{. 168942db23cSBram Moolenaar if !ends_in_comment && prev_text_end =~ '\s[{[]\s*$' && !found_cont 16982be4849SBram Moolenaar let ind = ind + shiftwidth() 17082be4849SBram Moolenaar endif 171071d4279SBram Moolenaar 172071d4279SBram Moolenaar " Subtract a 'shiftwidth' on a :endif, :endwhile, :catch, :finally, :endtry, 1738a7d6542SBram Moolenaar " :endfun, :enddef, :else and :augroup END. 17482be4849SBram Moolenaar if cur_text =~ '^\s*\(ene\@!\|cat\|finall\|el\|aug\%[roup]\s\+[eE][nN][dD]\)' 175705ada1aSBram Moolenaar let ind = ind - shiftwidth() 176071d4279SBram Moolenaar endif 177071d4279SBram Moolenaar 178071d4279SBram Moolenaar return ind 179071d4279SBram Moolenaarendfunction 180071d4279SBram Moolenaar 1818e52a593SBram Moolenaarlet &cpo = s:keepcpo 1828e52a593SBram Moolenaarunlet s:keepcpo 1838e52a593SBram Moolenaar 184071d4279SBram Moolenaar" vim:sw=2 185