Skip to content

Commit e0573ab

Browse files
authored
fix(completions): work in guards (#475)
1 parent 59e57ce commit e0573ab

File tree

2 files changed

+61
-40
lines changed

2 files changed

+61
-40
lines changed

priv/monkey/_next_ls_private_compiler.ex

+32-40
Original file line numberDiff line numberDiff line change
@@ -1425,39 +1425,36 @@ if Version.match?(System.version(), ">= 1.17.0-dev") do
14251425
end
14261426
end
14271427

1428-
defp expand_macro(_meta, Kernel, type, [{name, _, params}, [{_, block}]], _callback, state, env)
1429-
when type in [:def, :defp] and is_tuple(block) and is_atom(name) and is_list(params) do
1430-
{_, state, penv} =
1431-
for p <- params, reduce: {nil, state, env} do
1432-
{_, state, penv} ->
1433-
expand_pattern(p, state, penv)
1428+
defp expand_macro(_meta, Kernel, type, args, _callback, state, env)
1429+
when type in [:def, :defmacro, :defp, :defmacrop] do
1430+
# extract the name, params, guards, and blocks
1431+
{name, params, guards, blocks} =
1432+
case args do
1433+
[{:when, _, [{name, _, params} | guards]} | maybe_blocks] ->
1434+
{name, params, guards, maybe_blocks}
1435+
1436+
[{name, _, params} | maybe_blocks] ->
1437+
{name, params, [], maybe_blocks}
14341438
end
14351439

1436-
{res, state, _env} = expand(block, state, penv)
1437-
1438-
arity = length(List.wrap(params))
1439-
functions = Map.update(state.functions, env.module, [{name, arity}], &Keyword.put_new(&1, name, arity))
1440-
{res, put_in(state.functions, functions), env}
1441-
end
1442-
1443-
defp expand_macro(_meta, Kernel, type, [{name, _, params}, block], _callback, state, env)
1444-
when type in [:defmacro, :defmacrop] do
1445-
{_res, state, penv} = expand(params, state, env)
1446-
{res, state, _env} = expand(block, state, penv)
1440+
blocks = List.first(blocks, [])
14471441

1448-
arity = length(List.wrap(params))
1449-
macros = Map.update(state.macros, env.module, [{name, arity}], &Keyword.put_new(&1, name, arity))
1450-
{res, put_in(state.macros, macros), env}
1451-
end
1452-
1453-
defp expand_macro(_meta, Kernel, type, [{name, _, params}, blocks], _callback, state, env)
1454-
when type in [:def, :defp] and is_atom(name) and is_list(params) and is_list(blocks) do
1442+
# collect the environment from the parameters
1443+
# parameters are always patterns
14551444
{_, state, penv} =
14561445
for p <- params, reduce: {nil, state, env} do
14571446
{_, state, penv} ->
14581447
expand_pattern(p, state, penv)
14591448
end
14601449

1450+
# expand guards, which includes the env from params
1451+
{_, state, _} =
1452+
for guard <- guards, reduce: {nil, state, penv} do
1453+
{_, state, env} ->
1454+
expand(guard, state, env)
1455+
end
1456+
1457+
# expand the blocks, there could be `:do`, `:after`, `:catch`, etc
14611458
{blocks, state} =
14621459
for {type, block} <- blocks, reduce: {[], state} do
14631460
{acc, state} ->
@@ -1467,26 +1464,21 @@ if Version.match?(System.version(), ">= 1.17.0-dev") do
14671464

14681465
arity = length(List.wrap(params))
14691466

1470-
functions = Map.update(state.functions, env.module, [{name, arity}], &Keyword.put_new(&1, name, arity))
1471-
{Enum.reverse(blocks), put_in(state.functions, functions), env}
1472-
end
1473-
1474-
defp expand_macro(_meta, Kernel, type, [{_name, _, params}, blocks], _callback, state, env)
1475-
when type in [:def, :defp] and is_list(params) and is_list(blocks) do
1476-
{_, state, penv} =
1477-
for p <- params, reduce: {nil, state, env} do
1478-
{_, state, penv} ->
1479-
expand_pattern(p, state, penv)
1467+
# determine which key to save this function in state
1468+
state_key =
1469+
case type do
1470+
type when type in [:def, :defp] -> :functions
1471+
type when type in [:defmacro, :defmacrop] -> :macros
14801472
end
14811473

1482-
{blocks, state} =
1483-
for {type, block} <- blocks, reduce: {[], state} do
1484-
{acc, state} ->
1485-
{res, state, _env} = expand(block, state, penv)
1486-
{[{type, res} | acc], state}
1474+
funcs =
1475+
if is_atom(name) do
1476+
Map.update(state[state_key], env.module, [{name, arity}], &Keyword.put_new(&1, name, arity))
1477+
else
1478+
state[state_key]
14871479
end
14881480

1489-
{Enum.reverse(blocks), state, env}
1481+
{Enum.reverse(blocks), put_in(state[state_key], funcs), env}
14901482
end
14911483

14921484
defp expand_macro(meta, Kernel, :@, [{name, _, p}] = args, callback, state, env) when is_list(p) do

test/next_ls/completions_test.exs

+29
Original file line numberDiff line numberDiff line change
@@ -809,4 +809,33 @@ defmodule NextLS.CompletionsTest do
809809
assert_match %{"kind" => 6, "label" => "items"} in results
810810
assert_match %{"kind" => 6, "label" => "item"} not in results
811811
end
812+
813+
test "parameters are available inside guards", %{client: client, foo: foo} do
814+
uri = uri(foo)
815+
816+
did_open(client, foo, """
817+
defmodule Foo do
818+
def run(items) when is_list(i
819+
end
820+
""")
821+
822+
request client, %{
823+
method: "textDocument/completion",
824+
id: 2,
825+
jsonrpc: "2.0",
826+
params: %{
827+
textDocument: %{
828+
uri: uri
829+
},
830+
position: %{
831+
line: 1,
832+
character: 31
833+
}
834+
}
835+
}
836+
837+
assert_result 2, results
838+
839+
assert_match %{"kind" => 6, "label" => "items"} in results
840+
end
812841
end

0 commit comments

Comments
 (0)