@@ -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
147144end
0 commit comments