Skip to content

Commit 5e4c2cc

Browse files
committed
fix: __MODULE__ function calls support while renaming
1 parent 0c06fa3 commit 5e4c2cc

File tree

2 files changed

+117
-17
lines changed

2 files changed

+117
-17
lines changed

lib/refactorex/refactor/function/rename_function.ex

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@ defmodule Refactorex.Refactor.Function.RenameFunction do
1212
defguardp inside_range(min_args, max_args, num_args)
1313
when min_args <= num_args and num_args <= max_args
1414

15+
defmacrop module_call(name, meta, args) do
16+
quote do: {{:., _, [{:__MODULE__, _, nil}, unquote(name)]}, unquote(meta), unquote(args)}
17+
end
18+
19+
def can_refactor?(%{node: module_call(name, meta, args)} = zipper, selection) do
20+
zipper
21+
|> Z.replace({name, meta, args})
22+
|> can_refactor?(selection)
23+
end
24+
1525
def can_refactor?(%{node: {name, meta, _}} = zipper, selection) do
1626
cond do
1727
not AST.equal?({name, meta, nil}, selection) ->
@@ -30,29 +40,42 @@ defmodule Refactorex.Refactor.Function.RenameFunction do
3040

3141
def can_refactor?(_, _), do: false
3242

43+
def refactor(%{node: module_call(_, _, _)} = zipper, selection) do
44+
zipper
45+
|> replace_module_calls()
46+
|> refactor(selection)
47+
end
48+
3349
def refactor(%{node: {name, _, _}} = zipper, _) do
34-
[definition | _] = _definitions = Function.find_definitions(zipper)
50+
[definition | _] = Function.find_definitions(zipper)
3551
{min_args, max_args} = Function.range_of_args(definition)
3652

3753
zipper
3854
|> Module.go_to_scope()
55+
|> replace_module_calls()
3956
|> Z.traverse_while(fn
40-
%{node: {:|>, _, [_, {^name, meta, args}]}} = zipper
41-
when inside_range(min_args, max_args, length(args) + 1) ->
42-
{:cont,
43-
zipper
44-
|> Z.down()
45-
|> Z.right()
46-
|> Z.replace({placeholder(), meta, args})
47-
|> Z.up()}
48-
49-
%{node: {:/, _, [{^name, meta, nil}, {:__block__, _, [num_args]}]}} = zipper
50-
when inside_range(min_args, max_args, num_args) ->
51-
{:cont,
52-
zipper
53-
|> Z.down()
54-
|> Z.replace({placeholder(), meta, nil})
55-
|> Z.up()}
57+
%{node: {:|>, _, [_, {^name, meta, args}]}} = zipper ->
58+
{:skip,
59+
if inside_range(min_args, max_args, length(args) + 1) do
60+
zipper
61+
|> Z.down()
62+
|> Z.right()
63+
|> Z.replace({placeholder(), meta, args})
64+
|> Z.up()
65+
else
66+
zipper
67+
end}
68+
69+
%{node: {:/, _, [{^name, meta, args}, {:__block__, _, [num_args]}]}} = zipper ->
70+
{:skip,
71+
if inside_range(min_args, max_args, num_args) do
72+
zipper
73+
|> Z.down()
74+
|> Z.replace({placeholder(), meta, args})
75+
|> Z.up()
76+
else
77+
zipper
78+
end}
5679

5780
# zero arity function
5881
%{node: {^name, meta, args}} = zipper
@@ -66,5 +89,28 @@ defmodule Refactorex.Refactor.Function.RenameFunction do
6689
zipper ->
6790
{:cont, zipper}
6891
end)
92+
|> bring_module_calls_back()
93+
end
94+
95+
def replace_module_calls(zipper) do
96+
Z.traverse(zipper, fn
97+
%{node: module_call(name, meta, args)} = zipper ->
98+
Z.replace(zipper, {name, Keyword.put(meta, :replaced, true), args})
99+
100+
zipper ->
101+
zipper
102+
end)
103+
end
104+
105+
def bring_module_calls_back(zipper) do
106+
Z.traverse(zipper, fn
107+
%{node: {name, meta, args}} = zipper ->
108+
if meta[:replaced],
109+
do: Z.replace(zipper, {{:., [], [{:__MODULE__, [], nil}, name]}, meta, args}),
110+
else: zipper
111+
112+
zipper ->
113+
zipper
114+
end)
69115
end
70116
end

test/refactor/function/rename_function_test.exs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,60 @@ defmodule Refactorex.Refactor.Function.RenameFunctionTest do
133133
)
134134
end
135135

136+
test "renames collapsed anonymous function" do
137+
assert_refactored(
138+
RenameFunction,
139+
"""
140+
defmodule Foo do
141+
# v
142+
def foo() do
143+
# ^
144+
&foo/0
145+
&foo/1
146+
end
147+
end
148+
""",
149+
"""
150+
defmodule Foo do
151+
def #{placeholder()}() do
152+
&#{placeholder()}/0
153+
&foo/1
154+
end
155+
end
156+
"""
157+
)
158+
end
159+
160+
test "renames function called from __MODULE__" do
161+
assert_refactored(
162+
RenameFunction,
163+
"""
164+
defmodule Foo do
165+
def foo() do
166+
# v
167+
__MODULE__.foo()
168+
# ^
169+
__MODULE__.foo(10)
170+
&__MODULE__.foo/0
171+
&__MODULE__.foo/1
172+
&__MODULE__.foo()
173+
end
174+
end
175+
""",
176+
"""
177+
defmodule Foo do
178+
def #{placeholder()}() do
179+
__MODULE__.#{placeholder()}()
180+
__MODULE__.foo(10)
181+
&__MODULE__.#{placeholder()}/0
182+
&__MODULE__.foo/1
183+
&__MODULE__.#{placeholder()}()
184+
end
185+
end
186+
"""
187+
)
188+
end
189+
136190
test "renames the selected public function with multiple definitions" do
137191
assert_refactored(
138192
RenameFunction,

0 commit comments

Comments
 (0)