xref: /vim-8.2.3635/runtime/indent/sh.vim (revision 2bf24176)
1" Vim indent file
2" Language:            Shell Script
3" Maintainer:          Christian Brabandt <[email protected]>
4" Previous Maintainer: Peter Aronoff <[email protected]>
5" Original Author:     Nikolai Weibull <[email protected]>
6" Latest Revision:     2015-12-15
7" License:             Vim (see :h license)
8" Repository:          https://github.com/chrisbra/vim-sh-indent
9
10if exists("b:did_indent")
11  finish
12endif
13let b:did_indent = 1
14
15let b:undo_indent = 'setlocal indentexpr< indentkeys< smartindent<'
16
17setlocal indentexpr=GetShIndent()
18setlocal indentkeys+=0=then,0=do,0=else,0=elif,0=fi,0=esac,0=done,0=end,),0=;;,0=;&
19setlocal indentkeys+=0=fin,0=fil,0=fip,0=fir,0=fix
20setlocal indentkeys-=:,0#
21setlocal nosmartindent
22
23if exists("*GetShIndent")
24  finish
25endif
26
27let s:cpo_save = &cpo
28set cpo&vim
29
30function s:buffer_shiftwidth()
31  return &shiftwidth
32endfunction
33
34let s:sh_indent_defaults = {
35      \ 'default': function('s:buffer_shiftwidth'),
36      \ 'continuation-line': function('s:buffer_shiftwidth'),
37      \ 'case-labels': function('s:buffer_shiftwidth'),
38      \ 'case-statements': function('s:buffer_shiftwidth'),
39      \ 'case-breaks': 0 }
40
41function! s:indent_value(option)
42  let Value = exists('b:sh_indent_options')
43            \ && has_key(b:sh_indent_options, a:option) ?
44            \ b:sh_indent_options[a:option] :
45            \ s:sh_indent_defaults[a:option]
46  if type(Value) == type(function('type'))
47    return Value()
48  endif
49  return Value
50endfunction
51
52function! GetShIndent()
53  let lnum = prevnonblank(v:lnum - 1)
54  if lnum == 0
55    return 0
56  endif
57
58  let pnum = prevnonblank(lnum - 1)
59
60  let ind = indent(lnum)
61  let line = getline(lnum)
62  if line =~ '^\s*\%(if\|then\|do\|else\|elif\|case\|while\|until\|for\|select\|foreach\)\>'
63    if line !~ '\<\%(fi\|esac\|done\|end\)\>\s*\%(#.*\)\=$'
64      let ind += s:indent_value('default')
65    endif
66  elseif s:is_case_label(line, pnum)
67    if !s:is_case_ended(line)
68      let ind += s:indent_value('case-statements')
69    endif
70  elseif line =~ '^\s*\<\k\+\>\s*()\s*{' || line =~ '^\s*{'
71    if line !~ '}\s*\%(#.*\)\=$'
72      let ind += s:indent_value('default')
73    endif
74  elseif s:is_continuation_line(line)
75    if pnum == 0 || !s:is_continuation_line(getline(pnum))
76      let ind += s:indent_value('continuation-line')
77    endif
78  elseif pnum != 0 && s:is_continuation_line(getline(pnum))
79    let ind = indent(s:find_continued_lnum(pnum))
80  endif
81
82  let pine = line
83  let line = getline(v:lnum)
84  if line =~ '^\s*\%(then\|do\|else\|elif\|fi\|done\|end\)\>' || line =~ '^\s*}'
85    let ind -= s:indent_value('default')
86  elseif line =~ '^\s*esac\>' && s:is_case_empty(getline(v:lnum - 1))
87    let ind -= s:indent_value('default')
88  elseif line =~ '^\s*esac\>'
89    let ind -= (s:is_case_label(pine, lnum) && s:is_case_ended(pine) ?
90             \ 0 : s:indent_value('case-statements')) +
91             \ s:indent_value('case-labels')
92    if s:is_case_break(pine)
93      let ind += s:indent_value('case-breaks')
94    endif
95  elseif s:is_case_label(line, lnum)
96    if s:is_case(pine)
97      let ind = indent(lnum) + s:indent_value('case-labels')
98    else
99      let ind -= (s:is_case_label(pine, lnum) && s:is_case_ended(pine) ?
100                  \ 0 : s:indent_value('case-statements')) -
101                  \ s:indent_value('case-breaks')
102    endif
103  elseif s:is_case_break(line)
104    let ind -= s:indent_value('case-breaks')
105  endif
106
107  return ind
108endfunction
109
110function! s:is_continuation_line(line)
111  return a:line =~ '\%(\%(^\|[^\\]\)\\\|&&\|||\)$'
112endfunction
113
114function! s:find_continued_lnum(lnum)
115  let i = a:lnum
116  while i > 1 && s:is_continuation_line(getline(i - 1))
117    let i -= 1
118  endwhile
119  return i
120endfunction
121
122function! s:is_case_label(line, pnum)
123  if a:line !~ '^\s*(\=.*)'
124    return 0
125  endif
126
127  if a:pnum > 0
128    let pine = getline(a:pnum)
129    if !(s:is_case(pine) || s:is_case_ended(pine))
130      return 0
131    endif
132  endif
133
134  let suffix = substitute(a:line, '^\s*(\=', "", "")
135  let nesting = 0
136  let i = 0
137  let n = strlen(suffix)
138  while i < n
139    let c = suffix[i]
140    let i += 1
141    if c == '\\'
142      let i += 1
143    elseif c == '('
144      let nesting += 1
145    elseif c == ')'
146      if nesting == 0
147        return 1
148      endif
149      let nesting -= 1
150    endif
151  endwhile
152  return 0
153endfunction
154
155function! s:is_case(line)
156  return a:line =~ '^\s*case\>'
157endfunction
158
159function! s:is_case_break(line)
160  return a:line =~ '^\s*;[;&]'
161endfunction
162
163function! s:is_case_ended(line)
164  return s:is_case_break(a:line) || a:line =~ ';[;&]\s*\%(#.*\)\=$'
165endfunction
166
167function! s:is_case_empty(line)
168  if a:line =~ '^\s*$' || a:line =~ '^\s*#'
169    return s:is_case_empty(getline(v:lnum - 1))
170  else
171    return a:line =~ '^\s*case\>'
172  endif
173endfunction
174
175let &cpo = s:cpo_save
176unlet s:cpo_save
177