Skip to content

Commit 6ffa871

Browse files
authored
Autocompletion: more specific CompletionItemKind (#419)
* Autocompletion: more specific CompletionItemKind Instead of returning module for all types of modules, return "interface" for behaviours and protocols, and "struct" for structs. Also add the subtype to the label * fix server test * Treat exceptions as structs * Fix formatting
1 parent 88ec52e commit 6ffa871

File tree

5 files changed

+117
-8
lines changed

5 files changed

+117
-8
lines changed

apps/language_server/lib/language_server/providers/completion.ex

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,9 +246,25 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
246246
"module"
247247
end
248248

249+
kind =
250+
case subtype do
251+
:behaviour -> :interface
252+
:protocol -> :interface
253+
:exception -> :struct
254+
:struct -> :struct
255+
_ -> :module
256+
end
257+
258+
label =
259+
if subtype do
260+
"#{name} (#{subtype})"
261+
else
262+
name
263+
end
264+
249265
%__MODULE__{
250-
label: name,
251-
kind: :module,
266+
label: label,
267+
kind: kind,
252268
detail: detail,
253269
documentation: summary,
254270
insert_text: name,
@@ -582,8 +598,9 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
582598
text != ""
583599
end
584600

585-
defp completion_kind(type) do
586-
case type do
601+
# LSP CompletionItemKind enumeration
602+
defp completion_kind(kind) do
603+
case kind do
587604
:text -> 1
588605
:method -> 2
589606
:function -> 3

apps/language_server/test/providers/completion_test.exs

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,26 @@ defmodule ElixirLS.LanguageServer.Providers.CompletionTest do
110110
assert length(items) == 0
111111
end
112112

113+
test "completions of protocols are rendered as an interface" do
114+
text = """
115+
defmodule MyModule do
116+
def dummy_function() do
117+
ElixirLS.LanguageServer.Fixtures.ExampleP
118+
# ^
119+
end
120+
end
121+
"""
122+
123+
{line, char} = {2, 45}
124+
TestUtils.assert_has_cursor_char(text, line, char)
125+
126+
{:ok, %{"items" => [item]}} = Completion.completion(text, line, char, @supports)
127+
128+
# 8 is interface
129+
assert item["kind"] == 8
130+
assert item["label"] == "ExampleProtocol (protocol)"
131+
end
132+
113133
test "provides completions for protocol functions" do
114134
text = """
115135
defimpl ElixirLS.LanguageServer.Fixtures.ExampleProtocol, for: MyModule do
@@ -132,6 +152,50 @@ defmodule ElixirLS.LanguageServer.Providers.CompletionTest do
132152
]
133153
end
134154

155+
test "completions of behaviours are rendered as an interface" do
156+
text = """
157+
defmodule MyModule do
158+
def dummy_function() do
159+
ElixirLS.LanguageServer.Fixtures.ExampleB
160+
# ^
161+
end
162+
end
163+
"""
164+
165+
{line, char} = {2, 45}
166+
TestUtils.assert_has_cursor_char(text, line, char)
167+
168+
{:ok, %{"items" => items}} = Completion.completion(text, line, char, @supports)
169+
170+
assert [item, _] = items
171+
172+
# 8 is interface
173+
assert item["kind"] == 8
174+
assert item["label"] == "ExampleBehaviour (behaviour)"
175+
end
176+
177+
test "completions of exceptions are rendered as a struct" do
178+
text = """
179+
defmodule MyModule do
180+
def dummy_function() do
181+
ElixirLS.LanguageServer.Fixtures.ExampleE
182+
# ^
183+
end
184+
end
185+
"""
186+
187+
{line, char} = {2, 45}
188+
TestUtils.assert_has_cursor_char(text, line, char)
189+
190+
{:ok, %{"items" => items}} = Completion.completion(text, line, char, @supports)
191+
192+
assert [item] = items
193+
194+
# 22 is struct
195+
assert item["kind"] == 22
196+
assert item["label"] == "ExampleException (exception)"
197+
end
198+
135199
test "provides completions for callbacks without `def` before" do
136200
text = """
137201
defmodule MyModule do
@@ -199,7 +263,7 @@ defmodule ElixirLS.LanguageServer.Providers.CompletionTest do
199263
|> Enum.filter(&(&1["detail"] =~ "struct"))
200264
|> Enum.map(& &1["label"])
201265

202-
assert "NaiveDateTime" in completions
266+
assert "NaiveDateTime (struct)" in completions
203267

204268
{line, char} = {4, 17}
205269
TestUtils.assert_has_cursor_char(text, line, char)
@@ -221,7 +285,7 @@ defmodule ElixirLS.LanguageServer.Providers.CompletionTest do
221285
|> Enum.filter(&(&1["detail"] =~ "struct"))
222286
|> Enum.map(& &1["label"])
223287

224-
assert "NaiveDateTime" in completions
288+
assert "NaiveDateTime (struct)" in completions
225289
end
226290

227291
describe "deprecated" do
@@ -257,6 +321,28 @@ defmodule ElixirLS.LanguageServer.Providers.CompletionTest do
257321
end
258322

259323
describe "structs and maps" do
324+
test "completions of structs are rendered as a struct" do
325+
text = """
326+
defmodule MyModule do
327+
def dummy_function() do
328+
ElixirLS.LanguageServer.Fixtures.ExampleS
329+
# ^
330+
end
331+
end
332+
"""
333+
334+
{line, char} = {2, 45}
335+
TestUtils.assert_has_cursor_char(text, line, char)
336+
337+
{:ok, %{"items" => items}} = Completion.completion(text, line, char, @supports)
338+
339+
assert [item] = items
340+
341+
# 22 is struct
342+
assert item["kind"] == 22
343+
assert item["label"] == "ExampleStruct (struct)"
344+
end
345+
260346
test "returns struct fields in call syntax" do
261347
text = """
262348
defmodule MyModule do

apps/language_server/test/server_test.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -675,8 +675,8 @@ defmodule ElixirLS.LanguageServer.ServerTest do
675675
%{
676676
"detail" => "behaviour",
677677
"documentation" => _,
678-
"kind" => 9,
679-
"label" => "GenServer"
678+
"kind" => 8,
679+
"label" => "GenServer (behaviour)"
680680
}
681681
| _
682682
]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
defmodule ElixirLS.LanguageServer.Fixtures.ExampleException do
2+
defexception [:message]
3+
end
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
defmodule ElixirLS.LanguageServer.Fixtures.ExampleStruct do
2+
defstruct [:name]
3+
end

0 commit comments

Comments
 (0)