Skip to content

Commit b3bf75b

Browse files
authored
fix(completions): correctly accumulate variables in <- expressions (#424)
1 parent 04d3010 commit b3bf75b

File tree

4 files changed

+128
-7
lines changed

4 files changed

+128
-7
lines changed

Diff for: lib/next_ls/helpers/ast_helpers.ex

+1-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ defmodule NextLS.ASTHelpers do
160160
|> Zipper.zip()
161161
|> Zipper.find(fn
162162
{:@, _, [{:__cursor__, _, []}]} -> true
163-
{:__cursor__, _, []} -> true
163+
{:__cursor__, _, _} -> true
164164
{{:., _, [_, :__cursor__]}, _, _} -> true
165165
_ -> false
166166
end) do

Diff for: lib/next_ls/helpers/ast_helpers/env.ex

+14-6
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ defmodule NextLS.ASTHelpers.Env do
33
alias Sourceror.Zipper
44

55
defp inside?(range, position) do
6-
Sourceror.compare_positions(range.start, position) == :lt && Sourceror.compare_positions(range.end, position) == :gt
6+
Sourceror.compare_positions(range.start, position) in [:lt, :eq] &&
7+
Sourceror.compare_positions(range.end, position) in [:gt, :eq]
78
end
89

910
def build(nil) do
@@ -40,21 +41,28 @@ defmodule NextLS.ASTHelpers.Env do
4041

4142
Map.update!(acc, :variables, &(vars ++ &1))
4243

43-
{match_op, _, [pm | _]} when match_op in [:<-] ->
44+
{match_op, _, [pm | rhs]} when match_op in [:<-] ->
4445
up_node = zipper |> Zipper.up() |> Zipper.node()
4546

4647
# in_match operator comes with for and with normally, so we need to
4748
# check if we are inside the parent node, which is the for/with
48-
is_inside =
49-
with {_, _, _} <- up_node,
50-
range when not is_nil(range) <- Sourceror.get_range(up_node) do
49+
is_inside_p =
50+
with {_, _, _} <- up_node, range when not is_nil(range) <- Sourceror.get_range(up_node) do
5151
inside?(range, position)
5252
else
5353
_ ->
5454
false
5555
end
5656

57-
if is_inside do
57+
is_inside_rhs =
58+
with range when not is_nil(range) <- Sourceror.get_range(rhs) do
59+
inside?(range, position)
60+
else
61+
_ ->
62+
false
63+
end
64+
65+
if is_inside_p and not is_inside_rhs do
5866
{_, vars} =
5967
Macro.prewalk(pm, [], fn node, acc ->
6068
case node do

Diff for: test/next_ls/completions_test.exs

+69
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,20 @@ defmodule NextLS.CompletionsTest do
1919
end
2020
end
2121

22+
defmacrop assert_match({:not, _, [{:in, _, [left, right]}]}) do
23+
quote do
24+
refute Enum.any?(unquote(right), fn x ->
25+
match?(unquote(left), x)
26+
end),
27+
"""
28+
found a match inside of list, expected none
29+
30+
left: #{unquote(Macro.to_string(left))}
31+
right: #{inspect(unquote(right), pretty: true)}
32+
"""
33+
end
34+
end
35+
2236
@moduletag tmp_dir: true, root_paths: ["my_proj"]
2337

2438
setup %{tmp_dir: tmp_dir} do
@@ -482,4 +496,59 @@ defmodule NextLS.CompletionsTest do
482496
%{"data" => _, "documentation" => _, "insertText" => "capture_log", "kind" => 3, "label" => "capture_log/2"} in results
483497
)
484498
end
499+
500+
test "completions inside generator rhs", %{client: client, foo: foo} do
501+
uri = uri(foo)
502+
503+
did_open(client, foo, """
504+
defmodule Foo do
505+
def run() do
506+
var = "hi"
507+
508+
for thing <- v do
509+
end
510+
511+
end
512+
end
513+
""")
514+
515+
request client, %{
516+
method: "textDocument/completion",
517+
id: 2,
518+
jsonrpc: "2.0",
519+
params: %{
520+
textDocument: %{
521+
uri: uri
522+
},
523+
position: %{
524+
line: 4,
525+
character: 18
526+
}
527+
}
528+
}
529+
530+
assert_result 2, [
531+
%{
532+
"data" => nil,
533+
"documentation" => "",
534+
"insertText" => "var",
535+
"kind" => 6,
536+
"label" => "var"
537+
},
538+
%{
539+
"data" => nil,
540+
"documentation" => _,
541+
"insertText" => "var!",
542+
"kind" => 3,
543+
"label" => "var!/1"
544+
},
545+
%{
546+
"data" => nil,
547+
"documentation" => _,
548+
"insertText" => "var!",
549+
"kind" => 3,
550+
"label" => "var!/2"
551+
}
552+
]
553+
end
485554
end

Diff for: test/next_ls/helpers/ast_helpers/env_test.exs

+44
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,50 @@ defmodule NextLS.ASTHelpers.EnvTest do
156156

157157
assert actual.variables == ["entries"]
158158
end
159+
160+
test "comprehension lhs of generator do not leak into rhs " do
161+
code = """
162+
defmodule Foo do
163+
def one(entries) do
164+
for entry <- entries,
165+
not_me <- __cursor__() do
166+
:ok
167+
end
168+
end
169+
170+
def two do
171+
baz = :bar
172+
end
173+
end
174+
"""
175+
176+
actual = run(code)
177+
178+
assert actual.variables == ["entries", "entry"]
179+
end
180+
181+
test "multiple generators and filters in comprehension" do
182+
code = """
183+
defmodule Foo do
184+
def one(entries) do
185+
for entry <- entries,
186+
foo = do_something(),
187+
bar <- foo do
188+
__cursor__()
189+
:ok
190+
end
191+
end
192+
193+
def two do
194+
baz = :bar
195+
end
196+
end
197+
"""
198+
199+
actual = run(code)
200+
201+
assert actual.variables == ["entries", "entry", "foo", "bar"]
202+
end
159203
end
160204

161205
defp run(code) do

0 commit comments

Comments
 (0)