Skip to content

Commit

Permalink
Return LSP 3.17 labelDetails on completion items (#787)
Browse files Browse the repository at this point in the history
* return LSP 3.17 labelDetails on completion items

annotate completions that require alias insertions
deprioretize alias insertion
fix crash when completing one line source file

* Update apps/language_server/lib/language_server/source_file.ex

Co-authored-by: Steve Cohen <[email protected]>

* PR fix

* deprioretize exceptions

Co-authored-by: Steve Cohen <[email protected]>
  • Loading branch information
lukaszsamson and scohen authored Dec 16, 2022
1 parent c01d61b commit 25dc8f0
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 51 deletions.
86 changes: 68 additions & 18 deletions apps/language_server/lib/language_server/providers/completion.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
:filter_text,
# Lower priority is shown higher in the result list
:priority,
:label_details,
:tags,
:command,
{:preselect, false},
Expand Down Expand Up @@ -304,6 +305,7 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
%{
type: :module,
name: name,
full_name: full_name,
summary: summary,
subtype: subtype,
metadata: metadata,
Expand All @@ -317,14 +319,19 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
) do
completion_without_additional_text_edit =
from_completion_item(
%{type: :module, name: name, summary: summary, subtype: subtype, metadata: metadata},
%{
type: :module,
name: name,
full_name: full_name,
summary: summary,
subtype: subtype,
metadata: metadata
},
%{def_before: nil},
options
)

alias_value =
Atom.to_string(required_alias)
|> String.replace_prefix("Elixir.", "")
alias_value = inspect(required_alias)

indentation =
if column_to_insert_alias >= 1,
Expand All @@ -333,17 +340,33 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do

alias_edit = indentation <> "alias " <> alias_value <> "\n"

label_details =
Map.update!(
completion_without_additional_text_edit.label_details,
"description",
&("alias " <> &1)
)

struct(completion_without_additional_text_edit,
additional_text_edit: %TextEdit{
range: range(line_to_insert_alias, 0, line_to_insert_alias, 0),
newText: alias_edit
},
documentation: alias_value <> "\n" <> summary
documentation: alias_value <> "\n" <> summary,
label_details: label_details,
priority: 24
)
end

defp from_completion_item(
%{type: :module, name: name, summary: summary, subtype: subtype, metadata: metadata},
%{
type: :module,
name: name,
full_name: full_name,
summary: summary,
subtype: subtype,
metadata: metadata
},
%{def_before: nil},
_options
) do
Expand All @@ -363,27 +386,38 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
_ -> :module
end

label =
if subtype do
"#{name} (#{subtype})"
else
name
end
label_details = %{
"description" => full_name
}

label_details =
if detail != "module", do: Map.put(label_details, "detail", detail), else: label_details

insert_text =
case name do
":" <> rest -> rest
other -> other
end

priority =
case subtype do
:exception ->
# show exceptions after functions
18

_ ->
14
end

%__MODULE__{
label: label,
label: name,
kind: kind,
detail: detail,
documentation: summary,
insert_text: insert_text,
filter_text: name,
priority: 14,
label_details: label_details,
priority: priority,
tags: metadata_to_tags(metadata)
}
end
Expand Down Expand Up @@ -546,8 +580,15 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
_context,
_options
) do
%{name: name, arity: arity, origin: _origin, doc: doc, signature: signature, spec: spec} =
suggestion
%{
name: name,
arity: arity,
args_list: args_list,
origin: origin,
doc: doc,
signature: signature,
spec: spec
} = suggestion

formatted_spec =
if spec != "" do
Expand All @@ -564,8 +605,12 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
end

%__MODULE__{
label: signature,
label: name,
detail: "typespec #{signature}",
label_details: %{
"detail" => "(#{Enum.join(args_list, ", ")})",
"description" => if(origin, do: "#{origin}.#{name}/#{arity}", else: "#{name}/#{arity}")
},
documentation: "#{doc}#{formatted_spec}",
insert_text: snippet,
priority: 10,
Expand Down Expand Up @@ -952,7 +997,7 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
{name, name}

true ->
label = "#{name}/#{arity}"
label = name

insert_text =
function_snippet(
Expand Down Expand Up @@ -997,6 +1042,10 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
label: label,
kind: :function,
detail: detail,
label_details: %{
"detail" => "(#{Enum.join(args_list, ", ")})",
"description" => "#{origin}.#{name}/#{arity}"
},
documentation: summary <> footer,
insert_text: insert_text,
priority: 17,
Expand Down Expand Up @@ -1044,6 +1093,7 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
"kind" => completion_kind(item.kind),
"detail" => item.detail,
"documentation" => %{"value" => item.documentation || "", kind: "markdown"},
"labelDetails" => item.label_details,
"filterText" => item.filter_text,
"sortText" => String.pad_leading(to_string(idx), 8, "0"),
"insertText" => item.insert_text,
Expand Down
5 changes: 3 additions & 2 deletions apps/language_server/lib/language_server/source_file.ex
Original file line number Diff line number Diff line change
Expand Up @@ -334,10 +334,11 @@ defmodule ElixirLS.LanguageServer.SourceFile do
do: {max(elixir_line - 1, 0), 0}

def elixir_position_to_lsp(urf8_text, {elixir_line, elixir_character}) do
utf16_character =
line =
lines(urf8_text)
|> Enum.at(max(elixir_line - 1, 0))
|> elixir_character_to_lsp(elixir_character)

utf16_character = elixir_character_to_lsp(line || "", elixir_character)

{elixir_line - 1, utf16_character}
end
Expand Down
Loading

0 comments on commit 25dc8f0

Please sign in to comment.