Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -592,11 +592,14 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
capture_before? = context.capture_before?

for s <- suggestions do
# suggestions from third party plugins may not set optional keys like default_args
default_args = Map.get(s, :default_args) || 0

default_arg_variants =
if s.type in [:function, :macro] and s.default_args > 0 do
if s.type in [:function, :macro] and default_args > 0 do
max_arity_name = function_name_with_parens?(s.name, s.arity, locals_without_parens)

for i <- s.default_args..1//-1,
for i <- default_args..1//-1,
capture_before? or !signature_help_supported or
max_arity_name !=
function_name_with_parens?(s.name, s.arity - i, locals_without_parens) do
Expand Down
18 changes: 18 additions & 0 deletions apps/language_server/test/providers/completion_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,24 @@ defmodule ElixirLS.LanguageServer.Providers.CompletionTest do
assert first_item.preselect == true
end

test "plugin function suggestion without default_args does not crash completion" do
text = """
defmodule MyModule do
def fun do
legacy_plugin_fun
end
end
"""

{line, char} = {3, 22}
parser_context = ParserContextBuilder.from_string(text, {line, char})

{:ok, %GenLSP.Structures.CompletionList{items: items}} =
Completion.completion(parser_context, line, char, @supports)

assert Enum.any?(items, &(&1.label == "legacy_plugin_fun"))
end

test "keyword param option text edit uses 0-based LSP positions" do
text = """
defmodule MyModule do
Expand Down
30 changes: 30 additions & 0 deletions apps/language_server/test/support/plugins/legacy_format_plugin.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
defmodule ElixirLS.LanguageServer.Test.LegacyFormatPlugin do
@moduledoc false
# Emits a function suggestion in the shape of older third party plugins
# (no default_args key). Only reacts to the hint `legacy_plugin_fun`.
@behaviour ElixirLS.LanguageServer.Plugin

@impl true
def reduce("legacy_plugin_fun", _env, _buffer_metadata, _cursor_context, acc) do
suggestion = %{
type: :function,
name: "legacy_plugin_fun",
arity: 1,
def_arity: 1,
args: "value",
args_list: ["value"],
origin: "Legacy.Plugin.Builtins",
spec: "@spec legacy_plugin_fun(value :: atom()) :: atom()",
metadata: %{},
snippet: nil,
summary: "A legacy plugin function",
needed_import: nil,
needed_require: nil,
visibility: :public
}

{:cont, %{acc | result: acc.result ++ [suggestion]}}
end

def reduce(_hint, _env, _buffer_metadata, _cursor_context, acc), do: {:cont, acc}
end
Loading