vim.vim (6751B)
1 " Vim indent file 2 " Language: Vim script 3 " Maintainer: The Vim Project <https://github.com/vim/vim> 4 " Last Change: 2023 Aug 10 5 " Former Maintainer: Bram Moolenaar <Bram@vim.org> 6 7 " Only load this indent file when no other was loaded. 8 if exists("b:did_indent") 9 finish 10 endif 11 let b:did_indent = 1 12 13 setlocal indentexpr=GetVimIndent() 14 setlocal indentkeys+==endif,=enddef,=endfu,=endfor,=endwh,=endtry,=},=else,=cat,=finall,=END,0\\,0=\"\\\ 15 setlocal indentkeys-=0# 16 setlocal indentkeys-=: 17 18 let b:undo_indent = "setl indentkeys< indentexpr<" 19 20 " Only define the function once. 21 if exists("*GetVimIndent") 22 finish 23 endif 24 let s:keepcpo= &cpo 25 set cpo&vim 26 27 function GetVimIndent() 28 let ignorecase_save = &ignorecase 29 try 30 let &ignorecase = 0 31 return GetVimIndentIntern() 32 finally 33 let &ignorecase = ignorecase_save 34 endtry 35 endfunc 36 37 " Legacy script line continuation and Vim9 script operators that must mean an 38 " expression that continues from the previous line. 39 let s:lineContPat = '^\s*\(\\\|"\\ \|->\)' 40 41 function GetVimIndentIntern() 42 " If the current line has line continuation and the previous one too, use 43 " the same indent. This does not skip empty lines. 44 let cur_text = getline(v:lnum) 45 let cur_has_linecont = cur_text =~ s:lineContPat 46 if cur_has_linecont && v:lnum > 1 && getline(v:lnum - 1) =~ s:lineContPat 47 return indent(v:lnum - 1) 48 endif 49 50 " Find a non-blank line above the current line. 51 let lnum = prevnonblank(v:lnum - 1) 52 53 " The previous line, ignoring line continuation 54 let prev_text_end = lnum > 0 ? getline(lnum) : '' 55 56 " If the current line doesn't start with '\' or '"\ ' and below a line that 57 " starts with '\' or '"\ ', use the indent of the line above it. 58 if !cur_has_linecont 59 while lnum > 0 && getline(lnum) =~ s:lineContPat 60 let lnum = lnum - 1 61 endwhile 62 endif 63 64 " At the start of the file use zero indent. 65 if lnum == 0 66 return 0 67 endif 68 69 " the start of the previous line, skipping over line continuation 70 let prev_text = getline(lnum) 71 let found_cont = 0 72 73 " Add a 'shiftwidth' after :if, :while, :try, :catch, :finally, :function 74 " and :else. Add it three times for a line that starts with '\' or '"\ ' 75 " after a line that doesn't (or g:vim_indent_cont if it exists). 76 let ind = indent(lnum) 77 78 " In heredoc indenting works completely differently. 79 if has('syntax_items') 80 let syn_here = synIDattr(synID(v:lnum, 1, 1), "name") 81 if syn_here =~ 'vimLetHereDocStop' 82 " End of heredoc: use indent of matching start line 83 let lnum = v:lnum - 1 84 while lnum > 0 85 let attr = synIDattr(synID(lnum, 1, 1), "name") 86 if attr != '' && attr !~ 'vimLetHereDoc' 87 return indent(lnum) 88 endif 89 let lnum -= 1 90 endwhile 91 return 0 92 endif 93 if syn_here =~ 'vimLetHereDoc' 94 if synIDattr(synID(lnum, 1, 1), "name") !~ 'vimLetHereDoc' 95 " First line in heredoc: increase indent 96 return ind + shiftwidth() 97 endif 98 " Heredoc continues: no change in indent 99 return ind 100 endif 101 endif 102 103 if cur_text =~ s:lineContPat && v:lnum > 1 && prev_text !~ s:lineContPat 104 let found_cont = 1 105 if exists("g:vim_indent_cont") 106 let ind = ind + g:vim_indent_cont 107 else 108 let ind = ind + shiftwidth() * 3 109 endif 110 elseif prev_text =~ '^\s*aug\%[roup]\s\+' && prev_text !~ '^\s*aug\%[roup]\s\+[eE][nN][dD]\>' 111 let ind = ind + shiftwidth() 112 else 113 " A line starting with :au does not increment/decrement indent. 114 " A { may start a block or a dict. Assume that when a } follows it's a 115 " terminated dict. 116 " ":function" starts a block but "function(" doesn't. 117 if prev_text !~ '^\s*au\%[tocmd]' && prev_text !~ '^\s*{.*}' 118 let i = match(prev_text, '\(^\||\)\s*\(export\s\+\)\?\({\|\(if\|wh\%[ile]\|for\|try\|cat\%[ch]\|fina\|finall\%[y]\|def\|el\%[seif]\)\>\|fu\%[nction][! ]\)') 119 if i >= 0 120 let ind += shiftwidth() 121 if strpart(prev_text, i, 1) == '|' && has('syntax_items') 122 \ && synIDattr(synID(lnum, i, 1), "name") =~ '\(Comment\|String\|PatSep\)$' 123 let ind -= shiftwidth() 124 endif 125 endif 126 endif 127 endif 128 129 " If the previous line contains an "end" after a pipe, but not in an ":au" 130 " command. And not when there is a backslash before the pipe. 131 " And when syntax HL is enabled avoid a match inside a string. 132 let i = match(prev_text, '[^\\]|\s*\(ene\@!\)') 133 if i > 0 && prev_text !~ '^\s*au\%[tocmd]' 134 if !has('syntax_items') || synIDattr(synID(lnum, i + 2, 1), "name") !~ '\(Comment\|String\)$' 135 let ind = ind - shiftwidth() 136 endif 137 endif 138 139 " For a line starting with "}" find the matching "{". Align with that line, 140 " it is either the matching block start or dictionary start. 141 " Use the mapped "%" from matchit to find the match, otherwise we may match 142 " a { inside a comment or string. 143 if cur_text =~ '^\s*}' 144 if maparg('%') != '' 145 exe v:lnum 146 silent! normal % 147 if line('.') < v:lnum 148 let ind = indent('.') 149 endif 150 else 151 " todo: use searchpair() to find a match 152 endif 153 endif 154 155 " Look back for a line to align with 156 while lnum > 1 157 " Below a line starting with "}" find the matching "{". 158 if prev_text =~ '^\s*}' 159 if maparg('%') != '' 160 exe lnum 161 silent! normal % 162 if line('.') < lnum 163 let lnum = line('.') 164 let ind = indent(lnum) 165 let prev_text = getline(lnum) 166 else 167 break 168 endif 169 else 170 " todo: use searchpair() to find a match 171 break 172 endif 173 elseif prev_text =~ s:lineContPat 174 " looks like a continuation like, go back one line 175 let lnum = lnum - 1 176 let ind = indent(lnum) 177 let prev_text = getline(lnum) 178 else 179 break 180 endif 181 endwhile 182 183 " Below a line starting with "]" we must be below the end of a list. 184 " Include a "}" and "},} in case a dictionary ends too. 185 if prev_text_end =~ '^\s*\(},\=\s*\)\=]' 186 let ind = ind - shiftwidth() 187 endif 188 189 let ends_in_comment = has('syntax_items') 190 \ && synIDattr(synID(lnum, len(getline(lnum)), 1), "name") =~ '\(Comment\|String\)$' 191 192 " A line ending in "{" or "[" is most likely the start of a dict/list literal, 193 " indent the next line more. Not for a continuation line or {{{. 194 if !ends_in_comment && prev_text_end =~ '\s[{[]\s*$' && !found_cont 195 let ind = ind + shiftwidth() 196 endif 197 198 " Subtract a 'shiftwidth' on a :endif, :endwhile, :endfor, :catch, :finally, 199 " :endtry, :endfun, :enddef, :else and :augroup END. 200 " Although ":en" would be enough only match short command names as in 201 " 'indentkeys'. 202 if cur_text =~ '^\s*\(endif\|endwh\|endfor\|endtry\|endfu\|enddef\|cat\|finall\|else\|aug\%[roup]\s\+[eE][nN][dD]\)' 203 let ind = ind - shiftwidth() 204 if ind < 0 205 let ind = 0 206 endif 207 endif 208 209 return ind 210 endfunction 211 212 let &cpo = s:keepcpo 213 unlet s:keepcpo 214 215 " vim:sw=2