Skip to content

Commit

Permalink
Correctly highlight active param in calls with defaults
Browse files Browse the repository at this point in the history
Override active param on signature level
Partially fixes elixir-lsp/elixir-ls#994
  • Loading branch information
lukaszsamson committed Oct 2, 2023
1 parent ab47f47 commit d2e39a5
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 17 deletions.
1 change: 1 addition & 0 deletions lib/elixir_sense/core/metadata.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ defmodule ElixirSense.Core.Metadata do
moduledoc_positions: %{}

@type signature_t :: %{
optional(:active_param) => :non_neg_integer,
name: String.t(),
params: [String.t()],
spec: String.t(),
Expand Down
31 changes: 22 additions & 9 deletions lib/elixir_sense/providers/signature.ex
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ defmodule ElixirSense.Providers.Signature do
cursor_position
) do
signatures = find_signatures({mod, fun}, npar, kind, env, metadata)

%{active_param: npar, signatures: signatures}
else
_ ->
Expand All @@ -61,16 +62,28 @@ defmodule ElixirSense.Providers.Signature do
end

signatures
|> Enum.filter(fn %{params: params} ->
params_length = length(params)

if params_length == 0 do
npar == 0
else
params_length > npar
end
|> Enum.map(fn
%{params: []} = signature ->
if npar == 0 do
signature
end

%{params: params} = signature ->
defaults =
params
|> Enum.with_index()
|> Enum.map(fn {param, index} -> {Regex.match?(~r/\\\\/, param), index} end)
|> Enum.sort()
|> Enum.at(npar)

case defaults do
nil -> nil
{_, index} when index != npar -> Map.put(signature, :active_param, index)
_ -> signature
end
end)
|> Enum.sort_by(&length(&1.params))
|> Enum.reject(&is_nil/1)
|> Enum.sort_by(&{length(&1.params), -Map.get(&1, :active_param, npar)})
end

defp find_function_signatures({nil, _fun}, _env, _metadata), do: []
Expand Down
98 changes: 90 additions & 8 deletions test/elixir_sense/signature_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ defmodule ElixirSense.SignatureTest do
}
end

test "find signatures when function with many clausess" do
test "find signatures when function with many clauses" do
code = """
defmodule MyModule do
List.starts_with?(
Expand Down Expand Up @@ -539,7 +539,8 @@ defmodule ElixirSense.SignatureTest do
"Glues two documents (`doc1` and `doc2`) inserting the given\nbreak `break_string` between them.",
name: "glue",
params: ["doc1", "break_string \\\\ \" \"", "doc2"],
spec: "@spec glue(t(), binary(), t()) :: t()"
spec: "@spec glue(t(), binary(), t()) :: t()",
active_param: 2
}
]
}
Expand All @@ -560,7 +561,8 @@ defmodule ElixirSense.SignatureTest do
"Glues two documents (`doc1` and `doc2`) inserting the given\nbreak `break_string` between them.",
name: "glue",
params: ["doc1", "break_string \\\\ \" \"", "doc2"],
spec: "@spec glue(t(), binary(), t()) :: t()"
spec: "@spec glue(t(), binary(), t()) :: t()",
active_param: 2
}
]
}
Expand All @@ -582,7 +584,8 @@ defmodule ElixirSense.SignatureTest do
"Glues two documents (`doc1` and `doc2`) inserting the given\nbreak `break_string` between them.",
name: "glue",
params: ["doc1", "break_string \\\\ \" \"", "doc2"],
spec: "@spec glue(t(), binary(), t()) :: t()"
spec: "@spec glue(t(), binary(), t()) :: t()",
active_param: 2
}
]
}
Expand All @@ -605,7 +608,8 @@ defmodule ElixirSense.SignatureTest do
"Glues two documents (`doc1` and `doc2`) inserting the given\nbreak `break_string` between them.",
name: "glue",
params: ["doc1", "break_string \\\\ \" \"", "doc2"],
spec: "@spec glue(t(), binary(), t()) :: t()"
spec: "@spec glue(t(), binary(), t()) :: t()",
active_param: 2
}
]
}
Expand All @@ -627,7 +631,8 @@ defmodule ElixirSense.SignatureTest do
"Glues two documents (`doc1` and `doc2`) inserting the given\nbreak `break_string` between them.",
name: "glue",
params: ["doc1", "break_string \\\\ \" \"", "doc2"],
spec: "@spec glue(t(), binary(), t()) :: t()"
spec: "@spec glue(t(), binary(), t()) :: t()",
active_param: 2
}
]
}
Expand Down Expand Up @@ -780,7 +785,13 @@ defmodule ElixirSense.SignatureTest do
assert ElixirSense.signature(code, 4, 21) == %{
active_param: 1,
signatures: [
%{documentation: "", name: "sum", spec: "", params: ["s \\\\ nil", "f"]},
%{
documentation: "",
name: "sum",
spec: "",
params: ["s \\\\ nil", "f"],
active_param: 0
},
%{documentation: "", name: "sum", spec: "", params: ["arg", "x", "y"]}
]
}
Expand Down Expand Up @@ -810,7 +821,13 @@ defmodule ElixirSense.SignatureTest do
assert ElixirSense.signature(code, 15, 21) == %{
active_param: 1,
signatures: [
%{documentation: "", name: "sum", params: ["s \\\\ nil", "f"], spec: ""},
%{
documentation: "",
name: "sum",
params: ["s \\\\ nil", "f"],
spec: "",
active_param: 0
},
%{documentation: "", name: "sum", params: ["tuple", "x", "y"], spec: ""}
]
}
Expand Down Expand Up @@ -866,6 +883,71 @@ defmodule ElixirSense.SignatureTest do
}
end

test "finds signatures from metadata module functions with default param - correctly highlight active param" do
code = """
defmodule MyModule do
@spec sum(integer, integer, integer, integer, integer, integer) :: integer
defp sum(a \\\\ 1, b \\\\ 1, c, d, e \\\\ 1, f \\\\ 1) do
a + b
end
def run do
sum(1, 2, 3, 4, 5, 6)
end
end
"""

assert ElixirSense.signature(code, 8, 10) == %{
active_param: 0,
signatures: [
%{
name: "sum",
params: [
"a \\\\ 1",
"b \\\\ 1",
"c",
"d",
"e \\\\ 1",
"f \\\\ 1"
],
documentation: "",
spec:
"@spec sum(integer, integer, integer, integer, integer, integer) :: integer",
active_param: 2
}
]
}

assert %{
active_param: 1,
signatures: [%{active_param: 3}]
} = ElixirSense.signature(code, 8, 13)

assert %{
active_param: 2,
signatures: [%{active_param: 0}]
} = ElixirSense.signature(code, 8, 16)

assert %{
active_param: 3,
signatures: [%{active_param: 1}]
} = ElixirSense.signature(code, 8, 19)

assert %{
active_param: 4,
signatures: [signature]
} = ElixirSense.signature(code, 8, 22)

refute Map.has_key?(signature, :active_param)

assert %{
active_param: 5,
signatures: [signature]
} = ElixirSense.signature(code, 8, 25)

refute Map.has_key?(signature, :active_param)
end

test "finds signatures from metadata elixir behaviour call" do
code = """
defmodule MyModule do
Expand Down

0 comments on commit d2e39a5

Please sign in to comment.