@@ -6,10 +6,9 @@ defmodule ElixirLS.LanguageServer.Providers.CodeAction.ReplaceRemoteFunction do
66
77 use ElixirLS.LanguageServer.Protocol
88
9+ alias ElixirLS.LanguageServer.Protocol.TextEdit
910 alias ElixirLS.LanguageServer.Providers.CodeAction.CodeActionResult
1011 alias ElixirLS.LanguageServer.Providers.CodeMod.Ast
11- alias ElixirLS.LanguageServer.Providers.CodeMod.Diff
12- alias ElixirLS.LanguageServer.Providers.CodeMod.Text
1312 alias ElixirLS.LanguageServer.SourceFile
1413 alias ElixirSense.Core.Parser
1514
@@ -18,11 +17,14 @@ defmodule ElixirLS.LanguageServer.Providers.CodeAction.ReplaceRemoteFunction do
1817 @ spec apply ( SourceFile . t ( ) , String . t ( ) , [ map ( ) ] ) :: [ CodeActionResult . t ( ) ]
1918 def apply ( % SourceFile { } = source_file , uri , diagnostics ) do
2019 Enum . flat_map ( diagnostics , fn diagnostic ->
21- with { :ok , module , function , arity , line_number } <- extract_function_and_line ( diagnostic ) ,
22- { :ok , suggestions } <- prepare_suggestions ( module , function , arity ) do
23- to_code_actions ( source_file , line_number , module , function , suggestions , uri )
24- else
25- _ -> [ ]
20+ case extract_function_and_line ( diagnostic ) do
21+ { :ok , module , function , arity , line } ->
22+ suggestions = prepare_suggestions ( module , function , arity )
23+
24+ build_code_actions ( source_file , line , module , function , suggestions , uri )
25+
26+ :error ->
27+ [ ]
2628 end
2729 end )
2830 end
@@ -38,6 +40,9 @@ defmodule ElixirLS.LanguageServer.Providers.CodeAction.ReplaceRemoteFunction do
3840 with [ [ _ , module_and_function , arity ] ] <- Regex . scan ( @ function_re , message ) ,
3941 { :ok , module , function_name } <- separate_module_from_function ( module_and_function ) do
4042 { :ok , module , function_name , String . to_integer ( arity ) }
43+ else
44+ _ ->
45+ :error
4146 end
4247 end
4348
@@ -65,17 +70,14 @@ defmodule ElixirLS.LanguageServer.Providers.CodeAction.ReplaceRemoteFunction do
6570 @ function_threshold 0.77
6671 @ max_suggestions 5
6772 defp prepare_suggestions ( module , function , arity ) do
68- suggestions =
69- for { module_function , ^ arity } <- module_functions ( module ) ,
70- distance = module_function |> Atom . to_string ( ) |> String . jaro_distance ( function ) ,
71- distance >= @ function_threshold do
72- { distance , module_function }
73- end
74- |> Enum . sort ( :desc )
75- |> Enum . take ( @ max_suggestions )
76- |> Enum . map ( fn { _distance , module_function } -> module_function end )
77-
78- { :ok , suggestions }
73+ for { module_function , ^ arity } <- module_functions ( module ) ,
74+ distance = module_function |> Atom . to_string ( ) |> String . jaro_distance ( function ) ,
75+ distance >= @ function_threshold do
76+ { distance , module_function }
77+ end
78+ |> Enum . sort ( :desc )
79+ |> Enum . take ( @ max_suggestions )
80+ |> Enum . map ( fn { _distance , module_function } -> module_function end )
7981 end
8082
8183 defp module_functions ( module ) do
@@ -86,12 +88,12 @@ defmodule ElixirLS.LanguageServer.Providers.CodeAction.ReplaceRemoteFunction do
8688 end
8789 end
8890
89- defp to_code_actions ( % SourceFile { } = source_file , line_number , module , name , suggestions , uri ) do
91+ defp build_code_actions ( % SourceFile { } = source_file , line , module , name , suggestions , uri ) do
9092 suggestions
9193 |> Enum . reduce ( [ ] , fn suggestion , acc ->
92- case apply_transform ( source_file , line_number , module , name , suggestion ) do
94+ case text_edits ( source_file , line , module , name , suggestion ) do
9395 { :ok , [ _ | _ ] = text_edits } ->
94- text_edits = Enum . map ( text_edits , & update_line ( & 1 , line_number ) )
96+ text_edits = Enum . map ( text_edits , & update_line ( & 1 , line ) )
9597
9698 code_action =
9799 CodeActionResult . new ( "Rename to #{ suggestion } " , "quickfix" , text_edits , uri )
@@ -105,58 +107,51 @@ defmodule ElixirLS.LanguageServer.Providers.CodeAction.ReplaceRemoteFunction do
105107 |> Enum . reverse ( )
106108 end
107109
108- defp apply_transform ( source_file , line_number , module , name , suggestion ) do
109- with { :ok , text } <- fetch_line ( source_file , line_number ) ,
110- { :ok , ast } <- Ast . from ( text ) do
111- function_atom = String . to_atom ( name )
112-
113- leading_indent = Text . leading_indent ( text )
114- trailing_comment = Text . trailing_comment ( text )
115-
116- ast
117- |> Macro . postwalk ( fn
118- { :. , function_meta , [ { :__aliases__ , module_meta , module_alias } , ^ function_atom ] } ->
119- case expand_alias ( source_file , module_alias , line_number ) do
120- { :ok , ^ module } ->
121- { :. , function_meta , [ { :__aliases__ , module_meta , module_alias } , suggestion ] }
122-
123- _ ->
124- { :. , function_meta , [ { :__aliases__ , module_meta , module_alias } , function_atom ] }
125- end
126-
127- # erlang call
128- { :. , function_meta , [ ^ module , ^ function_atom ] } ->
129- { :. , function_meta , [ module , suggestion ] }
130-
131- other ->
132- other
133- end )
134- |> to_one_line_string ( )
135- |> case do
136- { :ok , updated_text } ->
137- text_edits = Diff . diff ( text , "#{ leading_indent } #{ updated_text } #{ trailing_comment } " )
138-
139- { :ok , text_edits }
140-
141- :error ->
142- :error
143- end
110+ @ spec text_edits ( SourceFile . t ( ) , non_neg_integer ( ) , atom ( ) , String . t ( ) , atom ( ) ) ::
111+ { :ok , [ TextEdit . t ( ) ] } | :error
112+ defp text_edits ( % SourceFile { } = source_file , line , module , name , suggestion ) do
113+ with { :ok , updated_text } <- apply_transform ( source_file , line , module , name , suggestion ) do
114+ to_text_edits ( source_file . text , updated_text )
144115 end
145116 end
146117
147- defp fetch_line ( % SourceFile { } = source_file , line_number ) do
148- lines = SourceFile . lines ( source_file )
118+ defp apply_transform ( source_file , line , module , name , suggestion ) do
119+ with { :ok , ast , comments } <- Ast . from ( source_file ) do
120+ function_atom = String . to_atom ( name )
149121
150- if length ( lines ) > line_number do
151- { :ok , Enum . at ( lines , line_number ) }
152- else
153- :error
122+ one_based_line = line + 1
123+
124+ updated_text =
125+ ast
126+ |> Macro . postwalk ( fn
127+ { :. , [ line: ^ one_based_line ] ,
128+ [ { :__aliases__ , module_meta , module_alias } , ^ function_atom ] } ->
129+ case expand_alias ( source_file , module_alias , line ) do
130+ { :ok , ^ module } ->
131+ { :. , [ line: one_based_line ] ,
132+ [ { :__aliases__ , module_meta , module_alias } , suggestion ] }
133+
134+ _ ->
135+ { :. , [ line: one_based_line ] ,
136+ [ { :__aliases__ , module_meta , module_alias } , function_atom ] }
137+ end
138+
139+ # erlang call
140+ { :. , [ line: ^ one_based_line ] , [ { :__block__ , module_meta , [ ^ module ] } , ^ function_atom ] } ->
141+ { :. , [ line: one_based_line ] , [ { :__block__ , module_meta , [ module ] } , suggestion ] }
142+
143+ other ->
144+ other
145+ end )
146+ |> Ast . to_string ( comments )
147+
148+ { :ok , updated_text }
154149 end
155150 end
156151
157152 @ spec expand_alias ( SourceFile . t ( ) , [ atom ( ) ] , non_neg_integer ( ) ) :: { :ok , atom ( ) } | :error
158- defp expand_alias ( source_file , module_alias , line_number ) do
159- with { :ok , aliases } <- aliases_at ( source_file , line_number ) do
153+ defp expand_alias ( source_file , module_alias , line ) do
154+ with { :ok , aliases } <- aliases_at ( source_file , line ) do
160155 aliases
161156 |> Enum . map ( fn { module , aliased } ->
162157 module = module |> module_to_alias ( ) |> List . first ( )
@@ -177,8 +172,8 @@ defmodule ElixirLS.LanguageServer.Providers.CodeAction.ReplaceRemoteFunction do
177172 end
178173 end
179174
180- defp aliases_at ( source_file , line_number ) do
181- one_based_line = line_number + 1
175+ defp aliases_at ( source_file , line ) do
176+ one_based_line = line + 1
182177
183178 metadata = Parser . parse_string ( source_file . text , true , true , { one_based_line , 1 } )
184179
0 commit comments