summaryrefslogtreecommitdiffstats
path: root/autoload/fish.vim
diff options
context:
space:
mode:
Diffstat (limited to 'autoload/fish.vim')
-rw-r--r--autoload/fish.vim128
1 files changed, 99 insertions, 29 deletions
diff --git a/autoload/fish.vim b/autoload/fish.vim
index 0ea2e474..92d9f642 100644
--- a/autoload/fish.vim
+++ b/autoload/fish.vim
@@ -2,44 +2,112 @@ if has_key(g:polyglot_is_disabled, 'fish')
finish
endif
+function! s:IsString(lnum, col)
+ " Returns "true" if syntax item at the given position is part of fishString.
+ let l:stack = map(synstack(a:lnum, a:col), 'synIDattr(v:val, "name")')
+ return len(filter(l:stack, 'v:val ==# "fishString"'))
+endfunction
+
+function! s:IsContinuedLine(lnum)
+ " Returns "true" if the given line is a continued line.
+ return getline(a:lnum - 1) =~ '\v\\$'
+endfunction
+
+function! s:FindPrevLnum(lnum)
+ " Starting on the given line, search backwards for a line that is not
+ " empty, not part of a string and not a continued line.
+ if a:lnum < 1 || a:lnum > line('$')
+ " First line or wrong value, follow prevnonblank() behaviour and
+ " return zero.
+ return 0
+ endif
+ let l:lnum = prevnonblank(a:lnum)
+ while l:lnum > 0 && ( s:IsContinuedLine(l:lnum) || s:IsString(l:lnum, 1) )
+ let l:lnum = prevnonblank(l:lnum - 1)
+ endwhile
+ return l:lnum
+endfunction
+
+function! s:IsSwitch(lnum)
+ " Returns "true" if the given line is part of a switch block.
+ let l:lnum = a:lnum
+ let l:line = getline(l:lnum)
+ let l:in_block = 0
+ let l:stop_pat = '\v^\s*%(if|else|while|for|begin)>'
+ let l:block_start_pat = '\v^\s*%(if|while|for|switch|begin)>'
+ while l:lnum > 0
+ let l:lnum = prevnonblank(l:lnum - 1)
+ let l:line = getline(l:lnum)
+ if l:line =~# '\v^\s*end>'
+ let l:in_block += 1
+ elseif l:in_block && l:line =~# l:block_start_pat
+ let l:in_block -= 1
+ elseif !l:in_block && l:line =~# l:stop_pat
+ return 0
+ elseif !l:in_block && l:line =~# '\v^\s*switch>'
+ return 1
+ endif
+ endwhile
+ return 0
+endfunction
+
function! fish#Indent()
- let l:prevlnum = prevnonblank(v:lnum - 1)
- if l:prevlnum ==# 0
+ let l:line = getline(v:lnum)
+ if s:IsString(v:lnum, 1)
+ return indent(v:lnum)
+ endif
+ " shiftwidth can be misleading in recent versions, use shiftwidth() if
+ " it is available.
+ if exists('*shiftwidth')
+ let l:shiftwidth = shiftwidth()
+ else
+ let l:shiftwidth = &shiftwidth
+ endif
+ let l:prevlnum = s:FindPrevLnum(v:lnum - 1)
+ if l:prevlnum == 0
return 0
endif
+ let l:shift = 0
let l:prevline = getline(l:prevlnum)
- let l:line = getline(v:lnum)
- let l:shiftwidth = shiftwidth()
let l:previndent = indent(l:prevlnum)
- let l:indent = l:previndent
- if l:prevline =~# '\v^\s*%(begin|if|else|while|for|function|switch|case)>'
- let l:indent += l:shiftwidth
- endif
- if l:line =~# '\v^\s*end>'
- let l:indent -= l:shiftwidth
- " If we're inside a case, dedent twice because it ends the switch.
- if l:prevline =~# '\v^\s*case>'
- " Previous line starts the case.
- let l:indent -= l:shiftwidth
+ if s:IsContinuedLine(v:lnum)
+ " It is customary to increment indentation of continued lines by three
+ " or a custom value defined by the user if available.
+ let l:previndent = indent(v:lnum - 1)
+ if s:IsContinuedLine(v:lnum - 1)
+ return l:previndent
+ elseif exists('g:fish_indent_cont')
+ return l:previndent + g:fish_indent_cont
+ elseif exists('g:indent_cont')
+ return l:previndent + g:indent_cont
else
- " Scan back to a dedented line to find whether we're in a case.
- let l:i = l:prevlnum
- while l:i >= 1 && indent(l:i) >= l:previndent
- let l:i = prevnonblank(l:i - 1)
- endwhile
- if indent(l:i) < l:previndent && getline(l:i) =~# '\v^\s*case>'
- let l:indent -= l:shiftwidth
- endif
+ return l:previndent + 3
endif
- elseif l:line =~# '\v^\s*else>'
- let l:indent -= l:shiftwidth
- elseif l:prevline !~# '\v^\s*switch>' && l:line =~# '\v^\s*case>'
- let l:indent -= l:shiftwidth
endif
- if l:indent < 0
- return 0
+ if l:prevline =~# '\v^\s*%(begin|if|else|while|for|function|case|switch)>'
+ " First line inside a block, increase by one.
+ let l:shift += 1
+ endif
+ if l:line =~# '\v^\s*%(end|case|else)>'
+ " "end", "case" or "else", decrease by one.
+ let l:shift -= 1
+ endif
+ if l:line =~# '\v^\s*<case>' && l:prevline =~# '\v<switch>'
+ " "case" following "switch", increase by one.
+ let l:shift += 1
+ endif
+ if l:line =~# '\v\s*end>' && s:IsSwitch(v:lnum)
+ " "end" ends switch block, decrease by one more so it matches
+ " the indentation of "switch".
+ let l:shift -= 1
+ endif
+ if l:prevline =~# '\v^\s*%(if|while|for|else|switch|end)>.*<begin>'
+ " "begin" after start of block, increase by one.
+ let l:shift += 1
endif
- return l:indent
+ let l:indent = l:previndent + l:shift * l:shiftwidth
+ " Only return zero or positive numbers.
+ return l:indent < 0 ? 0 : l:indent
endfunction
function! fish#Format()
@@ -49,6 +117,8 @@ function! fish#Format()
let l:command = v:lnum.','.(v:lnum+v:count-1).'!fish_indent'
echo l:command
execute l:command
+ " Fix indentation and replace tabs with spaces if necessary.
+ normal! '[=']
endif
endfunction