Skip to content

Commit 9afaff4

Browse files
authored
Move the indentation rules to autoload folder (#217)
- Simplify the `indent/elixir.vim` moving all the indentation rules to `autoload/indent.vim`. The indent script is responsible now for call the rules, not define them. - Rename `GetElixirIndent` to `elixir#indent`. - Use constants with SCREAM_CASE. - Each rule function now receives the current indentation and a line dictionary, which contains the current and last lines number and texts.
1 parent 8679261 commit 9afaff4

File tree

3 files changed

+297
-268
lines changed

3 files changed

+297
-268
lines changed

autoload/elixir/indent.vim

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
let s:NO_COLON_BEFORE = ':\@<!'
2+
let s:NO_COLON_AFTER = ':\@!'
3+
let s:ENDING_SYMBOLS = '\]\|}\|)'
4+
let s:STARTING_SYMBOLS = '\[\|{\|('
5+
let s:ARROW = '->'
6+
let s:END_WITH_ARROW = s:ARROW.'$'
7+
let s:SKIP_SYNTAX = '\%(Comment\|String\)$'
8+
let s:BLOCK_SKIP = "synIDattr(synID(line('.'),col('.'),1),'name') =~? '".s:SKIP_SYNTAX."'"
9+
let s:DEF = '^\s*def'
10+
let s:FN = '\<fn\>'
11+
let s:MULTILINE_FN = s:FN.'\%(.*end\)\@!'
12+
let s:BLOCK_START = '\%(\<do\>\|'.s:FN.'\)\>'
13+
let s:MULTILINE_BLOCK = '\%(\<do\>'.s:NO_COLON_AFTER.'\|'.s:MULTILINE_FN.'\)'
14+
let s:BLOCK_MIDDLE = '\<\%(else\|match\|elsif\|catch\|after\|rescue\)\>'
15+
let s:BLOCK_END = 'end'
16+
let s:STARTS_WITH_PIPELINE = '^\s*|>.*$'
17+
let s:ENDING_WITH_ASSIGNMENT = '=\s*$'
18+
let s:INDENT_KEYWORDS = s:NO_COLON_BEFORE.'\%('.s:MULTILINE_BLOCK.'\|'.s:BLOCK_MIDDLE.'\)'
19+
let s:DEINDENT_KEYWORDS = '^\s*\<\%('.s:BLOCK_END.'\|'.s:BLOCK_MIDDLE.'\)\>'
20+
let s:PAIR_START = '\<\%('.s:NO_COLON_BEFORE.s:BLOCK_START.'\)\>'.s:NO_COLON_AFTER
21+
let s:PAIR_MIDDLE = '^\s*\%('.s:BLOCK_MIDDLE.'\)\>'.s:NO_COLON_AFTER.'\zs'
22+
let s:PAIR_END = '\<\%('.s:NO_COLON_BEFORE.s:BLOCK_END.'\)\>\zs'
23+
24+
function! s:pending_parenthesis(line)
25+
if a:line.last.text !~ s:ARROW
26+
return elixir#util#count_indentable_symbol_diff(a:line, '(', '\%(end\s*\)\@<!)')
27+
end
28+
endfunction
29+
30+
function! s:pending_square_brackets(line)
31+
if a:line.last.text !~ s:ARROW
32+
return elixir#util#count_indentable_symbol_diff(a:line, '[', ']')
33+
end
34+
endfunction
35+
36+
function! s:pending_brackets(line)
37+
if a:line.last.text !~ s:ARROW
38+
return elixir#util#count_indentable_symbol_diff(a:line, '{', '}')
39+
end
40+
endfunction
41+
42+
function! elixir#indent#deindent_case_arrow(ind, line)
43+
if get(b:old_ind, 'arrow', 0) > 0
44+
\ && (a:line.current.text =~ s:ARROW
45+
\ || a:line.current.text =~ s:BLOCK_END)
46+
let ind = b:old_ind.arrow
47+
let b:old_ind.arrow = 0
48+
return ind
49+
else
50+
return a:ind
51+
end
52+
endfunction
53+
54+
function! elixir#indent#deindent_ending_symbols(ind, line)
55+
if a:line.current.text =~ '^\s*\('.s:ENDING_SYMBOLS.'\)'
56+
return a:ind - &sw
57+
else
58+
return a:ind
59+
end
60+
endfunction
61+
62+
function! elixir#indent#deindent_keywords(ind, line)
63+
if a:line.current.text =~ s:DEINDENT_KEYWORDS
64+
let bslnum = searchpair(
65+
\ s:PAIR_START,
66+
\ s:PAIR_MIDDLE,
67+
\ s:PAIR_END,
68+
\ 'nbW',
69+
\ s:BLOCK_SKIP
70+
\ )
71+
72+
return indent(bslnum)
73+
else
74+
return a:ind
75+
end
76+
endfunction
77+
78+
function! elixir#indent#deindent_opened_symbols(ind, line)
79+
let s:opened_symbol =
80+
\ s:pending_parenthesis(a:line)
81+
\ + s:pending_square_brackets(a:line)
82+
\ + s:pending_brackets(a:line)
83+
84+
if s:opened_symbol < 0
85+
let ind = get(b:old_ind, 'symbol', a:ind + (s:opened_symbol * &sw))
86+
let ind = float2nr(ceil(floor(ind)/&sw)*&sw)
87+
return ind <= 0 ? 0 : ind
88+
else
89+
return a:ind
90+
end
91+
endfunction
92+
93+
function! elixir#indent#indent_after_pipeline(ind, line)
94+
if a:line.last.text =~ s:STARTS_WITH_PIPELINE
95+
if empty(substitute(a:line.current.text, ' ', '', 'g'))
96+
\ || a:line.current.text =~ s:STARTS_WITH_PIPELINE
97+
return indent(a:line.last.num)
98+
elseif a:line.last.text !~ s:INDENT_KEYWORDS
99+
let ind = b:old_ind.pipeline
100+
let b:old_ind.pipeline = 0
101+
return ind
102+
end
103+
end
104+
105+
return a:ind
106+
endfunction
107+
108+
function! elixir#indent#indent_assignment(ind, line)
109+
if a:line.last.text =~ s:ENDING_WITH_ASSIGNMENT
110+
let b:old_ind.pipeline = indent(a:line.last.num) " FIXME: side effect
111+
return a:ind + &sw
112+
else
113+
return a:ind
114+
end
115+
endfunction
116+
117+
function! elixir#indent#indent_brackets(ind, line)
118+
if s:pending_brackets(a:line) > 0
119+
return a:ind + &sw
120+
else
121+
return a:ind
122+
end
123+
endfunction
124+
125+
function! elixir#indent#indent_case_arrow(ind, line)
126+
if a:line.last.text =~ s:END_WITH_ARROW && a:line.last.text !~ '\<fn\>'
127+
let b:old_ind.arrow = a:ind
128+
return a:ind + &sw
129+
else
130+
return a:ind
131+
end
132+
endfunction
133+
134+
function! elixir#indent#indent_ending_symbols(ind, line)
135+
if a:line.last.text =~ '^\s*\('.s:ENDING_SYMBOLS.'\)\s*$'
136+
return a:ind + &sw
137+
else
138+
return a:ind
139+
end
140+
endfunction
141+
142+
function! elixir#indent#indent_keywords(ind, line)
143+
if a:line.last.text =~ s:INDENT_KEYWORDS
144+
return a:ind + &sw
145+
else
146+
return a:ind
147+
end
148+
endfunction
149+
150+
function! elixir#indent#indent_parenthesis(ind, line)
151+
if s:pending_parenthesis(a:line) > 0
152+
\ && a:line.last.text !~ s:DEF
153+
\ && a:line.last.text !~ s:END_WITH_ARROW
154+
let b:old_ind.symbol = a:ind
155+
return matchend(a:line.last.text, '(')
156+
else
157+
return a:ind
158+
end
159+
endfunction
160+
161+
function! elixir#indent#indent_pipeline_assignment(ind, line)
162+
if a:line.current.text =~ s:STARTS_WITH_PIPELINE
163+
\ && a:line.last.text =~ '^[^=]\+=.\+$'
164+
let b:old_ind.pipeline = indent(a:line.last.num)
165+
" if line starts with pipeline
166+
" and last line is an attribution
167+
" indents pipeline in same level as attribution
168+
return match(a:line.last.text, '=\s*\zs[^ ]')
169+
else
170+
return a:ind
171+
end
172+
endfunction
173+
174+
function! elixir#indent#indent_pipeline_continuation(ind, line)
175+
if a:line.last.text =~ s:STARTS_WITH_PIPELINE
176+
\ && a:line.current.text =~ s:STARTS_WITH_PIPELINE
177+
return indent(a:line.last.num)
178+
else
179+
return a:ind
180+
end
181+
endfunction
182+
183+
function! elixir#indent#indent_square_brackets(ind, line)
184+
if s:pending_square_brackets(a:line) > 0
185+
if a:line.last.text =~ '[\s*$'
186+
return a:ind + &sw
187+
else
188+
" if start symbol is followed by a character, indent based on the
189+
" whitespace after the symbol, otherwise use the default shiftwidth
190+
" Avoid negative indentation index
191+
return matchend(a:line.last.text, '[\s*')
192+
end
193+
else
194+
return a:ind
195+
end
196+
endfunction
197+
198+
function! elixir#indent#deindent_case_arrow(ind, line)
199+
if get(b:old_ind, 'arrow', 0) > 0
200+
\ && (a:line.current.text =~ s:ARROW
201+
\ || a:line.current.text =~ s:BLOCK_END)
202+
let ind = b:old_ind.arrow
203+
let b:old_ind.arrow = 0
204+
return ind
205+
else
206+
return a:ind
207+
end
208+
endfunction

autoload/elixir/util.vim

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
let s:SKIP_SYNTAX = '\%(Comment\|String\)$'
2+
let s:BLOCK_SKIP = "synIDattr(synID(line('.'),col('.'),1),'name') =~? '".s:SKIP_SYNTAX."'"
3+
4+
function! elixir#util#is_indentable_at(line, col)
5+
" TODO: Remove these 2 lines
6+
" I don't know why, but for the test on spec/indent/lists_spec.rb:24.
7+
" Vim is making some mess on parsing the syntax of 'end', it is being
8+
" recognized as 'elixirString' when should be recognized as 'elixirBlock'.
9+
call synID(a:line, a:col, 1)
10+
" This forces vim to sync the syntax.
11+
syntax sync fromstart
12+
13+
return synIDattr(synID(a:line, a:col, 1), "name")
14+
\ !~ s:SKIP_SYNTAX
15+
endfunction
16+
17+
function! elixir#util#is_indentable_match(line, pattern)
18+
return elixir#util#is_indentable_at(a:line, match(getline(a:line), a:pattern))
19+
endfunction
20+
21+
function! elixir#util#count_indentable_symbol_diff(line, open, close)
22+
if elixir#util#is_indentable_match(a:line.last.num, a:open)
23+
\ && elixir#util#is_indentable_match(a:line.last.num, a:close)
24+
return
25+
\ s:match_count(a:line.last.text, a:open)
26+
\ - s:match_count(a:line.last.text, a:close)
27+
else
28+
return 0
29+
end
30+
endfunction
31+
32+
function! s:match_count(string, pattern)
33+
let size = strlen(a:string)
34+
let index = 0
35+
let counter = 0
36+
37+
while index < size
38+
let index = match(a:string, a:pattern, index)
39+
if index >= 0
40+
let index += 1
41+
let counter +=1
42+
else
43+
break
44+
end
45+
endwhile
46+
47+
return counter
48+
endfunction

0 commit comments

Comments
 (0)