Skip to content

Commit 3b5d778

Browse files
committed
adjust to review
1 parent df73277 commit 3b5d778

File tree

8 files changed

+186
-235
lines changed

8 files changed

+186
-235
lines changed
Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
11
defmodule ElixirLS.LanguageServer.Experimental.CodeMod.ReplaceRemoteFunction do
22
alias ElixirLS.LanguageServer.Experimental.CodeMod.Ast
33
alias ElixirLS.LanguageServer.Experimental.CodeMod.Diff
4+
alias ElixirLS.LanguageServer.Experimental.CodeMod.Text
45
alias ElixirLS.LanguageServer.Experimental.Protocol.Types.TextEdit
56

67
@spec text_edits(String.t(), Ast.t(), [atom()], atom(), atom()) ::
78
{:ok, [TextEdit.t()]} | :error
8-
def text_edits(original_text, ast, module, name, suggestion) do
9-
with {:ok, transformed} <- apply_transforms(original_text, ast, module, name, suggestion) do
9+
def text_edits(original_text, ast, module_aliases, name, suggestion) do
10+
with {:ok, transformed} <-
11+
apply_transforms(original_text, ast, module_aliases, name, suggestion) do
1012
{:ok, Diff.diff(original_text, transformed)}
1113
end
1214
end
1315

14-
defp apply_transforms(line_text, quoted_ast, module, name, suggestion) do
15-
leading_indent = leading_indent(line_text)
16+
defp apply_transforms(line_text, quoted_ast, module_aliases, name, suggestion) do
17+
leading_indent = Text.leading_indent(line_text)
1618

