11if ! exists (" g:elixir_indent_max_lookbehind" )
22 let g: elixir_indent_max_lookbehind = 30
33endif
4+ let g: elixir_indent_debug = 1
45
56" Return the effective value of 'shiftwidth'
67function ! s: sw ()
@@ -20,6 +21,7 @@ function! elixir#indent#indent(lnum)
2021 call cursor (lnum, 0 )
2122
2223 let handlers = [
24+ \' inside_embedded_view',
2325 \' top_of_file',
2426 \' starts_with_string_continuation',
2527 \' following_trailing_binary_operator',
@@ -65,6 +67,17 @@ function! s:prev_starts_with(context, expr)
6567 return s: _starts_with (a: context .prev_nb_text, a: expr , a: context .prev_nb_lnum)
6668endfunction
6769
70+ function ! s: in_embedded_view ()
71+ let groups = map (synstack (line (' .' ), col (' .' )), " synIDattr(v:val, 'name')" )
72+ for group in [' elixirPhoenixESigil' , ' elixirLiveViewSigil' , ' elixirSurfaceSigil' ]
73+ if index (groups, group) >= 0
74+ return 1
75+ endif
76+ endfor
77+
78+ return 0
79+ endfunction
80+
6881" Returns 0 or 1 based on whether or not the text starts with the given
6982" expression and is not a string or comment
7083function ! s: _starts_with (text, expr , lnum)
@@ -156,6 +169,104 @@ function! s:find_last_pos(lnum, text, match)
156169 return -1
157170endfunction
158171
172+ function ! elixir#indent#handle_inside_embedded_view (context)
173+ if ! s: in_embedded_view ()
174+ return -1
175+ endif
176+
177+ " Multi-line Surface data delimiters
178+ let pair_lnum = searchpair (' {{' , ' ' , ' }}' , ' bW' , " line('.') == " .a: context .lnum." || s:is_string_or_comment(line('.'), col('.'))" , max ([0 , a: context .lnum - g: elixir_indent_max_lookbehind ]))
179+ if pair_lnum
180+ if a: context .text = ~ ' }}$'
181+ return indent (pair_lnum)
182+ elseif a: context .text = ~ ' }}*>$'
183+ return -1
184+ elseif s: prev_ends_with (a: context , ' [\|%{' )
185+ return indent (a: context .prev_nb_lnum) + s: sw ()
186+ elseif a: context .prev_nb_text = ~ ' ,$'
187+ return indent (a: context .prev_nb_lnum)
188+ else
189+ return indent (pair_lnum) + s: sw ()
190+ endif
191+ endif
192+
193+ " Multi-line opening tag -- >, />, or %> are on a different line that their opening <
194+ let pair_lnum = searchpair (' ^\s\+<.*[^>]$' , ' ' , ' ^[^<]*[/%}]\?>$' , ' bW' , " line('.') == " .a: context .lnum." || s:is_string_or_comment(line('.'), col('.'))" , max ([0 , a: context .lnum - g: elixir_indent_max_lookbehind ]))
195+ if pair_lnum
196+ if a: context .text = ~ ' ^\s\+\%\(>\|\/>\|%>\|}}>\)$'
197+ call s: debug (" current line is a lone >, />, or %>" )
198+ return indent (pair_lnum)
199+ elseif a: context .text = ~ ' \%\(>\|\/>\|%>\|}}>\)$'
200+ call s: debug (" current line ends in >, />, or %>" )
201+ if s: prev_ends_with (a: context , ' ,' )
202+ return indent (a: context .prev_nb_lnum)
203+ else
204+ return -1
205+ endif
206+ else
207+ call s: debug (" in the body of a multi-line opening tag" )
208+ return indent (pair_lnum) + s: sw ()
209+ endif
210+ endif
211+
212+ " Special cases
213+ if s: prev_ends_with (a: context , ' ^[^<]*do\s%>' )
214+ call s: debug (" prev line closes a multi-line do block" )
215+ return indent (a: context .prev_nb_lnum)
216+ elseif a: context .prev_nb_text = ~ ' do\s*%>$'
217+ call s: debug (" prev line opens a do block" )
218+ return indent (a: context .prev_nb_lnum) + s: sw ()
219+ elseif a: context .text = ~ ' ^\s\+<\/[a-zA-Z0-9\.\-_]\+>\|<% end %>'
220+ call s: debug (" a single closing tag" )
221+ if a: context .prev_nb_text = ~ ' ^\s\+<[^%\/]*[^/]>.*<\/[a-zA-Z0-9\.\-_]\+>$'
222+ call s: debug (" opening and closing tags are on the same line" )
223+ return indent (a: context .prev_nb_lnum) - s: sw ()
224+ elseif a: context .prev_nb_text = ~ ' ^\s\+<[^%\/]*[^/]>\|\s\+>'
225+ call s: debug (" prev line is opening html tag or single >" )
226+ return indent (a: context .prev_nb_lnum)
227+ elseif s: prev_ends_with (a: context , ' ^[^<]*\%\(do\s\)\@<!%>' )
228+ call s: debug (" prev line closes a multi-line eex tag" )
229+ return indent (a: context .prev_nb_lnum) - 2 * s: sw ()
230+ else
231+ return indent (a: context .prev_nb_lnum) - s: sw ()
232+ endif
233+ elseif a: context .text = ~ ' ^\s*<%\s*\%(end\|else\|catch\|rescue\)\>.*%>'
234+ call s: debug (" eex middle or closing eex tag" )
235+ return indent (a: context .prev_nb_lnum) - s: sw ()
236+ elseif a: context .prev_nb_text = ~ ' \s*<\/\|<% end %>$'
237+ call s: debug (" prev is closing tag" )
238+ return indent (a: context .prev_nb_lnum)
239+ elseif a: context .prev_nb_text = ~ ' ^\s\+<[^%\/]*[^/]>.*<\/[a-zA-Z0-9\.\-_]\+>$'
240+ call s: debug (" opening and closing tags are on the same line" )
241+ return indent (a: context .prev_nb_lnum)
242+ elseif s: prev_ends_with (a: context , ' \s\+\/>' )
243+ call s: debug (" prev ends with a single \> " )
244+ return indent (a: context .prev_nb_lnum)
245+ elseif s: prev_ends_with (a: context , ' ^[^<]*\/>' )
246+ call s: debug (" prev line is closing a multi-line self-closing tag" )
247+ return indent (a: context .prev_nb_lnum) - s: sw ()
248+ elseif s: prev_ends_with (a: context , ' ^<*\/>' )
249+ call s: debug (" prev line is closing self-closing tag" )
250+ return indent (a: context .prev_nb_lnum)
251+ elseif a: context .prev_nb_text = ~ ' ^\s\+%\?>$'
252+ call s: debug (" prev line is a single > or %>" )
253+ return indent (a: context .prev_nb_lnum) + s: sw ()
254+ endif
255+
256+ " Simple HTML (ie, opening tag is not split across lines)
257+ let pair_lnum = searchpair (' ^\s\+<[^%\/].*[^\/>]>$' , ' ' , ' ^\s\+<\/\w\+>$' , ' bW' , " line('.') == " .a: context .lnum." || s:is_string_or_comment(line('.'), col('.'))" , max ([0 , a: context .lnum - g: elixir_indent_max_lookbehind ]))
258+ if pair_lnum
259+ call s: debug (" simple HTML" )
260+ if a: context .text = ~ ' ^\s\+<\/\w\+>$'
261+ return indent (pair_lnum)
262+ else
263+ return indent (pair_lnum) + s: sw ()
264+ endif
265+ endif
266+
267+ return -1
268+ endfunction
269+
159270function ! elixir#indent#handle_top_of_file (context)
160271 if a: context .prev_nb_lnum == 0
161272 return 0
0 commit comments