Skip to content

Commit 1c1ff84

Browse files
committed
improve do block indent level discovery algorithm
on 1.14+ use metadata from container cursor on < 1.14 support nested blocks and fn
1 parent 6db6971 commit 1c1ff84

File tree

1 file changed

+61
-9
lines changed

1 file changed

+61
-9
lines changed

apps/language_server/lib/language_server/providers/completion.ex

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,15 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
129129
text_before_cursor = String.slice(line_text, 0, character - 1)
130130
text_after_cursor = String.slice(line_text, (character - 1)..-1//1)
131131

132+
full_text_before_cursor =
133+
if line >= 2 do
134+
Enum.slice(lines, 0..(line - 2))
135+
else
136+
[]
137+
end
138+
|> Kernel.++([text_before_cursor])
139+
|> Enum.join("\n")
140+
132141
prefix = get_prefix(text_before_cursor)
133142

134143
env = ElixirSense.Core.Metadata.get_env(metadata, {line, character})
@@ -156,19 +165,62 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
156165
nil
157166
end
158167

168+
container_cursor_quoted =
169+
case NormalizedCode.Fragment.container_cursor_to_quoted(full_text_before_cursor,
170+
token_metadata: true,
171+
columns: true
172+
) do
173+
{:ok, ast} -> ast
174+
_ -> nil
175+
end
176+
159177
do_block_indent =
160-
lines
161-
|> Enum.slice(0..(line - 1))
162-
|> Enum.reverse()
163-
|> Enum.find_value(0, fn line_text ->
164-
if Regex.match?(~r/(?<=\s|^)do\s*(#.*)?$/u, line_text) do
165-
String.length(line_text) - String.length(String.trim_leading(line_text))
178+
if container_cursor_quoted != nil and Version.match?(System.version(), ">= 1.14.0-dev") do
179+
case Macro.path(container_cursor_quoted, &match?({:__cursor__, _, []}, &1)) do
180+
nil ->
181+
0
182+
183+
[_cursor | rest] ->
184+
Enum.find_value(rest, 0, fn
185+
{_, meta, _} when is_list(meta) ->
186+
if Keyword.has_key?(meta, :do) do
187+
line = Keyword.fetch!(meta, :line)
188+
line_text = Enum.at(lines, line - 1)
189+
String.length(line_text) - String.length(String.trim_leading(line_text))
190+
end
191+
192+
_ ->
193+
nil
194+
end)
166195
end
167-
end)
196+
else
197+
lines
198+
|> Enum.slice(0..(line - 1))
199+
|> Enum.reverse()
200+
|> Enum.reduce_while({0, 0}, fn line_text, {do_count, indent} ->
201+
if Regex.match?(~r/(?<=\s|^)do\s*(#.*)?$/u, line_text) or
202+
Regex.match?(~r/(?<=\s|^)fn\s*.*(->)?(#.*)?$/u, line_text) do
203+
if do_count == 0 do
204+
{:halt,
205+
{0, String.length(line_text) - String.length(String.trim_leading(line_text))}}
206+
else
207+
{:cont, {do_count - 1, indent}}
208+
end
209+
else
210+
if Regex.match?(~r/(?<=\s|^)end[\s\)]*(#.*)?$/u, line_text) do
211+
{:cont, {do_count + 1, indent}}
212+
else
213+
{:cont, {do_count, indent}}
214+
end
215+
end
216+
end)
217+
|> elem(1)
218+
end
168219

169220
line_indent = String.length(line_text) - String.length(String.trim_leading(line_text))
170221

171222
context = %{
223+
container_cursor_to_quoted: container_cursor_quoted,
172224
text_before_cursor: text_before_cursor,
173225
text_after_cursor: text_after_cursor,
174226
prefix: prefix,
@@ -290,7 +342,7 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
290342
"line" => context.line - 1,
291343
"character" =>
292344
context.character - String.length(hint) - 1 -
293-
(context.line_indent - context.do_block_indent)
345+
max(context.line_indent - context.do_block_indent, 0)
294346
},
295347
"end" => %{
296348
"line" => context.line - 1,
@@ -314,7 +366,7 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
314366
"line" => context.line - 1,
315367
"character" =>
316368
context.character - String.length(hint) - 1 -
317-
(context.line_indent - context.do_block_indent)
369+
max(context.line_indent - context.do_block_indent, 0)
318370
},
319371
"end" => %{"line" => context.line - 1, "character" => context.character - 1}
320372
},

0 commit comments

Comments
 (0)