Skip to content

Commit 987456e

Browse files
committed
Implement rename across multiple files
1 parent 1daf1c8 commit 987456e

File tree

4 files changed

+76
-12
lines changed

4 files changed

+76
-12
lines changed

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

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,25 @@ defmodule ElixirLS.LanguageServer.Providers.Rename do
99

1010
def rename(%SourceFile{} = source_file, start_uri, line, character, new_name) do
1111
edits =
12-
with %{context: {context, char_ident}} when context in [:local_or_var, :local_call] <-
13-
Code.Fragment.surround_context(source_file.text, {line, character}),
12+
with char_ident when not is_nil(char_ident) <-
13+
get_char_ident(source_file.text, line, character),
1414
%ElixirSense.Location{} = definition <-
1515
ElixirSense.definition(source_file.text, line, character),
1616
references <- ElixirSense.references(source_file.text, line, character) do
1717
length_old = length(char_ident)
1818

1919
definition_references =
2020
case definition do
21-
%{type: :function} ->
22-
parse_definition_source_code(definition, source_file.text)
21+
%{file: nil, type: :function} ->
22+
parse_definition_source_code(source_file.text)
2323
|> get_all_fn_header_positions(char_ident)
2424
|> positions_to_references(start_uri, length_old)
2525

26+
%{file: separate_file_path, type: :function} ->
27+
parse_definition_source_code(definition)
28+
|> get_all_fn_header_positions(char_ident)
29+
|> positions_to_references(SourceFile.path_to_uri(separate_file_path), length_old)
30+
2631
_ ->
2732
positions_to_references(
2833
[{definition.line, definition.column}],
@@ -93,14 +98,12 @@ defmodule ElixirLS.LanguageServer.Providers.Rename do
9398
end
9499
end
95100

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)
101+
defp parse_definition_source_code(%{file: file}) do
102+
ElixirSense.Core.Parser.parse_file(file, true, true, 0)
100103
end
101104

102-
defp parse_definition_source_code(%{file: file}, _) do
103-
ElixirSense.Core.Parser.parse_file(file, true, true, 0)
105+
defp parse_definition_source_code(source_text) when is_binary(source_text) do
106+
ElixirSense.Core.Parser.parse_string(source_text, true, true, 0)
104107
end
105108

106109
defp get_all_fn_header_positions(parsed_source, char_ident) do
@@ -129,4 +132,12 @@ defmodule ElixirLS.LanguageServer.Providers.Rename do
129132
end: %{line: end_line - 1, character: end_character - 1}
130133
}
131134
end
135+
136+
defp get_char_ident(text, line, character) do
137+
case Code.Fragment.surround_context(text, {line, character}) do
138+
%{context: {context, char_ident}} when context in [:local_or_var, :local_call] -> char_ident
139+
%{context: {:dot, _, char_ident}} -> char_ident
140+
_ -> nil
141+
end
142+
end
132143
end

apps/language_server/test/providers/rename_test.exs

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ defmodule ElixirLS.LanguageServer.Providers.RenameTest do
112112
assert sort_edit_by_start_line(edits) == expected_edits
113113
end
114114

115-
test "rename function with multiple heads: handle_error -> handle_errors" do
115+
test "rename function with multiple heads: add -> new_add" do
116116
file_path = FixtureHelpers.get_path("rename_example.ex")
117117
text = File.read!(file_path)
118118
uri = SourceFile.path_to_uri(file_path)
@@ -142,6 +142,56 @@ defmodule ElixirLS.LanguageServer.Providers.RenameTest do
142142

143143
assert sort_edit_by_start_line(edits) == expected_edits
144144
end
145+
146+
test "rename function defined in a different file ten -> new_ten" do
147+
file_path = FixtureHelpers.get_path("rename_example.ex")
148+
text = File.read!(file_path)
149+
uri = SourceFile.path_to_uri(file_path)
150+
151+
fn_definition_file_uri =
152+
FixtureHelpers.get_path("rename_example_b.ex") |> SourceFile.path_to_uri()
153+
154+
# b = ElixirLS.Test.RenameExampleB.ten()
155+
{line, char} = {4, 38}
156+
157+
assert {:ok,
158+
%{
159+
"documentChanges" => [
160+
%{
161+
"textDocument" => %{
162+
"uri" => ^uri,
163+
"version" => 1
164+
},
165+
"edits" => file_edits
166+
},
167+
%{
168+
"textDocument" => %{
169+
"uri" => ^fn_definition_file_uri,
170+
"version" => 1
171+
},
172+
"edits" => fn_definition_file_edits
173+
}
174+
]
175+
}} =
176+
Rename.rename(
177+
%SourceFile{text: text, version: 0},
178+
uri,
179+
line,
180+
char,
181+
"new_ten"
182+
)
183+
184+
expected_edits_fn_definition_file =
185+
[%{line: 1, start_char: 6, end_char: 9}] |> get_expected_edits("new_ten")
186+
187+
assert sort_edit_by_start_line(fn_definition_file_edits) ==
188+
expected_edits_fn_definition_file
189+
190+
expected_edits = [%{line: 3, start_char: 37, end_char: 40}] |> get_expected_edits("new_ten")
191+
192+
assert sort_edit_by_start_line(file_edits) ==
193+
expected_edits
194+
end
145195
end
146196

147197
describe "not yet (fully) supported/working renaming cases" do

apps/language_server/test/support/fixtures/rename_example.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
defmodule ElixirLS.Test.RenameExample do
22
def main do
33
a = 5
4-
b = 10
4+
b = ElixirLS.Test.RenameExampleB.ten()
55
c = add(a, b)
66
d = subtract(a, b)
77
add(c, d)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
defmodule ElixirLS.Test.RenameExampleB do
2+
def ten, do: 10
3+
end

0 commit comments

Comments
 (0)