@@ -11,6 +11,7 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceRemote
11
11
alias ElixirLS.LanguageServer.Experimental.Protocol.Types.TextEdit
12
12
alias ElixirLS.LanguageServer.Experimental.Protocol.Types.Workspace
13
13
alias ElixirLS.LanguageServer.Experimental.SourceFile
14
+ alias ElixirSense.Core.Parser
14
15
15
16
@ function_re ~r/ (.*)\/ (.*) is undefined or private. .*:\n (.*)/ s
16
17
@@ -24,9 +25,9 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceRemote
24
25
one_based_line = extract_start_line ( diagnostic )
25
26
suggestions = extract_suggestions ( diagnostic . message )
26
27
27
- with { :ok , module_aliases , name } <- extract_function ( diagnostic . message ) ,
28
+ with { :ok , module_alias , name } <- extract_function ( diagnostic . message ) ,
28
29
{ :ok , replies } <-
29
- build_code_actions ( source_file , one_based_line , module_aliases , name , suggestions ) do
30
+ build_code_actions ( source_file , one_based_line , module_alias , name , suggestions ) do
30
31
replies
31
32
else
32
33
_ -> [ ]
@@ -37,22 +38,22 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceRemote
37
38
defp extract_function ( message ) do
38
39
case Regex . scan ( @ function_re , message ) do
39
40
[ [ _ , full_name , _ , _ ] ] ->
40
- { module_aliases , name } = separate_module_from_name ( full_name )
41
- { :ok , module_aliases , name }
41
+ { module_alias , name } = separate_module_from_name ( full_name )
42
+ { :ok , module_alias , name }
42
43
43
44
_ ->
44
45
:error
45
46
end
46
47
end
47
48
48
49
defp separate_module_from_name ( full_name ) do
49
- { name , module_aliases } =
50
+ { name , module_alias } =
50
51
full_name
51
52
|> String . split ( "." )
52
53
|> Enum . map ( & String . to_atom / 1 )
53
54
|> List . pop_at ( - 1 )
54
55
55
- { module_aliases , name }
56
+ { module_alias , name }
56
57
end
57
58
58
59
@ suggestion_re ~r/ \* .*\/ [\d ]+/
@@ -80,14 +81,16 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceRemote
80
81
defp build_code_actions (
81
82
% SourceFile { } = source_file ,
82
83
one_based_line ,
83
- module_aliases ,
84
+ module_alias ,
84
85
name ,
85
86
suggestions
86
87
) do
87
88
with { :ok , line_text } <- SourceFile . fetch_text_at ( source_file , one_based_line ) ,
88
89
{ :ok , line_ast } <- Ast . from ( line_text ) ,
90
+ { :ok , possible_aliases } <-
91
+ fetch_possible_aliases ( source_file , one_based_line , module_alias ) ,
89
92
{ :ok , edits_per_suggestion } <-
90
- text_edits_per_suggestion ( line_text , line_ast , module_aliases , name , suggestions ) do
93
+ text_edits_per_suggestion ( line_text , line_ast , possible_aliases , name , suggestions ) do
91
94
case edits_per_suggestion do
92
95
[ ] ->
93
96
:error
@@ -98,7 +101,7 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceRemote
98
101
text_edits = Enum . map ( text_edits , & update_line ( & 1 , one_based_line ) )
99
102
100
103
CodeActionResult . new (
101
- title: construct_title ( module_aliases , suggestion ) ,
104
+ title: construct_title ( module_alias , suggestion ) ,
102
105
kind: :quick_fix ,
103
106
edit: Workspace.Edit . new ( changes: % { source_file . uri => text_edits } )
104
107
)
@@ -109,13 +112,42 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceRemote
109
112
end
110
113
end
111
114
112
- defp text_edits_per_suggestion ( line_text , line_ast , module_aliases , name , suggestions ) do
115
+ # Extracted `ElixirSense.Core.State.Env` contains all reachable aliases as a list of tuples
116
+ # `{alias, aliased}`. If `aliased` is a prefix of `module_alias`, the function to be replaced
117
+ # may use the corresponding `alias`.
118
+ defp fetch_possible_aliases ( source_file , one_based_line , module_alias ) do
119
+ metadata =
120
+ source_file
121
+ |> SourceFile . to_string ( )
122
+ |> Parser . parse_string ( true , true , one_based_line )
123
+
124
+ case metadata . lines_to_env [ one_based_line ] do
125
+ % ElixirSense.Core.State.Env { aliases: aliases } ->
126
+ possible_aliases =
127
+ Enum . flat_map ( aliases , fn { _alias , aliased } ->
128
+ aliased = aliased |> Module . split ( ) |> Enum . map ( & String . to_atom / 1 )
129
+
130
+ if aliased == Enum . take ( module_alias , length ( aliased ) ) do
131
+ [ Enum . drop ( module_alias , length ( aliased ) - 1 ) ]
132
+ else
133
+ [ ]
134
+ end
135
+ end )
136
+
137
+ { :ok , [ module_alias | possible_aliases ] }
138
+
139
+ _ ->
140
+ :error
141
+ end
142
+ end
143
+
144
+ defp text_edits_per_suggestion ( line_text , line_ast , possible_aliases , name , suggestions ) do
113
145
suggestions
114
146
|> Enum . reduce_while ( [ ] , fn suggestion , acc ->
115
147
case CodeMod.ReplaceRemoteFunction . text_edits (
116
148
line_text ,
117
149
line_ast ,
118
- module_aliases ,
150
+ possible_aliases ,
119
151
name ,
120
152
suggestion
121
153
) do
@@ -136,8 +168,8 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceRemote
136
168
|> put_in ( [ :range , :end , :line ] , line_number - 1 )
137
169
end
138
170
139
- defp construct_title ( module_aliases , suggestion ) do
140
- module_string = Enum . map_join ( module_aliases , "." , & Atom . to_string / 1 )
171
+ defp construct_title ( module_alias , suggestion ) do
172
+ module_string = Enum . map_join ( module_alias , "." , & Atom . to_string / 1 )
141
173
142
174
"Replace with #{ module_string } .#{ suggestion } "
143
175
end
0 commit comments