@@ -5,97 +5,100 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceRemote
5
5
6
6
alias ElixirLS.LanguageServer.Experimental.CodeMod
7
7
alias ElixirLS.LanguageServer.Experimental.CodeMod.Ast
8
+ alias ElixirLS.LanguageServer.Experimental.Protocol.Requests.CodeAction
8
9
alias ElixirLS.LanguageServer.Experimental.Protocol.Types.CodeAction , as: CodeActionResult
9
10
alias ElixirLS.LanguageServer.Experimental.Protocol.Types.Diagnostic
10
11
alias ElixirLS.LanguageServer.Experimental.Protocol.Types.TextEdit
11
12
alias ElixirLS.LanguageServer.Experimental.Protocol.Types.Workspace
12
13
alias ElixirLS.LanguageServer.Experimental.SourceFile
13
14
14
- @ pattern ~r/ (.*)\/ (.*) is undefined or private. .*:\n (.*)/ s
15
+ @ function_re ~r/ (.*)\/ (.*) is undefined or private. .*:\n (.*)/ s
15
16
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 ] ) || [ ]
18
21
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 )
31
35
end
32
36
33
37
defp extract_function ( message ) do
34
- case Regex . scan ( @ pattern , message ) do
38
+ case Regex . scan ( @ function_re , message ) do
35
39
[ [ _ , 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 }
38
42
39
43
_ ->
40
44
:error
41
45
end
42
46
end
43
47
44
48
defp separate_module_from_name ( full_name ) do
45
- { name , module } =
49
+ { name , module_aliases } =
46
50
full_name
47
51
|> String . split ( "." )
48
52
|> Enum . map ( & String . to_atom / 1 )
49
53
|> List . pop_at ( - 1 )
50
54
51
- { module , name }
55
+ { module_aliases , name }
52
56
end
53
57
54
- @ suggestion_pattern ~r/ \* .*\/ [\d ]+/
58
+ @ suggestion_re ~r/ \* .*\/ [\d ]+/
55
59
defp extract_suggestions ( message ) do
56
- case Regex . scan ( @ pattern , message ) do
60
+ case Regex . scan ( @ function_re , message ) do
57
61
[ [ _ , _ , 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 )
69
70
70
71
_ ->
71
- :error
72
+ [ ]
72
73
end
73
74
end
74
75
75
- defp extract_line ( % Diagnostic { } = diagnostic ) do
76
+ defp extract_start_line ( % Diagnostic { } = diagnostic ) do
76
77
diagnostic . range . start . line
77
78
end
78
79
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
80
87
with { :ok , line_text } <- SourceFile . fetch_text_at ( source_file , one_based_line ) ,
81
88
{ :ok , line_ast } <- Ast . from ( line_text ) ,
82
89
{ :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
84
91
case edits_per_suggestion do
85
92
[ ] ->
86
93
:error
87
94
88
95
[ _ | _ ] ->
89
- edits_per_suggestion =
96
+ replies =
90
97
Enum . map ( edits_per_suggestion , fn { text_edits , suggestion } ->
91
98
text_edits = Enum . map ( text_edits , & update_line ( & 1 , one_based_line ) )
92
- { text_edits , suggestion }
93
- end )
94
99
95
- replies =
96
- Enum . map ( edits_per_suggestion , fn { text_edits , function_name } ->
97
100
CodeActionResult . new (
98
- title: construct_title ( module , function_name ) ,
101
+ title: construct_title ( module_aliases , suggestion ) ,
99
102
kind: :quick_fix ,
100
103
edit: Workspace.Edit . new ( changes: % { source_file . uri => text_edits } )
101
104
)
@@ -106,24 +109,25 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceRemote
106
109
end
107
110
end
108
111
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
126
126
end )
127
+ |> case do
128
+ :error -> :error
129
+ edits -> { :ok , edits }
130
+ end
127
131
end
128
132
129
133
defp update_line ( % TextEdit { } = text_edit , line_number ) do
@@ -132,16 +136,9 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceRemote
132
136
|> put_in ( [ :range , :end , :line ] , line_number - 1 )
133
137
end
134
138
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 } "
146
143
end
147
144
end
0 commit comments