Skip to content

Commit 4b21660

Browse files
rename knows about def/defp/test
1 parent 4d2d304 commit 4b21660

File tree

6 files changed

+90
-30
lines changed

6 files changed

+90
-30
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
0.1.4
22
* ability to run rename
33
* better tests
4+
* rename knows about def/defp/test
45

56
0.1.3
67
* case insensitive sorting

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,14 @@ alias Foo.Bar.{Baz, Boom}
6363
> mix refactor rename [FILE] [LINE] [COLUMN]
6464
```
6565

66-
Renames a variable (currently this is a very, very naive implementation)
66+
Renames a variable (currently this is a fairly naive implementation - issues welcome!)
6767

6868

6969
More to come...
7070

7171
----
7272

73-
## Running against a codebase
73+
## Running against an entire codebase
7474

7575
Find is your friend
7676

lib/exacto_knife/refactorings/rename.ex

+19-7
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,35 @@ defmodule ExactoKnife.Refactorings.Rename do
1212
IO.puts("Couldn't find a variable at #{inspect(cursor)}.")
1313
zipper
1414

15-
{{current_name, _, _}, _} ->
16-
rename_instances(zipper, current_name, String.to_atom(new_name))
15+
{{current_name, _, _}, _} = pos_zipper ->
16+
scope = Ze.find_up(pos_zipper, &lexical_scope?/1) || zipper
17+
rename_instances(scope, current_name, String.to_atom(new_name))
1718
end
1819
end
1920

2021
def rename_instances(zipper, current_name, new_name) do
2122
zipper
2223
|> Z.traverse(fn
23-
{{marker, _, _} = node, _} = z when is_atom(marker) ->
24-
if marker == current_name do
25-
Z.replace(z, put_elem(node, 0, new_name))
26-
else
27-
z
24+
{{marker, _, _} = node, _} = z ->
25+
cond do
26+
marker == current_name ->
27+
Z.replace(z, put_elem(node, 0, new_name))
28+
29+
# skip renaming inside of other scopes
30+
Ze.safe_node(zipper) != Ze.safe_node(z) && lexical_scope?(node) ->
31+
Z.skip(z)
32+
33+
true ->
34+
z
2835
end
2936

3037
z ->
3138
z
3239
end)
3340
end
41+
42+
def lexical_scope?({:def, _, _}), do: true
43+
def lexical_scope?({:defp, _, _}), do: true
44+
def lexical_scope?({:test, _, _}), do: true
45+
def lexical_scope?(_), do: false
3446
end

lib/exacto_knife/refactorings/zipper_extensions.ex

+10-3
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ defmodule ExactoKnife.Refactorings.ZipperExtensions do
88
defdelegate zip(quoted), to: Z
99
defdelegate next(z), to: Z
1010

11-
def find_back(nil, _f), do: nil
11+
def find_up(nil, _f), do: nil
1212

13-
def find_back({node, _} = zipper, f) do
13+
def find_up({node, _} = zipper, f) do
1414
if f.(node) do
1515
zipper
1616
else
17-
find_back(Z.prev(zipper), f)
17+
find_up(Z.up(zipper), f)
1818
end
1919
end
2020

@@ -38,4 +38,11 @@ defmodule ExactoKnife.Refactorings.ZipperExtensions do
3838
# returns node, or nil if there is no node/zipper
3939
def safe_node({n, _}), do: n
4040
def safe_node(nil), do: nil
41+
42+
def compact(zipper) do
43+
Z.traverse(zipper, fn
44+
{{marker, _, children}, _} = z -> Z.replace(z, {marker, [], children})
45+
z -> z
46+
end)
47+
end
4148
end

test/exacto_knife/refactorings/rename_test.exs

+35-15
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ defmodule ExactoKnife.Refactorings.RenameTest do
22
@moduledoc false
33
use ExactoKnife.TestCase, async: true
44

5+
alias ExactoKnife.Refactorings.Rename
6+
57
import ExUnit.CaptureIO
68

79
describe "rename/1" do
@@ -56,7 +58,6 @@ defmodule ExactoKnife.Refactorings.RenameTest do
5658
""" == refactor(:rename, source, [{8, 17}, "food"])
5759
end
5860

59-
@tag :skip
6061
test "should only change instances in a function" do
6162
source = ~S"""
6263
bar = 3
@@ -72,20 +73,6 @@ defmodule ExactoKnife.Refactorings.RenameTest do
7273
end
7374
"""
7475

75-
assert ~S"""
76-
bomb = 3
77-
78-
def foo() do
79-
bar = 2
80-
baz = bar * 2
81-
end
82-
83-
defp food() do
84-
bar = 2
85-
baz = bar * 2
86-
end
87-
""" == refactor(:rename, source, [{1, 2}, "bomb"])
88-
8976
assert ~S"""
9077
bar = 3
9178
@@ -114,5 +101,38 @@ defmodule ExactoKnife.Refactorings.RenameTest do
114101
end
115102
""" == refactor(:rename, source, [{9, 3}, "bomb"])
116103
end
104+
105+
test "should not change instances in a named function when variable changes outside" do
106+
source = ~S"""
107+
bar = 3
108+
109+
def foo() do
110+
bar = 2
111+
baz = bar * 2
112+
end
113+
"""
114+
115+
assert ~S"""
116+
bomb = 3
117+
118+
def foo() do
119+
bar = 2
120+
baz = bar * 2
121+
end
122+
""" == refactor(:rename, source, [{1, 2}, "bomb"])
123+
end
124+
end
125+
126+
describe "#lexical_scope?/1" do
127+
test "finds def, defp, test" do
128+
assert Rename.lexical_scope?(build("def foo() do\nend"))
129+
assert Rename.lexical_scope?(build("defp foo() do\nend"))
130+
assert Rename.lexical_scope?(build("test \"bob\" do\nend"))
131+
end
132+
133+
test "not other stuff" do
134+
refute Rename.lexical_scope?(5)
135+
refute Rename.lexical_scope?(build("foo"))
136+
end
117137
end
118138
end

test/exacto_knife/refactorings/zipper_extensions_test.exs

+23-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ defmodule ExactoKnife.Refactorings.ZipperExtensionsTest do
55
alias ExactoKnife.Refactorings.ZipperExtensions, as: Ze
66
alias Sourceror.Zipper, as: Z
77

8-
describe "#find_back/2" do
8+
describe "#find" do
99
test "finds a thing" do
1010
z = Z.zip([1, [2, [3, 4]], 5, 6])
1111

@@ -14,8 +14,28 @@ defmodule ExactoKnife.Refactorings.ZipperExtensionsTest do
1414
assert nil == z |> Z.find(&(&1 == 10))
1515
assert {3, %{l: nil, r: [4]}} = z |> Z.find(&(&1 == 3))
1616
assert {5, %{r: [6]}} = at_5
17-
assert {2, %{r: [[3, 4]]}} = at_5 |> Ze.find_back(&(&1 == 2))
18-
assert nil == at_5 |> Ze.find_back(&(&1 == 20))
17+
assert {2, %{r: [[3, 4]]}} = at_5 |> Z.find(:prev, &(&1 == 2))
18+
assert nil == at_5 |> Z.find(:prev, &(&1 == 20))
19+
end
20+
end
21+
22+
describe "#find_up/2" do
23+
test "finds a thing" do
24+
z = Z.zip([1, [2, [3, 4]], 5, 6])
25+
26+
at_3 = z |> Z.find(&(&1 == 3))
27+
28+
first_is? = fn i ->
29+
fn
30+
list when is_list(list) -> Enum.at(list, 0) == i
31+
_ -> false
32+
end
33+
end
34+
35+
assert nil == at_3 |> Ze.find_up(first_is?.(4))
36+
assert nil == at_3 |> Ze.find_up(first_is?.(5))
37+
assert at_3 |> Ze.find_up(first_is?.(1))
38+
assert at_3 |> Ze.find_up(first_is?.(2))
1939
end
2040
end
2141

0 commit comments

Comments
 (0)