| 
									
										
										
										
											2004-06-13 20:20:40 +00:00
										 |  |  | " Vim indent file | 
					
						
							| 
									
										
										
										
											2015-08-11 20:34:49 +02:00
										 |  |  | " Language:            Shell Script | 
					
						
							|  |  |  | " Maintainer:          Christian Brabandt <cb@256bit.org> | 
					
						
							|  |  |  | " Original Author:     Nikolai Weibull <now@bitwi.se> | 
					
						
							| 
									
										
										
										
											2018-05-13 22:34:24 +02:00
										 |  |  | " Previous Maintainer: Peter Aronoff <telemachus@arpinum.org> | 
					
						
							|  |  |  | " Latest Revision:     2018-03-26 | 
					
						
							| 
									
										
										
										
											2015-08-11 20:34:49 +02:00
										 |  |  | " License:             Vim (see :h license) | 
					
						
							|  |  |  | " Repository:          https://github.com/chrisbra/vim-sh-indent | 
					
						
							| 
									
										
										
										
											2016-07-02 21:42:23 +02:00
										 |  |  | " Changelog: | 
					
						
							| 
									
										
										
										
											2018-07-29 15:07:52 +02:00
										 |  |  | "          20180724  - make check for zsh syntax more rigid (needs word-boundaries) | 
					
						
							| 
									
										
										
										
											2018-05-13 22:34:24 +02:00
										 |  |  | "          20180326  - better support for line continuation | 
					
						
							|  |  |  | "          20180325  - better detection of function definitions | 
					
						
							|  |  |  | "          20180127  - better support for zsh complex commands | 
					
						
							| 
									
										
										
										
											2017-08-11 19:50:37 +02:00
										 |  |  | "          20170808: - better indent of line continuation | 
					
						
							| 
									
										
										
										
											2017-05-27 16:45:17 +02:00
										 |  |  | "          20170502: - get rid of buffer-shiftwidth function | 
					
						
							|  |  |  | "          20160912: - preserve indentation of here-doc blocks | 
					
						
							| 
									
										
										
										
											2016-07-02 21:42:23 +02:00
										 |  |  | "          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 | 
					
						
							| 
									
										
										
										
											2004-06-13 20:20:40 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | if exists("b:did_indent") | 
					
						
							|  |  |  |   finish | 
					
						
							|  |  |  | endif | 
					
						
							|  |  |  | let b:did_indent = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | setlocal indentexpr=GetShIndent() | 
					
						
							| 
									
										
										
										
											2015-08-11 20:34:49 +02:00
										 |  |  | setlocal indentkeys+=0=then,0=do,0=else,0=elif,0=fi,0=esac,0=done,0=end,),0=;;,0=;& | 
					
						
							| 
									
										
										
										
											2010-01-06 20:54:52 +01:00
										 |  |  | setlocal indentkeys+=0=fin,0=fil,0=fip,0=fir,0=fix | 
					
						
							| 
									
										
										
										
											2004-06-13 20:20:40 +00:00
										 |  |  | setlocal indentkeys-=:,0# | 
					
						
							| 
									
										
										
										
											2010-01-06 20:54:52 +01:00
										 |  |  | setlocal nosmartindent | 
					
						
							| 
									
										
										
										
											2004-06-13 20:20:40 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-25 00:00:01 +01:00
										 |  |  | let b:undo_indent = 'setlocal indentexpr< indentkeys< smartindent<' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-06-13 20:20:40 +00:00
										 |  |  | if exists("*GetShIndent") | 
					
						
							|  |  |  |   finish | 
					
						
							|  |  |  | endif | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-06-29 22:40:58 +00:00
										 |  |  | let s:cpo_save = &cpo | 
					
						
							|  |  |  | set cpo&vim | 
					
						
							| 
									
										
										
										
											2004-06-13 20:20:40 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-06 20:54:52 +01:00
										 |  |  | let s:sh_indent_defaults = { | 
					
						
							| 
									
										
										
										
											2017-05-27 16:45:17 +02:00
										 |  |  |       \ 'default': function('shiftwidth'), | 
					
						
							|  |  |  |       \ 'continuation-line': function('shiftwidth'), | 
					
						
							|  |  |  |       \ 'case-labels': function('shiftwidth'), | 
					
						
							|  |  |  |       \ 'case-statements': function('shiftwidth'), | 
					
						
							| 
									
										
										
										
											2010-01-06 20:54:52 +01:00
										 |  |  |       \ '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() | 
					
						
							| 
									
										
										
										
											2004-06-13 20:20:40 +00:00
										 |  |  |   let lnum = prevnonblank(v:lnum - 1) | 
					
						
							|  |  |  |   if lnum == 0 | 
					
						
							|  |  |  |     return 0 | 
					
						
							|  |  |  |   endif | 
					
						
							| 
									
										
										
										
											2018-05-13 00:02:36 +02:00
										 |  |  |   let line = getline(lnum) | 
					
						
							| 
									
										
										
										
											2004-06-13 20:20:40 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-06 20:54:52 +01:00
										 |  |  |   let pnum = prevnonblank(lnum - 1) | 
					
						
							| 
									
										
										
										
											2018-05-13 00:02:36 +02:00
										 |  |  |   let pline = getline(pnum) | 
					
						
							| 
									
										
										
										
											2004-06-13 20:20:40 +00:00
										 |  |  |   let ind = indent(lnum) | 
					
						
							| 
									
										
										
										
											2018-05-13 00:02:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   " Check contents of previous lines | 
					
						
							|  |  |  |   if line =~ '^\s*\%(if\|then\|do\|else\|elif\|case\|while\|until\|for\|select\|foreach\)\>' || | 
					
						
							| 
									
										
										
										
											2018-07-29 15:07:52 +02:00
										 |  |  |         \  (&ft is# 'zsh' && line =~ '\<\%(if\|then\|do\|else\|elif\|case\|while\|until\|for\|select\|foreach\)\>') | 
					
						
							| 
									
										
										
										
											2015-08-11 20:34:49 +02:00
										 |  |  |     if line !~ '\<\%(fi\|esac\|done\|end\)\>\s*\%(#.*\)\=$' | 
					
						
							| 
									
										
										
										
											2010-01-06 20:54:52 +01:00
										 |  |  |       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') | 
					
						
							| 
									
										
										
										
											2004-06-13 20:20:40 +00:00
										 |  |  |     endif | 
					
						
							| 
									
										
										
										
											2018-05-13 00:02:36 +02:00
										 |  |  |   " function definition | 
					
						
							|  |  |  |   elseif s:is_function_definition(line) | 
					
						
							| 
									
										
										
										
											2010-01-06 20:54:52 +01:00
										 |  |  |     if line !~ '}\s*\%(#.*\)\=$' | 
					
						
							|  |  |  |       let ind += s:indent_value('default') | 
					
						
							|  |  |  |     endif | 
					
						
							|  |  |  |   elseif s:is_continuation_line(line) | 
					
						
							| 
									
										
										
										
											2018-05-13 00:02:36 +02:00
										 |  |  |     if pnum == 0 || !s:is_continuation_line(pline) | 
					
						
							| 
									
										
										
										
											2010-01-06 20:54:52 +01:00
										 |  |  |       let ind += s:indent_value('continuation-line') | 
					
						
							|  |  |  |     endif | 
					
						
							| 
									
										
										
										
											2018-05-13 00:02:36 +02:00
										 |  |  |   elseif s:end_block(line) && !s:start_block(line) | 
					
						
							|  |  |  |     let ind -= s:indent_value('default') | 
					
						
							|  |  |  |   elseif pnum != 0 && s:is_continuation_line(pline) && !s:end_block(getline(v:lnum)) | 
					
						
							|  |  |  |     " 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 | 
					
						
							| 
									
										
										
										
											2004-06-13 20:20:40 +00:00
										 |  |  |   endif | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-06 20:54:52 +01:00
										 |  |  |   let pine = line | 
					
						
							| 
									
										
										
										
											2018-05-13 00:02:36 +02:00
										 |  |  |   " Check content of current line | 
					
						
							| 
									
										
										
										
											2004-06-13 20:20:40 +00:00
										 |  |  |   let line = getline(v:lnum) | 
					
						
							| 
									
										
										
										
											2018-05-13 00:02:36 +02:00
										 |  |  |   if line =~ '^\s*\%(then\|do\|else\|elif\|fi\|done\|end\)\>' || s:end_block(line) | 
					
						
							| 
									
										
										
										
											2010-01-06 20:54:52 +01:00
										 |  |  |     let ind -= s:indent_value('default') | 
					
						
							| 
									
										
										
										
											2013-12-11 18:53:29 +01:00
										 |  |  |   elseif line =~ '^\s*esac\>' && s:is_case_empty(getline(v:lnum - 1)) | 
					
						
							|  |  |  |     let ind -= s:indent_value('default') | 
					
						
							| 
									
										
										
										
											2010-01-06 20:54:52 +01:00
										 |  |  |   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 | 
					
						
							| 
									
										
										
										
											2014-08-22 19:21:47 +02:00
										 |  |  |       let ind -= (s:is_case_label(pine, lnum) && s:is_case_ended(pine) ? | 
					
						
							|  |  |  |                   \ 0 : s:indent_value('case-statements')) - | 
					
						
							|  |  |  |                   \ s:indent_value('case-breaks') | 
					
						
							| 
									
										
										
										
											2010-01-06 20:54:52 +01:00
										 |  |  |     endif | 
					
						
							|  |  |  |   elseif s:is_case_break(line) | 
					
						
							|  |  |  |     let ind -= s:indent_value('case-breaks') | 
					
						
							| 
									
										
										
										
											2016-07-02 21:42:23 +02:00
										 |  |  |   elseif s:is_here_doc(line) | 
					
						
							|  |  |  |     let ind = 0 | 
					
						
							| 
									
										
										
										
											2017-05-27 16:45:17 +02:00
										 |  |  |   " 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) | 
					
						
							| 
									
										
										
										
											2018-05-13 00:02:36 +02:00
										 |  |  |   elseif s:is_comment(line) && s:is_empty(getline(v:lnum-1)) | 
					
						
							|  |  |  |     return indent(v:lnum) | 
					
						
							| 
									
										
										
										
											2004-06-13 20:20:40 +00:00
										 |  |  |   endif | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-13 00:02:36 +02:00
										 |  |  |   return ind > 0 ? ind : 0 | 
					
						
							| 
									
										
										
										
											2004-06-13 20:20:40 +00:00
										 |  |  | endfunction | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-06 20:54:52 +01:00
										 |  |  | function! s:is_continuation_line(line) | 
					
						
							| 
									
										
										
										
											2018-05-13 00:02:36 +02:00
										 |  |  |   " 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 =~ '\%(\%(^\|[^\\]\)\\\|&&\|||\||\)' . | 
					
						
							| 
									
										
										
										
											2017-08-11 19:50:37 +02:00
										 |  |  |                  \ '\s*\({\s*\)\=\(#.*\)\=$' | 
					
						
							| 
									
										
										
										
											2018-05-13 00:02:36 +02:00
										 |  |  |   endif | 
					
						
							| 
									
										
										
										
											2010-01-06 20:54:52 +01:00
										 |  |  | 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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-13 00:02:36 +02:00
										 |  |  | function! s:is_function_definition(line) | 
					
						
							|  |  |  |   return a:line =~ '^\s*\<\k\+\>\s*()\s*{' || | 
					
						
							|  |  |  |        \ a:line =~ '^\s*{' || | 
					
						
							|  |  |  |        \ a:line =~ '^\s*function\s*\w\S\+\s*\%(()\)\?\s*{' | 
					
						
							|  |  |  | endfunction | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-06 20:54:52 +01:00
										 |  |  | 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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-02 21:42:23 +02:00
										 |  |  | 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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-06 20:54:52 +01:00
										 |  |  | function! s:is_case_ended(line) | 
					
						
							|  |  |  |   return s:is_case_break(a:line) || a:line =~ ';[;&]\s*\%(#.*\)\=$' | 
					
						
							|  |  |  | endfunction | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-11 18:53:29 +01:00
										 |  |  | 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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-02 21:42:23 +02:00
										 |  |  | function! s:escape(pattern) | 
					
						
							|  |  |  |     return '\V'. escape(a:pattern, '\\') | 
					
						
							|  |  |  | endfunction | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-13 00:02:36 +02:00
										 |  |  | 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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-06-29 22:40:58 +00:00
										 |  |  | let &cpo = s:cpo_save | 
					
						
							|  |  |  | unlet s:cpo_save |