Skip to content

Commit

Permalink
Add alias as additionalTextEdits
Browse files Browse the repository at this point in the history
  • Loading branch information
ajayvigneshk committed Aug 27, 2022
1 parent 68fe3f7 commit f3aebd8
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 12 deletions.
3 changes: 2 additions & 1 deletion apps/elixir_ls_debugger/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ defmodule ElixirLS.Debugger.Mixfile do

defp deps do
[
{:elixir_sense, github: "elixir-lsp/elixir_sense"},
{:elixir_sense,
github: "ajayvigneshk/elixir_sense", branch: "find_elixir_mod_requiring_alias"},
{:elixir_ls_utils, in_umbrella: true},
{:dialyxir, "~> 1.0", runtime: false}
]
Expand Down
120 changes: 111 additions & 9 deletions apps/language_server/lib/language_server/providers/completion.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
with the Language Server Protocol. We also attempt to determine the context based on the line
text before the cursor so we can filter out suggestions that are not relevant.
"""
alias ElixirLS.LanguageServer.Protocol.TextEdit
alias ElixirLS.LanguageServer.SourceFile
import ElixirLS.LanguageServer.Protocol, only: [range: 4]

@enforce_keys [:label, :kind, :insert_text, :priority, :tags]
defstruct [
Expand All @@ -21,7 +23,8 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
:priority,
:tags,
:command,
{:preselect, false}
{:preselect, false},
:additional_text_edit
]

@func_snippets %{
Expand Down Expand Up @@ -102,8 +105,10 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do

# TODO: Don't call into here directly
# Can we use ElixirSense.Providers.Suggestion? ElixirSense.suggestions/3
metadata = ElixirSense.Core.Parser.parse_string(text, true, true, line)

env =
ElixirSense.Core.Parser.parse_string(text, true, true, line)
metadata
|> ElixirSense.Core.Metadata.get_env(line)

scope =
Expand Down Expand Up @@ -139,8 +144,25 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
module: env.module
}

position_to_insert_alias =
ElixirSense.Core.Metadata.get_position_to_insert_alias(metadata, line)

context =
case position_to_insert_alias do
{line_to_insert_alias, column_to_insert_alias} ->
# Don't know why - 1 is needed here
Map.put(
context,
:position_to_insert_alias,
{line_to_insert_alias - 1, column_to_insert_alias - 1}
)

_ ->
Map.put(context, :position_to_insert_alias, {line - 1, 2})
end

items =
ElixirSense.suggestions(text, line, character)
ElixirSense.suggestions(text, line, character, required_alias: true)
|> maybe_reject_derived_functions(context, options)
|> Enum.map(&from_completion_item(&1, context, options))
|> maybe_add_do(context)
Expand Down Expand Up @@ -285,6 +307,79 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
}
end

defp from_completion_item(
%{
type: :module,
name: name,
summary: summary,
subtype: subtype,
metadata: metadata,
required_alias: required_alias
},
%{
def_before: nil,
position_to_insert_alias: {line_to_insert_alias, column_to_insert_alias}
},
_options
) do
detail =
if subtype do
Atom.to_string(subtype)
else
"module"
end

kind =
case subtype do
:behaviour -> :interface
:protocol -> :interface
:exception -> :struct
:struct -> :struct
_ -> :module
end

label =
if subtype do
"#{name} (#{subtype})"
else
name
end

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

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

indentation = 1..column_to_insert_alias |> Enum.map(fn _ -> " " end) |> Enum.join()
alias_edit = indentation <> "alias " <> alias_value <> "\n"

%__MODULE__{
label: label,
kind: kind,
detail: detail,
documentation: alias_value <> "\n" <> summary,
insert_text: insert_text,
filter_text: name,
priority: 14,
tags: metadata_to_tags(metadata),
additional_text_edit: %TextEdit{
range:
range(
line_to_insert_alias,
0,
line_to_insert_alias,
0
),
newText: alias_edit
}
}
end

defp from_completion_item(
%{type: :module, name: name, summary: summary, subtype: subtype, metadata: metadata},
%{def_before: nil},
Expand Down Expand Up @@ -975,20 +1070,27 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
"filterText" => item.filter_text,
"sortText" => String.pad_leading(to_string(idx), 8, "0"),
"insertText" => item.insert_text,
"additionalTextEdits" =>
if item.additional_text_edit do
[item.additional_text_edit]
else
nil
end,
"command" => item.command,
"insertTextFormat" =>
if Keyword.get(options, :snippets_supported, false) do
insert_text_format(:snippet)
else
insert_text_format(:plain_text)
end,
end
}

json = if item.preselect do
Map.put(json, "preselect", true)
else
json
end
json =
if item.preselect do
Map.put(json, "preselect", true)
else
json
end

# deprecated as of Language Server Protocol Specification - 3.15
json =
Expand Down
3 changes: 2 additions & 1 deletion apps/language_server/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ defmodule ElixirLS.LanguageServer.Mixfile do
defp deps do
[
{:elixir_ls_utils, in_umbrella: true},
{:elixir_sense, github: "elixir-lsp/elixir_sense"},
{:elixir_sense,
github: "ajayvigneshk/elixir_sense", branch: "find_elixir_mod_requiring_alias"},
{:erl2ex, github: "dazuma/erl2ex"},
{:dialyxir, "~> 1.0", runtime: false},
{:jason_vendored, github: "elixir-lsp/jason", branch: "vendored"},
Expand Down
42 changes: 42 additions & 0 deletions apps/language_server/test/providers/completion_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,48 @@ defmodule ElixirLS.LanguageServer.Providers.CompletionTest do
end

describe "structs and maps" do
test "suggests full module path as additionalTextEdits" do
text = """
defmodule MyModule do
@moduledoc \"\"\"
This
is a
long
moduledoc
\"\"\"
def dummy_function() do
ExampleS
# ^
end
end
"""

{line, char} = {10, 12}
TestUtils.assert_has_cursor_char(text, line, char)

{:ok, %{"items" => items}} = Completion.completion(text, line, char, @supports)

assert [item] = items

# 22 is struct
assert item["kind"] == 22
assert item["label"] == "ExampleStruct (struct)"

assert [%{newText: " alias ElixirLS.LanguageServer.Fixtures.ExampleStruct\n"}] =
item["additionalTextEdits"]

assert [
%{
range: %{
"end" => %{"character" => 0, "line" => 8},
"start" => %{"character" => 0, "line" => 8}
}
}
] = item["additionalTextEdits"]
end

test "completions of structs are rendered as a struct" do
text = """
defmodule MyModule do
Expand Down
2 changes: 1 addition & 1 deletion mix.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
%{
"dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"},
"docsh": {:hex, :docsh, "0.7.2", "f893d5317a0e14269dd7fe79cf95fb6b9ba23513da0480ec6e77c73221cae4f2", [:rebar3], [{:providers, "1.8.1", [hex: :providers, repo: "hexpm", optional: false]}], "hexpm", "4e7db461bb07540d2bc3d366b8513f0197712d0495bb85744f367d3815076134"},
"elixir_sense": {:git, "https://github.com/elixir-lsp/elixir_sense.git", "192f5a18f18f2d86ff5731af143ea628c00c01c7", []},
"elixir_sense": {:git, "https://github.com/ajayvigneshk/elixir_sense.git", "ac18bb9e1ebb7370907a355c1c7ea85314ed0878", [branch: "find_elixir_mod_requiring_alias"]},
"erl2ex": {:git, "https://github.com/dazuma/erl2ex.git", "244c2d9ed5805ef4855a491d8616b8842fef7ca4", []},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"getopt": {:hex, :getopt, "1.0.1", "c73a9fa687b217f2ff79f68a3b637711bb1936e712b521d8ce466b29cbf7808a", [:rebar3], [], "hexpm", "53e1ab83b9ceb65c9672d3e7a35b8092e9bdc9b3ee80721471a161c10c59959c"},
Expand Down

0 comments on commit f3aebd8

Please sign in to comment.