Skip to content

Commit aecdfa7

Browse files
committed
Fix renaming functions with multiple headers
1 parent 6bd7de3 commit aecdfa7

File tree

2 files changed

+67
-33
lines changed

2 files changed

+67
-33
lines changed

apps/language_server/lib/language_server/providers/rename.ex

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ defmodule ElixirLS.LanguageServer.Providers.Rename do
66
"""
77

88
alias ElixirLS.LanguageServer.SourceFile
9-
#import ElixirLS.LanguageServer.Protocol
109

1110
def rename(%SourceFile{} = source_file, start_uri, line, character, new_name) do
1211
edits =
@@ -15,23 +14,24 @@ defmodule ElixirLS.LanguageServer.Providers.Rename do
1514
%ElixirSense.Location{} = definition <-
1615
ElixirSense.definition(source_file.text, line, character),
1716
references <- ElixirSense.references(source_file.text, line, character) do
18-
19-
2017
length_old = length(char_ident)
2118

22-
[
23-
%{
24-
uri: start_uri,
25-
range:
26-
adjust_range(
27-
definition.line,
28-
definition.column,
29-
definition.line,
30-
definition.column + length_old
19+
definition_references =
20+
case definition do
21+
%{type: :function} ->
22+
parse_definition_source_code(definition, source_file.text)
23+
|> get_all_fn_header_positions(char_ident)
24+
|> positions_to_references(start_uri, length_old)
25+
26+
_ ->
27+
positions_to_references(
28+
[{definition.line, definition.column}],
29+
start_uri,
30+
length_old
3131
)
32-
}
33-
| repack_references(references, start_uri)
34-
]
32+
end
33+
34+
definition_references ++ repack_references(references, start_uri)
3535
else
3636
_ ->
3737
[]
@@ -62,7 +62,9 @@ defmodule ElixirLS.LanguageServer.Providers.Rename do
6262
begin: {start_line, start_col},
6363
end: {end_line, end_col},
6464
context: {context, char_ident}
65-
} when context in [:local_or_var, :local_call] <- Code.Fragment.surround_context(source_file.text, {line, character}) do
65+
}
66+
when context in [:local_or_var, :local_call] <-
67+
Code.Fragment.surround_context(source_file.text, {line, character}) do
6668
%{
6769
range: adjust_range(start_line, start_col, end_line, end_col),
6870
placeholder: to_string(char_ident)
@@ -91,6 +93,36 @@ defmodule ElixirLS.LanguageServer.Providers.Rename do
9193
end
9294
end
9395

96+
defp parse_definition_source_code(definition, source_text)
97+
98+
defp parse_definition_source_code(%{file: nil}, source_text) do
99+
ElixirSense.Core.Parser.parse_string(source_text, true, true, 0)
100+
end
101+
102+
defp parse_definition_source_code(%{file: file}, _) do
103+
ElixirSense.Core.Parser.parse_file(file, true, true, 0)
104+
end
105+
106+
defp get_all_fn_header_positions(parsed_source, char_ident) do
107+
parsed_source.mods_funs_to_positions
108+
|> Map.filter(fn
109+
{{_, fn_name, _}, _} -> Atom.to_charlist(fn_name) == char_ident
110+
end)
111+
|> Enum.flat_map(fn {_, %{positions: positions}} -> positions end)
112+
|> Enum.uniq()
113+
end
114+
115+
defp positions_to_references(header_positions, start_uri, length_old)
116+
when is_list(header_positions) do
117+
header_positions
118+
|> Enum.map(fn {line, column} ->
119+
%{
120+
uri: start_uri,
121+
range: adjust_range(line, column, line, column + length_old)
122+
}
123+
end)
124+
end
125+
94126
defp adjust_range(start_line, start_character, end_line, end_character) do
95127
%{
96128
start: %{line: start_line - 1, character: start_character - 1},

apps/language_server/test/providers/rename_test.exs

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -132,18 +132,15 @@ defmodule ElixirLS.LanguageServer.Providers.RenameTest do
132132
]
133133
} == changes
134134
end
135-
end
136135

137-
describe "not yet (fully) supported/working renaming cases" do
138-
test "rename started with cursor at function definition" do
136+
test "rename function with multiple heads: handle_error -> handle_errors" do
139137
file_path = FixtureHelpers.get_path("rename_example.exs")
140138
text = File.read!(file_path)
141139
uri = SourceFile.path_to_uri(file_path)
142140

143-
# defp _handle_error({:ok, message})
144-
{line, char} = {4, 8}
141+
{line, char} = {29, 8}
145142

146-
assert {:ok, %{"documentChanges" => changes}} =
143+
assert {:ok, %{"documentChanges" => [changes]}} =
147144
Rename.rename(
148145
%SourceFile{text: text, version: 0},
149146
uri,
@@ -152,37 +149,39 @@ defmodule ElixirLS.LanguageServer.Providers.RenameTest do
152149
"handle_errors"
153150
)
154151

155-
refute %{
152+
assert %{
156153
"textDocument" => %{
157154
"uri" => uri,
158155
"version" => 1
159156
},
160157
"edits" => [
161158
%{
162159
"newText" => "handle_errors",
163-
"range" => %{end: %{character: 19, line: 37}, start: %{character: 7, line: 37}}
160+
"range" => %{end: %{character: 19, line: 39}, start: %{character: 7, line: 39}}
164161
},
165162
%{
166163
"newText" => "handle_errors",
167-
"range" => %{end: %{character: 19, line: 39}, start: %{character: 7, line: 39}}
164+
"range" => %{end: %{character: 19, line: 37}, start: %{character: 7, line: 37}}
168165
},
169166
%{
170167
"newText" => "handle_errors",
171168
"range" => %{end: %{character: 19, line: 28}, start: %{character: 7, line: 28}}
172169
}
173170
]
174-
} == List.first(changes)
171+
} == changes
175172
end
173+
end
176174

177-
test "rename function with multiple heads" do
175+
describe "not yet (fully) supported/working renaming cases" do
176+
test "rename started with cursor at function definition" do
178177
file_path = FixtureHelpers.get_path("rename_example.exs")
179178
text = File.read!(file_path)
180179
uri = SourceFile.path_to_uri(file_path)
181180

182-
# |> _handle_error
183-
{line, char} = {29, 8}
181+
# defp _handle_error({:ok, message})
182+
{line, char} = {4, 8}
184183

185-
assert {:ok, %{"documentChanges" => [changes]}} =
184+
assert {:ok, %{"documentChanges" => changes}} =
186185
Rename.rename(
187186
%SourceFile{text: text, version: 0},
188187
uri,
@@ -191,8 +190,7 @@ defmodule ElixirLS.LanguageServer.Providers.RenameTest do
191190
"handle_errors"
192191
)
193192

194-
# missed second function head on line 40: handle_error({:error, changeset})
195-
assert %{
193+
refute %{
196194
"textDocument" => %{
197195
"uri" => uri,
198196
"version" => 1
@@ -202,12 +200,16 @@ defmodule ElixirLS.LanguageServer.Providers.RenameTest do
202200
"newText" => "handle_errors",
203201
"range" => %{end: %{character: 19, line: 37}, start: %{character: 7, line: 37}}
204202
},
203+
%{
204+
"newText" => "handle_errors",
205+
"range" => %{end: %{character: 19, line: 39}, start: %{character: 7, line: 39}}
206+
},
205207
%{
206208
"newText" => "handle_errors",
207209
"range" => %{end: %{character: 19, line: 28}, start: %{character: 7, line: 28}}
208210
}
209211
]
210-
} == changes
212+
} == List.first(changes)
211213
end
212214
end
213215
end

0 commit comments

Comments
 (0)