Skip to content

Commit 9637725

Browse files
authored
Add support for deprecated and completion tags in complete provider (#180)
* Add support for deprecated and tags in complete provider update elixir sense * PR suggestions applied * tests added * option name fixed
1 parent 1533d0a commit 9637725

File tree

6 files changed

+160
-39
lines changed

6 files changed

+160
-39
lines changed

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

Lines changed: 79 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
99
"""
1010
alias ElixirLS.LanguageServer.SourceFile
1111

12-
@enforce_keys [:label, :kind, :insert_text, :priority]
13-
defstruct [:label, :kind, :detail, :documentation, :insert_text, :filter_text, :priority]
12+
@enforce_keys [:label, :kind, :insert_text, :priority, :tags]
13+
defstruct [:label, :kind, :detail, :documentation, :insert_text, :filter_text, :priority, :tags]
1414

1515
@module_attr_snippets [
1616
{"doc", "doc \"\"\"\n$0\n\"\"\"", "Documents a function"},
@@ -84,7 +84,7 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
8484
[".", "@"]
8585
end
8686

87-
def completion(text, line, character, snippets_supported) do
87+
def completion(text, line, character, options) do
8888
line_text =
8989
text
9090
|> SourceFile.lines()
@@ -144,7 +144,7 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
144144
|> Enum.reject(&is_nil/1)
145145
|> Enum.uniq_by(& &1.insert_text)
146146
|> sort_items()
147-
|> items_to_json(snippets_supported)
147+
|> items_to_json(options)
148148

149149
{:ok, %{"isIncomplete" => false, "items" => items_json}}
150150
end
@@ -169,7 +169,8 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
169169
detail: "module attribute",
170170
insert_text: insert_text,
171171
filter_text: name_only,
172-
priority: 3
172+
priority: 3,
173+
tags: []
173174
}
174175
end
175176
end
@@ -184,7 +185,8 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
184185
kind: :variable,
185186
detail: "variable",
186187
insert_text: name,
187-
priority: 3
188+
priority: 3,
189+
tags: []
188190
}
189191
end
190192

@@ -200,14 +202,18 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
200202
detail: "return value",
201203
documentation: spec,
202204
insert_text: snippet,
203-
priority: 5
205+
priority: 5,
206+
tags: []
204207
}
205208
end
206209

207-
defp from_completion_item(%{type: :module, name: name, summary: summary, subtype: subtype}, %{
208-
def_before: nil,
209-
prefix: prefix
210-
}) do
210+
defp from_completion_item(
211+
%{type: :module, name: name, summary: summary, subtype: subtype, metadata: metadata},
212+
%{
213+
def_before: nil,
214+
prefix: prefix
215+
}
216+
) do
211217
capitalized? = String.first(name) == String.upcase(String.first(name))
212218

213219
if String.ends_with?(prefix, ":") and capitalized? do
@@ -229,7 +235,8 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
229235
documentation: summary,
230236
insert_text: name,
231237
filter_text: name,
232-
priority: 4
238+
priority: 4,
239+
tags: metadata_to_tags(metadata)
233240
}
234241
end
235242
end
@@ -242,7 +249,8 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
242249
name: name,
243250
summary: summary,
244251
arity: arity,
245-
origin: origin
252+
origin: origin,
253+
metadata: metadata
246254
},
247255
context
248256
) do
@@ -269,7 +277,8 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
269277
documentation: summary,
270278
insert_text: full_snippet,
271279
priority: 2,
272-
filter_text: name
280+
filter_text: name,
281+
tags: metadata_to_tags(metadata)
273282
}
274283
end
275284
end
@@ -282,7 +291,8 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
282291
name: name,
283292
summary: summary,
284293
arity: arity,
285-
origin: origin
294+
origin: origin,
295+
metadata: metadata
286296
},
287297
context
288298
) do
@@ -298,7 +308,8 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
298308
documentation: summary,
299309
insert_text: full_snippet,
300310
priority: 2,
301-
filter_text: name
311+
filter_text: name,
312+
tags: metadata_to_tags(metadata)
302313
}
303314
end
304315

@@ -308,7 +319,8 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
308319
detail: "#{origin} struct field",
309320
insert_text: "#{name}: ",
310321
priority: 0,
311-
kind: :field
322+
kind: :field,
323+
tags: []
312324
}
313325
end
314326

@@ -329,11 +341,12 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
329341
documentation: "#{doc}#{formatted_spec}",
330342
insert_text: "#{name}: ",
331343
priority: 0,
332-
kind: :field
344+
kind: :field,
345+
tags: []
333346
}
334347
end
335348

336-
defp from_completion_item(%{type: :type_spec} = suggestion, _context) do
349+
defp from_completion_item(%{type: :type_spec, metadata: metadata} = suggestion, _context) do
337350
%{name: name, arity: arity, origin: _origin, doc: doc, signature: signature, spec: spec} =
338351
suggestion
339352

@@ -357,7 +370,8 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
357370
documentation: "#{doc}#{formatted_spec}",
358371
insert_text: snippet,
359372
priority: 0,
360-
kind: :class
373+
kind: :class,
374+
tags: metadata_to_tags(metadata)
361375
}
362376
end
363377

@@ -485,6 +499,7 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
485499
detail: "module attribute",
486500
insert_text: snippet,
487501
filter_text: name,
502+
tags: [],
488503
priority: 6
489504
}
490505
end
@@ -503,6 +518,7 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
503518
kind: :keyword,
504519
detail: "keyword",
505520
insert_text: snippet,
521+
tags: [],
506522
priority: 1
507523
}
508524
end)
@@ -516,7 +532,8 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
516532
summary: summary,
517533
arity: arity,
518534
spec: spec,
519-
origin: origin
535+
origin: origin,
536+
metadata: metadata
520537
} = info
521538

522539
# ElixirSense now returns types as an atom
@@ -571,7 +588,8 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
571588
detail: detail,
572589
documentation: summary,
573590
insert_text: snippet,
574-
priority: 7
591+
priority: 7,
592+
tags: metadata_to_tags(metadata)
575593
}
576594
end
577595

@@ -587,18 +605,20 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
587605
end)
588606
end
589607

590-
defp items_to_json(items, snippets_supported) do
608+
defp items_to_json(items, options) do
609+
snippets_supported = Keyword.get(options, :snippets_supported, false)
610+
591611
items =
592612
Enum.reject(items, fn item ->
593-
!snippets_supported && snippet?(item)
613+
not snippets_supported and snippet?(item)
594614
end)
595615

596616
for {item, idx} <- Enum.with_index(items) do
597-
item_to_json(item, idx, snippets_supported)
617+
item_to_json(item, idx, options)
598618
end
599619
end
600620

601-
defp item_to_json(item, idx, snippets_supported) do
621+
defp item_to_json(item, idx, options) do
602622
json = %{
603623
"label" => item.label,
604624
"kind" => completion_kind(item.kind),
@@ -608,17 +628,49 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
608628
"sortText" => String.pad_leading(to_string(idx), 8, "0"),
609629
"insertText" => item.insert_text,
610630
"insertTextFormat" =>
611-
if snippets_supported do
631+
if Keyword.get(options, :snippets_supported, false) do
612632
insert_text_format(:snippet)
613633
else
614634
insert_text_format(:plain_text)
615635
end
616636
}
617637

638+
# deprecated as of Language Server Protocol Specification - 3.15
639+
json =
640+
if Keyword.get(options, :deprecated_supported, false) do
641+
Map.merge(json, %{
642+
"deprecated" => item.tags |> Enum.any?(&(&1 == :deprecated))
643+
})
644+
else
645+
json
646+
end
647+
648+
tags_supported = options |> Keyword.get(:tags_supported, [])
649+
650+
json =
651+
if tags_supported != [] do
652+
Map.merge(json, %{
653+
"tags" => item.tags |> Enum.map(&tag_to_code/1) |> Enum.filter(&(&1 in tags_supported))
654+
})
655+
else
656+
json
657+
end
658+
618659
for {k, v} <- json, not is_nil(v), into: %{}, do: {k, v}
619660
end
620661

621662
defp snippet?(item) do
622663
item.kind == :snippet || String.match?(item.insert_text, ~r/\$\d/)
623664
end
665+
666+
# As defined by CompletionItemTag in https://microsoft.github.io/language-server-protocol/specifications/specification-current/
667+
defp tag_to_code(:deprecated), do: 1
668+
669+
defp metadata_to_tags(metadata) do
670+
# As of Language Server Protocol Specification - 3.15 only one tag is supported
671+
case metadata[:deprecated] do
672+
nil -> []
673+
_ -> [:deprecated]
674+
end
675+
end
624676
end

apps/language_server/lib/language_server/server.ex

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,8 +443,32 @@ defmodule ElixirLS.LanguageServer.Server do
443443
"snippetSupport"
444444
])
445445

446+
# deprecated as of Language Server Protocol Specification - 3.15
447+
deprecated_supported =
448+
!!get_in(state.client_capabilities, [
449+
"textDocument",
450+
"completion",
451+
"completionItem",
452+
"deprecatedSupport"
453+
])
454+
455+
tags_supported =
456+
case get_in(state.client_capabilities, [
457+
"textDocument",
458+
"completion",
459+
"completionItem",
460+
"tagSupport"
461+
]) do
462+
nil -> []
463+
%{"valueSet" => value_set} -> value_set
464+
end
465+
446466
fun = fn ->
447-
Completion.completion(state.source_files[uri].text, line, character, snippets_supported)
467+
Completion.completion(state.source_files[uri].text, line, character,
468+
snippets_supported: snippets_supported,
469+
deprecated_supported: deprecated_supported,
470+
tags_supported: tags_supported
471+
)
448472
end
449473

450474
{:async, fun, state}

apps/language_server/lib/language_server/source_file.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ defmodule ElixirLS.LanguageServer.SourceFile do
119119
nil ->
120120
nil
121121

122-
{line, _} ->
122+
{line, _docs, _metadata} ->
123123
line
124124
end
125125
end
@@ -132,7 +132,7 @@ defmodule ElixirLS.LanguageServer.SourceFile do
132132

133133
docs ->
134134
Enum.find_value(docs, fn
135-
{{^fun, ^arity}, line, _, _, _} -> line
135+
{{^fun, ^arity}, line, _, _, _, _metadata} -> line
136136
_ -> nil
137137
end)
138138
end

0 commit comments

Comments
 (0)