diff options
author | Adam Stankiewicz <sheerun@sher.pl> | 2016-12-20 20:57:20 +0100 |
---|---|---|
committer | Adam Stankiewicz <sheerun@sher.pl> | 2016-12-20 20:57:20 +0100 |
commit | e404a658b1647fad396a954776eda0bdabf8353c (patch) | |
tree | fcdab0e324fd72015ba656e43bd8f8c243030c14 /indent/elixir.vim | |
parent | 74652b465d7eff97070001317a4ea5557717378d (diff) | |
download | vim-polyglot-e404a658b1647fad396a954776eda0bdabf8353c.tar.gz vim-polyglot-e404a658b1647fad396a954776eda0bdabf8353c.zip |
Update
Diffstat (limited to 'indent/elixir.vim')
-rw-r--r-- | indent/elixir.vim | 224 |
1 files changed, 48 insertions, 176 deletions
diff --git a/indent/elixir.vim b/indent/elixir.vim index 3e7b6de0..c5ce4f40 100644 --- a/indent/elixir.vim +++ b/indent/elixir.vim @@ -1,199 +1,71 @@ if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'elixir') == -1 -if exists("b:did_indent") - finish -end -let b:did_indent = 1 - setlocal nosmartindent +setlocal indentexpr=elixir#indent() +setlocal indentkeys+=0),0],0=\|>,=-> +setlocal indentkeys+=0=end,0=else,0=match,0=elsif,0=catch,0=after,0=rescue -setlocal indentexpr=GetElixirIndent() -setlocal indentkeys+=0),0],0=end,0=else,0=match,0=elsif,0=catch,0=after,0=rescue,0=\|> - -if exists("*GetElixirIndent") +if exists("b:did_indent") || exists("*elixir#indent") finish end +let b:did_indent = 1 let s:cpo_save = &cpo set cpo&vim -let s:no_colon_before = ':\@<!' -let s:no_colon_after = ':\@!' -let s:symbols_end = '\]\|}\|)' -let s:symbols_start = '\[\|{\|(' -let s:arrow = '^.*->$' -let s:skip_syntax = '\%(Comment\|String\)$' -let s:block_skip = "synIDattr(synID(line('.'),col('.'),1),'name') =~? '".s:skip_syntax."'" -let s:block_start = '\<\%(do\|fn\)\>' -let s:block_middle = 'else\|match\|elsif\|catch\|after\|rescue' -let s:block_end = 'end' -let s:starts_with_pipeline = '^\s*|>.*$' -let s:ending_with_assignment = '=\s*$' - -let s:indent_keywords = '\<'.s:no_colon_before.'\%('.s:block_start.'\|'.s:block_middle.'\)$'.'\|'.s:arrow -let s:deindent_keywords = '^\s*\<\%('.s:block_end.'\|'.s:block_middle.'\)\>'.'\|'.s:arrow - -let s:pair_start = '\<\%('.s:no_colon_before.s:block_start.'\)\>'.s:no_colon_after -let s:pair_middle = '^\s*\%('.s:block_middle.'\)\>'.s:no_colon_after.'\zs' -let s:pair_end = '\<\%('.s:no_colon_before.s:block_end.'\)\>\zs' - -function! s:is_indentable_syntax() - " TODO: Remove these 2 lines - " I don't know why, but for the test on spec/indent/lists_spec.rb:24. - " Vim is making some mess on parsing the syntax of 'end', it is being - " recognized as 'elixirString' when should be recognized as 'elixirBlock'. - call synID(s:current_line_ref, 1, 1) - " This forces vim to sync the syntax. - syntax sync fromstart - - return synIDattr(synID(s:current_line_ref, 1, 1), "name") - \ !~ s:skip_syntax -endfunction - -function! s:indent_opened_symbol(ind) - if s:opened_symbol > 0 - if s:pending_parenthesis > 0 - \ && s:last_line !~ '^\s*def' - \ && s:last_line !~ s:arrow - let b:old_ind = a:ind - return matchend(s:last_line, '(') - " if start symbol is followed by a character, indent based on the - " whitespace after the symbol, otherwise use the default shiftwidth - " Avoid negative indentation index - elseif s:last_line =~ '\('.s:symbols_start.'\).' - let regex = '\('.s:symbols_start.'\)\s*' - let opened_prefix = matchlist(s:last_line, regex)[0] - return a:ind + (s:opened_symbol * strlen(opened_prefix)) - else - return a:ind + (s:opened_symbol * &sw) - end - elseif s:opened_symbol < 0 - let ind = get(b:, 'old_ind', a:ind + (s:opened_symbol * &sw)) - let ind = float2nr(ceil(floor(ind)/&sw)*&sw) - return ind <= 0 ? 0 : ind - else - return a:ind - end -endfunction - -function! s:indent_last_line_end_symbol_or_indent_keyword(ind) - if s:last_line =~ '^\s*\('.s:symbols_end.'\)' - \ || s:last_line =~ s:indent_keywords - return a:ind + &sw - else - return a:ind - end -endfunction - -function! s:indent_symbols_ending(ind) - if s:current_line =~ '^\s*\('.s:symbols_end.'\)' - return a:ind - &sw - else - return a:ind - end -endfunction - -function! s:indent_assignment(ind) - if s:last_line =~ s:ending_with_assignment - let b:old_ind = indent(s:last_line_ref) " FIXME: side effect - return a:ind + &sw - else - return a:ind - end -endfunction +function! elixir#indent() + " initiates the `old_ind` dictionary + let b:old_ind = get(b:, 'old_ind', {}) + " initialtes the `line` dictionary + let line = s:build_line(v:lnum) -function! s:indent_pipeline(ind) - if s:last_line =~ s:starts_with_pipeline - \ && s:current_line =~ s:starts_with_pipeline - indent(s:last_line_ref) - elseif s:current_line =~ s:starts_with_pipeline - \ && s:last_line =~ '^[^=]\+=.\+$' - let b:old_ind = indent(s:last_line_ref) - " if line starts with pipeline - " and last line is an attribution - " indents pipeline in same level as attribution - return match(s:last_line, '=\s*\zs[^ ]') + if s:is_beginning_of_file(line) + " Reset `old_ind` dictionary at the beginning of the file + let b:old_ind = {} + " At the start of the file use zero indent. + return 0 + elseif !s:is_indentable_line(line) + " Keep last line indentation if the current line does not have an + " indentable syntax + return indent(line.last.num) else - return a:ind + " Calculates the indenation level based on the rules + " All the rules are defined in `autoload/indent.vim` + let ind = indent(line.last.num) + let ind = elixir#indent#deindent_case_arrow(ind, line) + let ind = elixir#indent#indent_parenthesis(ind, line) + let ind = elixir#indent#indent_square_brackets(ind, line) + let ind = elixir#indent#indent_brackets(ind, line) + let ind = elixir#indent#deindent_opened_symbols(ind, line) + let ind = elixir#indent#indent_pipeline_assignment(ind, line) + let ind = elixir#indent#indent_pipeline_continuation(ind, line) + let ind = elixir#indent#indent_after_pipeline(ind, line) + let ind = elixir#indent#indent_assignment(ind, line) + let ind = elixir#indent#indent_ending_symbols(ind, line) + let ind = elixir#indent#indent_keywords(ind, line) + let ind = elixir#indent#deindent_keywords(ind, line) + let ind = elixir#indent#deindent_ending_symbols(ind, line) + let ind = elixir#indent#indent_case_arrow(ind, line) + return ind end endfunction -function! s:indent_after_pipeline(ind) - if s:last_line =~ s:starts_with_pipeline - if empty(substitute(s:current_line, ' ', '', 'g')) - \ || s:current_line =~ s:starts_with_pipeline - return indent(s:last_line_ref) - elseif s:last_line !~ s:indent_keywords - return b:old_ind - else - return a:ind - end - else - return a:ind - end +function! s:is_beginning_of_file(line) + return a:line.last.num == 0 endfunction -function! s:deindent_keyword(ind) - if s:current_line =~ s:deindent_keywords - let bslnum = searchpair( - \ s:pair_start, - \ s:pair_middle, - \ s:pair_end, - \ 'nbW', - \ s:block_skip - \ ) - - return indent(bslnum) - else - return a:ind - end +function! s:is_indentable_line(line) + return elixir#util#is_indentable_at(a:line.current.num, 1) endfunction -function! s:indent_arrow(ind) - if s:current_line =~ s:arrow - " indent case statements '->' - return a:ind + &sw - else - return a:ind - end -endfunction - -function! GetElixirIndent() - let s:current_line_ref = v:lnum - let s:last_line_ref = prevnonblank(s:current_line_ref - 1) - let s:current_line = getline(s:current_line_ref) - let s:last_line = getline(s:last_line_ref) - let s:pending_parenthesis = 0 - let s:opened_symbol = 0 - - if s:last_line !~ s:arrow - let splitted_line = split(s:last_line, '\zs') - let s:pending_parenthesis = - \ + count(splitted_line, '(') - count(splitted_line, ')') - let s:opened_symbol = - \ + s:pending_parenthesis - \ + count(splitted_line, '[') - count(splitted_line, ']') - \ + count(splitted_line, '{') - count(splitted_line, '}') - end +function! s:build_line(line) + let line = { 'current': {}, 'last': {} } + let line.current.num = a:line + let line.current.text = getline(line.current.num) + let line.last.num = prevnonblank(line.current.num - 1) + let line.last.text = getline(line.last.num) - if s:last_line_ref == 0 - " At the start of the file use zero indent. - return 0 - elseif !s:is_indentable_syntax() - " Current syntax is not indentable, keep last line indentation - return indent(s:last_line_ref) - else - let ind = indent(s:last_line_ref) - let ind = s:indent_opened_symbol(ind) - let ind = s:indent_symbols_ending(ind) - let ind = s:indent_pipeline(ind) - let ind = s:indent_after_pipeline(ind) - let ind = s:indent_assignment(ind) - let ind = s:indent_last_line_end_symbol_or_indent_keyword(ind) - let ind = s:deindent_keyword(ind) - let ind = s:indent_arrow(ind) - return ind - end + return line endfunction let &cpo = s:cpo_save |