Skip to content

Commit 8987355

Browse files
committed
wip
1 parent f04f45b commit 8987355

File tree

5 files changed

+100
-70
lines changed

5 files changed

+100
-70
lines changed

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

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -744,8 +744,8 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
744744
}
745745
end
746746

747-
defp from_completion_item(%{type: :param_option} = suggestion, _context, _options) do
748-
%{name: name, origin: _origin, doc: doc, type_spec: type_spec, expanded_spec: expanded_spec} =
747+
defp from_completion_item(%{type: :param_option} = suggestion, context, _options) do
748+
%{name: name, origin: _origin, doc: doc, type_spec: type_spec, expanded_spec: expanded_spec, subtype: subtype} =
749749
suggestion
750750

751751
formatted_spec =
@@ -755,11 +755,33 @@ defmodule ElixirLS.LanguageServer.Providers.Completion do
755755
""
756756
end
757757

758+
{insert_text, text_edit} = cond do
759+
subtype == :keyword and not String.ends_with?(context.prefix, ":") ->
760+
{"#{name}: ", nil}
761+
subtype == :keyword ->
762+
{"", %{
763+
"range" => %{
764+
"start" => %{
765+
"line" => context.line,
766+
"character" =>
767+
context.character - String.length(context.prefix)
768+
},
769+
"end" => %{"line" => context.line, "character" => context.character}
770+
},
771+
"newText" => "#{name}: "
772+
}}
773+
match?(":" <> _, context.prefix) ->
774+
{name, nil}
775+
true ->
776+
{":#{name}", nil}
777+
end
778+
758779
%__MODULE__{
759780
label: to_string(name),
760781
detail: "#{type_spec}",
761782
documentation: "#{doc}#{formatted_spec}",
762-
insert_text: "#{name}: ",
783+
insert_text: insert_text,
784+
text_edit: text_edit,
763785
priority: 10,
764786
kind: :field,
765787
tags: []

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

Lines changed: 34 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ defmodule ElixirLS.LanguageServer.Providers.Completion.Reducers.Params do
1616

1717
@type param_option :: %{
1818
type: :param_option,
19+
subtype: :keyword | :atom,
1920
name: String.t(),
2021
origin: String.t(),
2122
type_spec: String.t(),
@@ -36,6 +37,8 @@ defmodule ElixirLS.LanguageServer.Providers.Completion.Reducers.Params do
3637
with %{
3738
candidate: {mod, fun},
3839
elixir_prefix: elixir_prefix,
40+
options_so_far: options_so_far,
41+
cursor_at_option: cursor_at_option,
3942
npar: npar
4043
} <-
4144
Source.which_func(prefix, binding_env),
@@ -48,63 +51,38 @@ defmodule ElixirLS.LanguageServer.Providers.Completion.Reducers.Params do
4851
cursor_context.cursor_position,
4952
not elixir_prefix
5053
) do
51-
list = for {name, type} <- ElixirSense.Core.Options.get_param_options(mod, fun, npar + 1, buffer_metadata) do
52-
%{
53-
doc: "",
54-
expanded_spec: "",
55-
name: name |> Atom.to_string(),
56-
origin: inspect(mod),
57-
type: :param_option,
58-
type_spec: Introspection.to_string_with_parens(type)
59-
}
54+
list = for opt <- ElixirSense.Core.Options.get_param_options(mod, fun, npar + 1, buffer_metadata) do
55+
case opt do
56+
{name, type} ->
57+
# match on atom:
58+
if Matcher.match?(to_string(name) <> ":", hint) do
59+
expanded_spec = Introspection.to_string_with_parens(type)
60+
%{
61+
doc: "",
62+
expanded_spec: expanded_spec,
63+
name: name |> Atom.to_string(),
64+
origin: inspect(mod),
65+
type: :param_option,
66+
subtype: :keyword,
67+
type_spec: expanded_spec
68+
}
69+
end
70+
name ->
71+
# match on :atom
72+
if options_so_far == [] and cursor_at_option == true and Matcher.match?(inspect(name), hint) do
73+
%{
74+
doc: "",
75+
expanded_spec: "",
76+
name: name |> Atom.to_string(),
77+
origin: inspect(mod),
78+
type: :param_option,
79+
subtype: :atom,
80+
type_spec: ""
81+
}
82+
end
83+
end
6084
end
61-
62-
63-
# list =
64-
# if Code.ensure_loaded?(mod) do
65-
# if function_exported?(mod, fun, npar + 1) do
66-
# TypeInfo.extract_param_options(mod, fun, npar)
67-
# else
68-
# TypeInfo.extract_param_options(mod, :"MACRO-#{fun}", npar + 1)
69-
# end
70-
# |> options_to_suggestions(mod)
71-
# |> Enum.filter(&Matcher.match?(&1.name, hint))
72-
# |> dbg
73-
# else
74-
# # TODO metadata?
75-
# dbg(buffer_metadata.specs)
76-
77-
# with %ElixirSense.Core.State.SpecInfo{specs: [spec | _]} = info <-
78-
# buffer_metadata.specs[{mod, fun, npar + 1}],
79-
# {:ok,
80-
# {:@, _,
81-
# [
82-
# {:spec, _,
83-
# [
84-
# {:"::", _,
85-
# [
86-
# {^fun, _, params},
87-
# _
88-
# ]}
89-
# ]}
90-
# ]}}
91-
# when is_list(params) <- Code.string_to_quoted(spec),
92-
# {:list, _, [options]} <- List.last(params) |> dbg do
93-
# for {name, type} <- extract_options(options, []) do
94-
# %{
95-
# doc: "",
96-
# expanded_spec: "",
97-
# name: name |> Atom.to_string(),
98-
# origin: inspect(mod),
99-
# type: :param_option,
100-
# type_spec: Introspection.to_string_with_parens(type)
101-
# }
102-
# end
103-
# |> dbg
104-
# else
105-
# _ -> []
106-
# end
107-
# end
85+
|> Enum.reject(&is_nil/1)
10886

10987
{:cont, %{acc | result: acc.result ++ list}}
11088
else

apps/language_server/lib/language_server/providers/formatting.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ defmodule ElixirLS.LanguageServer.Providers.Formatting do
1111
# file_path and project_dir are absolute paths with universal separators
1212
if SourceFile.Path.path_in_dir?(file_path, project_dir) do
1313
# file in project_dir we find formatter and options for file
14-
case SourceFile.formatter_for(uri, project_dir, mix_project?) do
14+
case SourceFile.formatter_for(uri, project_dir, mix_project?) |> dbg do
1515
{:ok, {formatter, opts, formatter_exs_dir}} ->
1616
if should_format?(uri, formatter_exs_dir, opts[:inputs], project_dir) do
1717
do_format(source_file, formatter, opts)

apps/language_server/lib/language_server/source_file.ex

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -267,14 +267,19 @@ defmodule ElixirLS.LanguageServer.SourceFile do
267267
if mix_project? do
268268
if MixProjectCache.loaded?() do
269269
opts = [
270-
deps_paths: MixProjectCache.deps_paths(),
271-
manifest_path: MixProjectCache.manifest_path(),
272-
config_mtime: MixProjectCache.config_mtime(),
273-
mix_project: MixProjectCache.get(),
270+
# deps_paths: MixProjectCache.deps_paths(),
271+
# manifest_path: MixProjectCache.manifest_path(),
272+
# config_mtime: MixProjectCache.config_mtime(),
273+
# mix_project: MixProjectCache.get(),
274274
root: project_dir
275275
]
276276

277-
{:ok, Mix.Tasks.ElixirLSFormat.formatter_for_file(path, opts)}
277+
case Mix.Tasks.Format.formatter_for_file(path, opts) do
278+
{formatter, opts} ->
279+
{:ok, {formatter, opts, opts[:root]}}
280+
end
281+
282+
# {:ok, Mix.Tasks.ElixirLSFormat.formatter_for_file(path, opts)}
278283
else
279284
{:error, :project_not_loaded}
280285
end

apps/language_server/test/providers/completion/suggestions_test.exs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3996,10 +3996,35 @@ defmodule ElixirLS.LanguageServer.Providers.Completion.SuggestionTest do
39963996
end
39973997

39983998
test "atom only options" do
3999-
buffer = ":ets.new(:name,"
4000-
4001-
assert suggestion_by_name("duplicate_bag", buffer).type_spec == ""
4002-
assert suggestion_by_name("named_table", buffer).doc == ""
3999+
# only keyword in shorthand keyword list
4000+
buffer = ":ets.new(:name, "
4001+
assert list = suggestions_by_type(:param_option, buffer)
4002+
refute Enum.any?(list, &match?(%{name: "bag"}, &1))
4003+
assert Enum.any?(list, &match?(%{name: "write_concurrency"}, &1))
4004+
4005+
buffer = ":ets.new(:name, heir: pid, "
4006+
assert list = suggestions_by_type(:param_option, buffer)
4007+
refute Enum.any?(list, &match?(%{name: "bag"}, &1))
4008+
assert Enum.any?(list, &match?(%{name: "write_concurrency"}, &1))
4009+
4010+
# suggest atom options in list
4011+
buffer = ":ets.new(:name, ["
4012+
assert list = suggestions_by_type(:param_option, buffer)
4013+
assert Enum.any?(list, &match?(%{name: "bag"}, &1))
4014+
assert Enum.any?(list, &match?(%{name: "set"}, &1))
4015+
assert Enum.any?(list, &match?(%{name: "write_concurrency"}, &1))
4016+
4017+
buffer = ":ets.new(:name, [:set, "
4018+
assert list = suggestions_by_type(:param_option, buffer)
4019+
assert Enum.any?(list, &match?(%{name: "bag"}, &1))
4020+
# refute Enum.any?(list, &match?(%{name: "set"}, &1))
4021+
assert Enum.any?(list, &match?(%{name: "write_concurrency"}, &1))
4022+
4023+
# no atoms after keyword pair
4024+
buffer = ":ets.new(:name, [:set, heir: pid, "
4025+
assert list = suggestions_by_type(:param_option, buffer)
4026+
refute Enum.any?(list, &match?(%{name: "bag"}, &1))
4027+
assert Enum.any?(list, &match?(%{name: "write_concurrency"}, &1))
40034028
end
40044029

40054030
test "format type spec" do
@@ -4029,7 +4054,7 @@ defmodule ElixirLS.LanguageServer.Providers.Completion.SuggestionTest do
40294054
"""
40304055

40314056
list = Suggestion.suggestions(buffer, 6, 10)
4032-
assert [%{name: "remote_with_params_o"}] = list |> Enum.filter(&(&1.type == :param_option))
4057+
assert [%{name: "bar"}, %{name: "foo"}] = list |> Enum.filter(&(&1.type == :param_option))
40334058
end
40344059
end
40354060

0 commit comments

Comments
 (0)