@@ -11,6 +11,7 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceRemote
1111 alias ElixirLS.LanguageServer.Experimental.Protocol.Types.TextEdit
1212 alias ElixirLS.LanguageServer.Experimental.Protocol.Types.Workspace
1313 alias ElixirLS.LanguageServer.Experimental.SourceFile
14+ alias ElixirSense.Core.Parser
1415
1516 @ function_re ~r/ (.*)\/ (.*) is undefined or private. .*:\n (.*)/ s
1617
@@ -24,9 +25,9 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceRemote
2425 one_based_line = extract_start_line ( diagnostic )
2526 suggestions = extract_suggestions ( diagnostic . message )
2627
27- with { :ok , module_aliases , name } <- extract_function ( diagnostic . message ) ,
28+ with { :ok , module_alias , name } <- extract_function ( diagnostic . message ) ,
2829 { :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
3031 replies
3132 else
3233 _ -> [ ]
@@ -37,22 +38,22 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceRemote
3738 defp extract_function ( message ) do
3839 case Regex . scan ( @ function_re , message ) do
3940 [ [ _ , 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 }
4243
4344 _ ->
4445 :error
4546 end
4647 end
4748
4849 defp separate_module_from_name ( full_name ) do
49- { name , module_aliases } =
50+ { name , module_alias } =
5051 full_name
5152 |> String . split ( "." )
5253 |> Enum . map ( & String . to_atom / 1 )
5354 |> List . pop_at ( - 1 )
5455
55- { module_aliases , name }
56+ { module_alias , name }
5657 end
5758
5859 @ suggestion_re ~r/ \* .*\/ [\d ]+/
@@ -80,14 +81,16 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceRemote
8081 defp build_code_actions (
8182 % SourceFile { } = source_file ,
8283 one_based_line ,
83- module_aliases ,
84+ module_alias ,
8485 name ,
8586 suggestions
8687 ) do
8788 with { :ok , line_text } <- SourceFile . fetch_text_at ( source_file , one_based_line ) ,
8889 { :ok , line_ast } <- Ast . from ( line_text ) ,
90+ { :ok , possible_aliases } <-
91+ fetch_possible_aliases ( source_file , one_based_line , module_alias ) ,
8992 { :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
9194 case edits_per_suggestion do
9295 [ ] ->
9396 :error
@@ -98,7 +101,7 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceRemote
98101 text_edits = Enum . map ( text_edits , & update_line ( & 1 , one_based_line ) )
99102
100103 CodeActionResult . new (
101- title: construct_title ( module_aliases , suggestion ) ,
104+ title: construct_title ( module_alias , suggestion ) ,
102105 kind: :quick_fix ,
103106 edit: Workspace.Edit . new ( changes: % { source_file . uri => text_edits } )
104107 )
@@ -109,13 +112,42 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceRemote
109112 end
110113 end
111114
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
113145 suggestions
114146 |> Enum . reduce_while ( [ ] , fn suggestion , acc ->
115147 case CodeMod.ReplaceRemoteFunction . text_edits (
116148 line_text ,
117149 line_ast ,
118- module_aliases ,
150+ possible_aliases ,
119151 name ,
120152 suggestion
121153 ) do
@@ -136,8 +168,8 @@ defmodule ElixirLS.LanguageServer.Experimental.Provider.CodeAction.ReplaceRemote
136168 |> put_in ( [ :range , :end , :line ] , line_number - 1 )
137169 end
138170
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 )
141173
142174 "Replace with #{ module_string } .#{ suggestion } "
143175 end
0 commit comments