77" License: Vim (see :h license)
88" Repository: https://github.com/chrisbra/vim-sh-indent
99" Changelog:
10+ " 20250318 - Detect local arrays in functions
1011" 20241411 - Detect dash character in function keyword for
1112" bash mode (issue #16049)
1213" 20190726 - Correctly skip if keywords in syntax comments
@@ -73,6 +74,8 @@ function! s:indent_value(option)
7374endfunction
7475
7576function ! GetShIndent ()
77+ let mode = mode ()
78+
7679 let curline = getline (v: lnum )
7780 let lnum = prevnonblank (v: lnum - 1 )
7881 if lnum == 0
@@ -86,8 +89,11 @@ function! GetShIndent()
8689
8790 " Check contents of previous lines
8891 " should not apply to e.g. commented lines
89- if line = ~ ' ^\s*\%(if\|then\|do\|else\|elif\|case\|while\|until\|for\|select\|foreach\)\>' ||
90- \ (&ft is # ' zsh' && line = ~ ' ^\s*\<\%(if\|then\|do\|else\|elif\|case\|while\|until\|for\|select\|foreach\)\>' )
92+
93+ if s: start_block (line )
94+ let ind += s: indent_value (' default' )
95+ elseif line = ~ ' ^\s*\%(if\|then\|do\|else\|elif\|case\|while\|until\|for\|select\|foreach\)\>\($\|\s\)' ||
96+ \ (&ft is # ' zsh' && line = ~ ' ^\s*\<\%(if\|then\|do\|else\|elif\|case\|while\|until\|for\|select\|foreach\)\>\($\|\s\)' )
9197 if ! s: is_end_expression (line )
9298 let ind += s: indent_value (' default' )
9399 endif
@@ -111,7 +117,7 @@ function! GetShIndent()
111117 let ind += s: indent_value (' continuation-line' )
112118 endif
113119 elseif s: end_block (line ) && ! s: start_block (line )
114- let ind = indent (lnum )
120+ let ind -= s: indent_value ( ' default ' )
115121 elseif pnum != 0 &&
116122 \ s: is_continuation_line (pline) &&
117123 \ ! s: end_block (curline) &&
@@ -122,7 +128,7 @@ function! GetShIndent()
122128 while ! s: is_empty (getline (i )) && i > pnum
123129 let i -= 1
124130 endw
125- if i == pnum
131+ if i == pnum && ( s: is_continuation_line ( line ) || pline = ~ ' {\s*\(#.*\)\=$ ' )
126132 let ind += ind2
127133 else
128134 let ind = ind2
@@ -136,7 +142,11 @@ function! GetShIndent()
136142 " TODO: should we do the same for other "end" lines?
137143 if curline = ~ ' ^\s*\%(fi\);\?\s*\%(#.*\)\=$'
138144 let ind = indent (v: lnum )
139- let previous_line = searchpair (' \<if\>' , ' ' , ' \<fi\>\zs' , ' bnW' , ' synIDattr(synID(line("."),col("."), 1),"name") =~? "comment\\|quote"' )
145+ " in insert mode, try to place the cursor after the fi statement
146+ let endp = ' \<fi\>' .. (mode == ? ' i' ? ' \zs' : ' ' )
147+ let startp = ' ^\s*\<if\>'
148+ let previous_line = searchpair (startp, ' ' , endp , ' bnW' ,
149+ \ ' synIDattr(synID(line("."),col("."), 1),"name") =~? "comment\\|quote\\|option"' )
140150 if previous_line > 0
141151 let ind = indent (previous_line)
142152 endif
@@ -167,7 +177,13 @@ function! GetShIndent()
167177 elseif match (map (synstack (v: lnum , 1 ), ' synIDattr(v:val, "name")' ), ' \c\mheredoc' ) > -1
168178 return indent (v: lnum )
169179 elseif s: is_comment (line ) && s: is_empty (getline (v: lnum- 1 ))
170- return indent (v: lnum )
180+ if s: is_in_block (v: lnum )
181+ " return indent of line in same block
182+ return indent (lnum)
183+ else
184+ " use indent of current line
185+ return indent (v: lnum )
186+ endif
171187 endif
172188
173189 return ind > 0 ? ind : 0
@@ -203,7 +219,18 @@ function! s:is_function_definition(line)
203219endfunction
204220
205221function ! s: is_array (line )
206- return a: line = ~ ' ^\s*\<\k\+\>=('
222+ return a: line = ~ ' ^\s*\(\(declare\|typeset\|local\)\s\+\(-[Aalrtu]\+\s\+\)\?\)\?\<\k\+\>=('
223+ endfunction
224+
225+ function ! s: is_in_block (line )
226+ " checks whether a:line is whithin a
227+ " block e.g. a shell function
228+ " foo() {
229+ " ..
230+ " }
231+ let prevline = searchpair (' {' , ' ' , ' }' , ' bnW' , ' synIDattr(synID(line("."),col("."), 1),"name") =~? "comment\\|quote"' )
232+ let nextline = searchpair (' {' , ' ' , ' }' , ' nW' , ' synIDattr(synID(line("."),col("."), 1),"name") =~? "comment\\|quote"' )
233+ return a: line > prevline && a: line < nextline
207234endfunction
208235
209236function ! s: is_case_label (line , pnum)
@@ -280,15 +307,8 @@ function! s:end_block(line)
280307endfunction
281308
282309function ! s: start_block (line )
283- return a: line = ~ ' {\s*\(#.*\)\?$'
284- endfunction
285-
286- function ! s: find_start_block (lnum)
287- let i = a: lnum
288- while i > 1 && ! s: start_block (getline (i ))
289- let i -= 1
290- endwhile
291- return i
310+ # return a: line = ~ ' {\s*\(#.*\)\?$'
311+ return a: line = ~ ' ^[^#]*[{(]\s*\(#.*\)\?$'
292312endfunction
293313
294314function ! s: is_comment (line )
@@ -300,7 +320,11 @@ function! s:is_end_expression(line)
300320endfunction
301321
302322function ! s: is_bash ()
303- return get (g: , ' is_bash' , 0 ) || get (b: , ' is_bash' , 0 )
323+ if &ft is # ' bash' || getline (1 ) is # ' #!/bin/bash'
324+ return v: true
325+ else
326+ return get (g: , ' is_bash' , 0 ) || get (b: , ' is_bash' , 0 )
327+ endif
304328endfunction
305329
306330let &cpo = s: cpo_save
0 commit comments