1719
updated_ast =
1820
Macro.postwalk(quoted_ast, fn
19-
{:., meta1, [{:__aliases__, meta2, ^module}, ^name]} ->
20-
{:., meta1, [{:__aliases__, meta2, module}, suggestion]}
21+
{:., function_meta, [{:__aliases__, module_meta, ^module_aliases}, ^name]} ->
22+
{:., function_meta, [{:__aliases__, module_meta, module_aliases}, suggestion]}
2123

2224
other ->
2325
other
@@ -30,7 +32,7 @@ defmodule ElixirLS.LanguageServer.Experimental.CodeMod.ReplaceRemoteFunction do
3032
# If the line doesn't compile (like it has a do with no end), ElixirSense
3133
# adds additional lines do documents with errors, so take the first line, as it's
3234
# the properly transformed source
33-
|> fetch_line(0)
35+
|> Text.fetch_line(0)
3436
|> case do
3537
{:ok, text} ->
3638
{:ok, "#{leading_indent}#{text}"}
@@ -42,24 +44,4 @@ defmodule ElixirLS.LanguageServer.Experimental.CodeMod.ReplaceRemoteFunction do
4244
:error
4345
end
4446
end
45-
46-
@indent_regex ~r/^\s+/
47-
defp leading_indent(line_text) do
48-
case Regex.scan(@indent_regex, line_text) do
49-
[indent] -> indent
50-
_ -> ""
51-
end
52-
end
53-
54-
defp fetch_line(message, line_number) do
55-
line =
56-
message
57-
|> String.split(["\r\n", "\r", "\n"])
58-
|> Enum.at(line_number)
59-
60-
case line do
61-
nil -> :error
62-
other -> {:ok, other}
63-
end
64-
end
6547
end

apps/language_server/lib/language_server/experimental/code_mod/replace_with_underscore.ex

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
defmodule ElixirLS.LanguageServer.Experimental.CodeMod.ReplaceWithUnderscore do
22
alias ElixirLS.LanguageServer.Experimental.CodeMod.Ast
33
alias ElixirLS.LanguageServer.Experimental.CodeMod.Diff
4+
alias ElixirLS.LanguageServer.Experimental.CodeMod.Text
45
alias ElixirLS.LanguageServer.Experimental.Protocol.Types.TextEdit
56

67
@spec text_edits(String.t(), Ast.t(), String.t() | atom) :: {:ok, [TextEdit.t()]} | :error
@@ -28,7 +29,7 @@ defmodule ElixirLS.LanguageServer.Experimental.CodeMod.ReplaceWithUnderscore do
2829

2930
defp apply_transform(line_text, quoted_ast, unused_variable_name) do
3031
underscored_variable_name = :"_#{unused_variable_name}"
31-
leading_indent = leading_indent(line_text)
32+
leading_indent = Text.leading_indent(line_text)
3233

3334
Macro.postwalk(quoted_ast, fn
3435
{^unused_variable_name, meta, context} ->
@@ -42,7 +43,7 @@ defmodule ElixirLS.LanguageServer.Experimental.CodeMod.ReplaceWithUnderscore do
4243
# If the line doesn't compile (like it has a do with no end), ElixirSense
4344
# adds additional lines do documents with errors, so take the first line, as it's
4445
# the properly transformed source
45-
|> fetch_line(0)
46+
|> Text.fetch_line(0)
4647
|> case do
4748
{:ok, text} ->
4849
{:ok, "#{leading_indent}#{text}"}
@@ -51,24 +52,4 @@ defmodule ElixirLS.LanguageServer.Experimental.CodeMod.ReplaceWithUnderscore do
5152
error
5253
end
5354
end
54-
55-
@indent_regex ~r/^\s+/
56-
defp leading_indent(line_text) do
57-
case Regex.scan(@indent_regex, line_text) do
58-
[indent] -> indent
59-
_ -> ""
60-
end
61-
end
62-
63-
defp fetch_line(message, line_number) do
64-
line =
65-
message
66-
|> String.split(["\r\n", "\r", "\n"])
67-
|> Enum.at(line_number)
68-
69-
case line do
70-
nil -> :error
71-
other -> {:ok, other}
72-
end
73-
end
7455
end
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
defmodule ElixirLS.LanguageServer.Experimental.CodeMod.Text do
2+
@indent_regex ~r/^\s+/
3+
def leading_indent(line_text) do
4+
case Regex.scan(@indent_regex, line_text) do
5+
[indent] -> indent
6+
_ -> ""
7+
end
8+
end
9+
10+
def fetch_line(message, line_number) do
11+
line =
12+
message
13+
|> String.split(["\r\n", "\r", "\n"])
14+
|> Enum.at(line_number)
15+
16+
case line do
17+
nil -> :error
18+
other -> {:ok, other}
19+
end
20+
end
21+
end

apps/language_server/lib/language_server/experimental/provider/code_action/replace_remote_function.ex

Lines changed: 68 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -5,97 +5,100 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceRemote
55

66
alias ElixirLS.LanguageServer.Experimental.CodeMod
77
alias ElixirLS.LanguageServer.Experimental.CodeMod.Ast
8+
alias ElixirLS.LanguageServer.Experimental.Protocol.Requests.CodeAction
89
alias ElixirLS.LanguageServer.Experimental.Protocol.Types.CodeAction, as: CodeActionResult
910
alias ElixirLS.LanguageServer.Experimental.Protocol.Types.Diagnostic
1011
alias ElixirLS.LanguageServer.Experimental.Protocol.Types.TextEdit
1112
alias ElixirLS.LanguageServer.Experimental.Protocol.Types.Workspace
1213
alias ElixirLS.LanguageServer.Experimental.SourceFile
1314

14-
@pattern ~r/(.*)\/(.*) is undefined or private. .*:\n(.*)/s
15+
@function_re ~r/(.*)\/(.*) is undefined or private. .*:\n(.*)/s
1516

16-
@spec pattern() :: Regex.t()
17-
def pattern, do: @pattern
17+
@spec apply(CodeAction.t()) :: [CodeActionResult.t()]
18+
def apply(%CodeAction{} = code_action) do
19+
source_file = code_action.source_file
20+
diagnostics = get_in(code_action, [:context, :diagnostics]) || []
1821

19-
@spec apply(SourceFile.t(), Diagnostic.t()) :: [CodeActionResult.t()]
20-
def apply(source_file, diagnostic) do
21-
with {:ok, module, name} <- extract_function(diagnostic.message),
22-
{:ok, suggestions} <- extract_suggestions(diagnostic.message),
23-
one_based_line = extract_line(diagnostic),
24-
{:ok, replies} <-
25-
build_code_actions(source_file, one_based_line, module, name, suggestions) do
26-
replies
27-
else
28-
_ ->
29-
[]
30-
end
22+
diagnostics
23+
|> Enum.flat_map(fn %Diagnostic{} = diagnostic ->
24+
one_based_line = extract_start_line(diagnostic)
25+
suggestions = extract_suggestions(diagnostic.message)
26+
27+
with {:ok, module_aliases, name} <- extract_function(diagnostic.message),
28+
{:ok, replies} <-
29+
build_code_actions(source_file, one_based_line, module_aliases, name, suggestions) do
30+
replies
31+
else
32+
_ -> []
33+
end
34+
end)
3135
end
3236

3337
defp extract_function(message) do
34-
case Regex.scan(@pattern, message) do
38+
case Regex.scan(@function_re, message) do
3539
[[_, full_name, _, _]] ->
36-
{module, name} = separate_module_from_name(full_name)
37-
{:ok, module, name}
40+
{module_aliases, name} = separate_module_from_name(full_name)
41+
{:ok, module_aliases, name}
3842

3943
_ ->
4044
:error
4145
end
4246
end
4347

4448
defp separate_module_from_name(full_name) do
45-
{name, module} =
49+
{name, module_aliases} =
4650
full_name
4751
|> String.split(".")
4852
|> Enum.map(&String.to_atom/1)
4953
|> List.pop_at(-1)
5054

51-
{module, name}
55+
{module_aliases, name}
5256
end
5357

54-
@suggestion_pattern ~r/\* .*\/[\d]+/
58+
@suggestion_re ~r/\* .*\/[\d]+/
5559
defp extract_suggestions(message) do
56-
case Regex.scan(@pattern, message) do
60+
case Regex.scan(@function_re, message) do
5761
[[_, _, arity, suggestions_string]] ->
58-
suggestions =
59-
@suggestion_pattern
60-
|> Regex.scan(suggestions_string)
61-
|> Enum.flat_map(fn [suggestion] ->
62-
case String.split(suggestion, [" ", "/"]) do
63-
["*", name, ^arity] -> [String.to_atom(name)]
64-
_ -> []
65-
end
66-
end)
67-
68-
{:ok, suggestions}
62+
@suggestion_re
63+
|> Regex.scan(suggestions_string)
64+
|> Enum.flat_map(fn [suggestion] ->
65+
case String.split(suggestion, [" ", "/"]) do
66+
["*", name, ^arity] -> [String.to_atom(name)]
67+
_ -> []
68+
end
69+
end)
6970

7071
_ ->
71-
:error
72+
[]
7273
end
7374
end
7475

75-
defp extract_line(%Diagnostic{} = diagnostic) do
76+
defp extract_start_line(%Diagnostic{} = diagnostic) do
7677
diagnostic.range.start.line
7778
end
7879

79-
defp build_code_actions(%SourceFile{} = source_file, one_based_line, module, name, suggestions) do
80+
defp build_code_actions(
81+
%SourceFile{} = source_file,
82+
one_based_line,
83+
module_aliases,
84+
name,
85+
suggestions
86+
) do
8087
with {:ok, line_text} <- SourceFile.fetch_text_at(source_file, one_based_line),
8188
{:ok, line_ast} <- Ast.from(line_text),
8289
{:ok, edits_per_suggestion} <-
83-
text_edits_per_suggestion(line_text, line_ast, module, name, suggestions) do
90+
text_edits_per_suggestion(line_text, line_ast, module_aliases, name, suggestions) do
8491
case edits_per_suggestion do
8592
[] ->
8693
:error
8794

8895
[_ | _] ->
89-
edits_per_suggestion =
96+
replies =
9097
Enum.map(edits_per_suggestion, fn {text_edits, suggestion} ->
9198
text_edits = Enum.map(text_edits, &update_line(&1, one_based_line))
92-
{text_edits, suggestion}
93-
end)
9499

95-
replies =
96-
Enum.map(edits_per_suggestion, fn {text_edits, function_name} ->
97100
CodeActionResult.new(
98-
title: construct_title(module, function_name),
101+
title: construct_title(module_aliases, suggestion),
99102
kind: :quick_fix,
100103
edit: Workspace.Edit.new(changes: %{source_file.uri => text_edits})
101104
)
@@ -106,24 +109,25 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceRemote
106109
end
107110
end
108111

109-
defp text_edits_per_suggestion(line_text, line_ast, module, name, suggestions) do
110-
Enum.reduce(suggestions, {:ok, []}, fn
111-
suggestion, {:ok, edits_per_suggestions} ->
112-
case CodeMod.ReplaceRemoteFunction.text_edits(
113-
line_text,
114-
line_ast,
115-
module,
116-
name,
117-
suggestion
118-
) do
119-
{:ok, []} -> {:ok, edits_per_suggestions}
120-
{:ok, text_edits} -> {:ok, [{text_edits, suggestion} | edits_per_suggestions]}
121-
:error -> :error
122-
end
123-
124-
_suggestion, :error ->
125-
:error
112+
defp text_edits_per_suggestion(line_text, line_ast, module_aliases, name, suggestions) do
113+
suggestions
114+
|> Enum.reduce_while([], fn suggestion, acc ->
115+
case CodeMod.ReplaceRemoteFunction.text_edits(
116+
line_text,
117+
line_ast,
118+
module_aliases,
119+
name,
120+
suggestion
121+
) do
122+
{:ok, []} -> {:cont, acc}
123+
{:ok, edits} -> {:cont, [{edits, suggestion} | acc]}
124+
:error -> {:halt, :error}
125+
end
126126
end)
127+
|> case do
128+
:error -> :error
129+
edits -> {:ok, edits}
130+
end
127131
end
128132

129133
defp update_line(%TextEdit{} = text_edit, line_number) do
@@ -132,16 +136,9 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceRemote
132136
|> put_in([:range, :end, :line], line_number - 1)
133137
end
134138

135-
defp construct_title(module_list, function_name) do
136-
module_string =
137-
module_list
138-
|> Enum.map(fn module ->
139-
module
140-
|> Atom.to_string()
141-
|> String.trim_leading("Elixir.")
142-
end)
143-
|> Enum.join(".")
144-
145-
"Replace function with #{module_string}.#{function_name}"
139+
defp construct_title(module_aliases, suggestion) do
140+
module_string = Enum.map_join(module_aliases, ".", &Atom.to_string/1)
141+
142+
"Replace with #{module_string}.#{suggestion}"
146143
end
147144
end

apps/language_server/lib/language_server/experimental/provider/code_action/replace_with_underscore.ex

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,28 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceWithUn
55

66
alias ElixirLS.LanguageServer.Experimental.CodeMod
77
alias ElixirLS.LanguageServer.Experimental.CodeMod.Ast
8+
alias ElixirLS.LanguageServer.Experimental.Protocol.Requests.CodeAction
89
alias ElixirLS.LanguageServer.Experimental.Protocol.Types.CodeAction, as: CodeActionResult
910
alias ElixirLS.LanguageServer.Experimental.Protocol.Types.Diagnostic
1011
alias ElixirLS.LanguageServer.Experimental.Protocol.Types.TextEdit
1112
alias ElixirLS.LanguageServer.Experimental.Protocol.Types.Workspace
1213
alias ElixirLS.LanguageServer.Experimental.SourceFile
1314

14-
@pattern ~r/variable "([^"]+)" is unused/
15+
@spec apply(CodeAction.t()) :: [CodeActionResult.t()]
16+
def apply(%CodeAction{} = code_action) do
17+
source_file = code_action.source_file
18+
diagnostics = get_in(code_action, [:context, :diagnostics]) || []
1519

16-
@spec pattern() :: Regex.t()
17-
def pattern, do: @pattern
18-
19-
@spec apply(SourceFile.t(), Diagnostic.t()) :: [CodeActionResult.t()]
20-
def apply(source_file, diagnostic) do
21-
with {:ok, variable_name, one_based_line} <- extract_variable_and_line(diagnostic),
22-
{:ok, reply} <- build_code_action(source_file, one_based_line, variable_name) do
23-
[reply]
24-
else
25-
_ ->
26-
[]
27-
end
20+
diagnostics
21+
|> Enum.flat_map(fn %Diagnostic{} = diagnostic ->
22+
with {:ok, variable_name, one_based_line} <- extract_variable_and_line(diagnostic),
23+
{:ok, reply} <- build_code_action(source_file, one_based_line, variable_name) do
24+
[reply]
25+
else
26+
_ ->
27+
[]
28+
end
29+
end)
2830
end
2931

3032
defp build_code_action(%SourceFile{} = source_file, one_based_line, variable_name) do
@@ -64,8 +66,9 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceWithUn
6466
end
6567
end
6668

69+
@variable_re ~r/variable "([^"]+)" is unused/
6770
defp extract_variable_name(message) do
68-
case Regex.scan(@pattern, message) do
71+
case Regex.scan(@variable_re, message) do
6972
[[_, variable_name]] ->
7073
{:ok, String.to_atom(variable_name)}
7174

0 commit comments

Comments
 (0)