Skip to content

Commit

Permalink
Infer function arg names from named types in spec
Browse files Browse the repository at this point in the history
  • Loading branch information
zachallaun committed Jul 26, 2024
1 parent 4fad3fc commit c1f3856
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -573,12 +573,19 @@ defmodule Lexical.Server.CodeIntelligence.Completion.Translations.Macro do

snippet =
with {:ok, %Position{} = position} <- Env.prev_significant_position(env),
{:ok, name, arity} <- extract_spec_name_arity(env, position) do
{:ok, name, args} <- extract_spec_name_and_args(env, position) do
args_snippet =
if arity == 0 do
""
else
"(" <> Enum.map_join(1..arity, ", ", &"${#{&1}:arg_#{&1}}") <> ")"
case suggest_arg_names(args) do
[] ->
""

names ->
placeholders =
names
|> Enum.with_index(1)
|> Enum.map_join(", ", fn {name, i} -> "${#{i}:#{name}}" end)

"(" <> placeholders <> ")"
end

"""
Expand All @@ -605,21 +612,28 @@ defmodule Lexical.Server.CodeIntelligence.Completion.Translations.Macro do
|> builder.set_sort_scope(SortScope.global())
end

defp extract_spec_name_arity(%Env{} = env, %Position{} = position) do
defp extract_spec_name_and_args(%Env{} = env, %Position{} = position) do
with {:ok, [maybe_spec | _]} <- Ast.path_at(env.analysis, position),
{:@, _, [{:spec, _, [typespec]}]} <- maybe_spec,
{:"::", _, [{name, _, args}, _return]} <- typespec do
if is_list(args) do
{:ok, name, length(args)}
{:ok, name, args}
else
{:ok, name, 0}
{:ok, name, []}
end
else
_ -> :error
end
end

def suggest_module_name(%Document{} = document) do
defp suggest_arg_names(args) do
Enum.with_index(args, fn
{:"::", _, [{name, _, nil}, _]}, _i when is_atom(name) -> name
_, i -> "arg_#{i + 1}"
end)
end

defp suggest_module_name(%Document{} = document) do
result =
document.path
|> Path.split()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,25 @@ defmodule Lexical.Server.CodeIntelligence.Completion.Translations.MacroTest do
]
end

test "def preceeded by a @spec with named args", %{project: project} do
source = ~q[
@spec my_function(x :: term(), y :: term(), term()) :: term()
def|
]

assert {:ok, completion} =
project
|> complete(source)
|> fetch_completion("def ")

assert apply_completion(completion) == ~q[
@spec my_function(x :: term(), y :: term(), term()) :: term()
def my_function(${1:x}, ${2:y}, ${3:arg_3}) do
$0
end
]
end

test "def preceeded by a @spec without args", %{project: project} do
source = ~q[
@spec my_function :: term()
Expand Down

0 comments on commit c1f3856

Please sign in to comment.