diff options
Diffstat (limited to 'autoload/elixir')
| -rw-r--r-- | autoload/elixir/indent.vim | 212 | ||||
| -rw-r--r-- | autoload/elixir/util.vim | 56 | 
2 files changed, 268 insertions, 0 deletions
| diff --git a/autoload/elixir/indent.vim b/autoload/elixir/indent.vim new file mode 100644 index 00000000..8e0c609c --- /dev/null +++ b/autoload/elixir/indent.vim @@ -0,0 +1,212 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'elixir') == -1 +   +let s:NO_COLON_BEFORE = ':\@<!' +let s:NO_COLON_AFTER = ':\@!' +let s:ENDING_SYMBOLS = '\]\|}\|)' +let s:STARTING_SYMBOLS = '\[\|{\|(' +let s:ARROW = '->' +let s:END_WITH_ARROW = s:ARROW.'$' +let s:SKIP_SYNTAX = '\%(Comment\|String\)$' +let s:BLOCK_SKIP = "synIDattr(synID(line('.'),col('.'),1),'name') =~? '".s:SKIP_SYNTAX."'" +let s:DEF = '^\s*def' +let s:FN = '\<fn\>' +let s:MULTILINE_FN = s:FN.'\%(.*end\)\@!' +let s:BLOCK_START = '\%(\<do\>\|'.s:FN.'\)\>' +let s:MULTILINE_BLOCK = '\%(\<do\>'.s:NO_COLON_AFTER.'\|'.s:MULTILINE_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:MULTILINE_BLOCK.'\|'.s:BLOCK_MIDDLE.'\)' +let s:DEINDENT_KEYWORDS = '^\s*\<\%('.s:BLOCK_END.'\|'.s:BLOCK_MIDDLE.'\)\>' +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:pending_parenthesis(line) +  if a:line.last.text !~ s:ARROW +    return elixir#util#count_indentable_symbol_diff(a:line.last, '(', '\%(end\s*\)\@<!)') +  end +endfunction + +function! s:pending_square_brackets(line) +  if a:line.last.text !~ s:ARROW +    return elixir#util#count_indentable_symbol_diff(a:line.last, '[', ']') +  end +endfunction + +function! s:pending_brackets(line) +  if a:line.last.text !~ s:ARROW +    return elixir#util#count_indentable_symbol_diff(a:line.last, '{', '}') +  end +endfunction + +function! elixir#indent#deindent_case_arrow(ind, line) +  if get(b:old_ind, 'arrow', 0) > 0 +        \ && (a:line.current.text =~ s:ARROW +        \ || a:line.current.text =~ s:BLOCK_END) +    let ind = b:old_ind.arrow +    let b:old_ind.arrow = 0 +    return ind +  else +    return a:ind +  end +endfunction + +function! elixir#indent#deindent_ending_symbols(ind, line) +  if a:line.current.text =~ '^\s*\('.s:ENDING_SYMBOLS.'\)' +    return a:ind - &sw +  else +    return a:ind +  end +endfunction + +function! elixir#indent#deindent_keywords(ind, line) +  if a:line.current.text =~ 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 +endfunction + +function! elixir#indent#deindent_opened_symbols(ind, line) +  let s:opened_symbol = +        \   s:pending_parenthesis(a:line) +        \ + s:pending_square_brackets(a:line) +        \ + s:pending_brackets(a:line) + +  if s:opened_symbol < 0 +    let ind = get(b:old_ind, 'symbol', 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! elixir#indent#indent_after_pipeline(ind, line) +  if a:line.last.text =~ s:STARTS_WITH_PIPELINE +    if empty(substitute(a:line.current.text, ' ', '', 'g')) +          \ || a:line.current.text =~ s:STARTS_WITH_PIPELINE +      return indent(a:line.last.num) +    elseif a:line.last.text !~ s:INDENT_KEYWORDS +      let ind = b:old_ind.pipeline +      let b:old_ind.pipeline = 0 +      return ind +    end +  end + +  return a:ind +endfunction + +function! elixir#indent#indent_assignment(ind, line) +  if a:line.last.text =~ s:ENDING_WITH_ASSIGNMENT +    let b:old_ind.pipeline = indent(a:line.last.num) " FIXME: side effect +    return a:ind + &sw +  else +    return a:ind +  end +endfunction + +function! elixir#indent#indent_brackets(ind, line) +  if s:pending_brackets(a:line) > 0 +    return a:ind + &sw +  else +    return a:ind +  end +endfunction + +function! elixir#indent#indent_case_arrow(ind, line) +  if a:line.last.text =~ s:END_WITH_ARROW && a:line.last.text !~ '\<fn\>' +    let b:old_ind.arrow = a:ind +    return a:ind + &sw +  else +    return a:ind +  end +endfunction + +function! elixir#indent#indent_ending_symbols(ind, line) +  if a:line.last.text =~ '^\s*\('.s:ENDING_SYMBOLS.'\)\s*$' +    return a:ind + &sw +  else +    return a:ind +  end +endfunction + +function! elixir#indent#indent_keywords(ind, line) +  if a:line.last.text =~ s:INDENT_KEYWORDS +    return a:ind + &sw +  else +    return a:ind +  end +endfunction + +function! elixir#indent#indent_parenthesis(ind, line) +  if s:pending_parenthesis(a:line) > 0 +        \ && a:line.last.text !~ s:DEF +        \ && a:line.last.text !~ s:END_WITH_ARROW +    let b:old_ind.symbol = a:ind +    return matchend(a:line.last.text, '(') +  else +    return a:ind +  end +endfunction + +function! elixir#indent#indent_pipeline_assignment(ind, line) +  if a:line.current.text =~ s:STARTS_WITH_PIPELINE +        \ && a:line.last.text =~ '^[^=]\+=.\+$' +    let b:old_ind.pipeline = indent(a:line.last.num) +    " if line starts with pipeline +    " and last line is an attribution +    " indents pipeline in same level as attribution +    return match(a:line.last.text, '=\s*\zs[^ ]') +  else +    return a:ind +  end +endfunction + +function! elixir#indent#indent_pipeline_continuation(ind, line) +  if a:line.last.text =~ s:STARTS_WITH_PIPELINE +        \ && a:line.current.text =~ s:STARTS_WITH_PIPELINE +    return indent(a:line.last.num) +  else +    return a:ind +  end +endfunction + +function! elixir#indent#indent_square_brackets(ind, line) +  if s:pending_square_brackets(a:line) > 0 +    if a:line.last.text =~ '[\s*$' +      return a:ind + &sw +    else +      " 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 +      return matchend(a:line.last.text, '[\s*') +    end +  else +    return a:ind +  end +endfunction + +function! elixir#indent#deindent_case_arrow(ind, line) +  if get(b:old_ind, 'arrow', 0) > 0 +        \ && (a:line.current.text =~ s:ARROW +        \ || a:line.current.text =~ s:BLOCK_END) +    let ind = b:old_ind.arrow +    let b:old_ind.arrow = 0 +    return ind +  else +    return a:ind +  end +endfunction + +endif diff --git a/autoload/elixir/util.vim b/autoload/elixir/util.vim new file mode 100644 index 00000000..3139d779 --- /dev/null +++ b/autoload/elixir/util.vim @@ -0,0 +1,56 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'elixir') == -1 +   +let s:SKIP_SYNTAX = '\%(Comment\|String\)$' +let s:BLOCK_SKIP = "synIDattr(synID(line('.'),col('.'),1),'name') =~? '".s:SKIP_SYNTAX."'" + +function! elixir#util#is_indentable_at(line, col) +  if a:col == -1 " skip synID lookup for not found match +    return 1 +  end +  " 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(a:line, a:col, 1) +  " This forces vim to sync the syntax. Using fromstart is very slow on files +  " over 1k lines +  syntax sync minlines=20 maxlines=150 + +  return synIDattr(synID(a:line, a:col, 1), "name") +        \ !~ s:SKIP_SYNTAX +endfunction + +function! elixir#util#is_indentable_match(line, pattern) +  return elixir#util#is_indentable_at(a:line.num, match(a:line.text, a:pattern)) +endfunction + +function! elixir#util#count_indentable_symbol_diff(line, open, close) +  if elixir#util#is_indentable_match(a:line, a:open) +        \ && elixir#util#is_indentable_match(a:line, a:close) +    return +          \   s:match_count(a:line.text, a:open) +          \ - s:match_count(a:line.text, a:close) +  else +    return 0 +  end +endfunction + +function! s:match_count(string, pattern) +  let size = strlen(a:string) +  let index = 0 +  let counter = 0 + +  while index < size +    let index = match(a:string, a:pattern, index) +    if index >= 0 +      let index += 1 +      let counter +=1 +    else +      break +    end +  endwhile + +  return counter +endfunction + +endif | 
