Skip to content

Commit e4d7ee4

Browse files
committed
global refactor only from its definition
1 parent bf977e5 commit e4d7ee4

File tree

1 file changed

+61
-26
lines changed

1 file changed

+61
-26
lines changed

src/refactor.jl

Lines changed: 61 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,27 @@ function renamerefactor(
2222
)
2323
mod = getmodule(mod)
2424

25-
# catch field renaming
25+
# check on dot accessor
2626
modnote = if (obj = first(split(full, '.'))) != old
27-
if (parentmod = getfield′(mod, obj)) isa Module && parentmod != mod
28-
modulenote(old, parentmod)
27+
if (parent = getfield′(mod, obj)) isa Module
28+
if parent != mod
29+
modulenote(old, parent)
30+
else
31+
""
32+
end
2933
else
34+
# catch field renaming
3035
return Dict(:warning => "Rename refactoring on a field isn't available: `$obj.$old`")
3136
end
3237
else
3338
""
3439
end
3540

36-
# local refactor only if `old` is really a local binding
37-
bind = CSTParser.bindingof(CSTParser.parse(context))
38-
if bind === nothing || old != bind.name
41+
expr = CSTParser.parse(context)
42+
bind = CSTParser.bindingof(expr)
43+
44+
# local rename refactor if `old` isn't a toplevel binding
45+
if bind === nothing || old bind.name
3946
try
4047
refactored = localrefactor(old, new, path, column, row, startrow, context)
4148
isempty(refactored) || return Dict(
@@ -47,31 +54,52 @@ function renamerefactor(
4754
end
4855
end
4956

57+
# global rename refactor if the local rename refactor didn't happen
5058
try
5159
val = getfield′(mod, full)
60+
5261
# catch keyword renaming
5362
if val isa Undefined && Symbol(old) in keys(Docs.keywords)
5463
return Dict(:warning => "Keywords can't be renamed: `$old`")
5564
end
56-
# update modnote
57-
if isempty(modnote) && applicable(parentmodule, val) && (parentmod = parentmodule(val)) != mod
58-
modnote = modulenote(old, parentmod)
65+
66+
# catch global refactoring not on definition, e.g.: on a call site
67+
if bind === nothing || old bind.name
68+
# TODO: `goto` uri
69+
return Dict(:info => contextnote(old, mod, context))
5970
end
60-
kind, desc = globalrefactor(old, new, mod)
61-
return Dict(
62-
kind => kind === :success ?
63-
join(("_Global_ rename refactoring `$old` ⟹ `$new` succeeded.", modnote, desc), "\n\n") :
64-
desc
65-
)
71+
72+
kind, desc = globalrefactor(old, new, mod, expr)
73+
74+
# make description
75+
if kind === :success
76+
# update modnote
77+
if isempty(modnote) && applicable(parentmodule, val) && (parent = parentmodule(val)) mod
78+
modnote = modulenote(old, parent)
79+
end
80+
81+
desc = join(("_Global_ rename refactoring `$old` ⟹ `$new` succeeded.", modnote, desc), "\n\n")
82+
end
83+
84+
return Dict(kind => desc)
6685
catch err
6786
@error err
6887
end
6988

7089
return Dict(:error => "Rename refactoring `$old` ⟹ `$new` failed")
7190
end
7291

73-
modulenote(old, parentmod) =
74-
"**NOTE**: `$old` is defined in `$parentmod` -- you may need the same rename refactorings in that module as well."
92+
modulenote(old, parentmod) = """
93+
**NOTE**: `$old` is defined in `$parentmod`
94+
-- you may need the same rename refactorings in that module as well.
95+
"""
96+
97+
contextnote(old, mod, context) = """
98+
`$old` isn't found in local bindings in the current context:
99+
<details><summary>Context</summary><pre><code>$(strip(context))</code></p></details>
100+
101+
If you want a global rename refactoring on `$mod.$old`, you need to call from its definition.
102+
"""
75103

76104
# local refactor
77105
# --------------
@@ -91,35 +119,42 @@ end
91119
# global refactor
92120
# ---------------
93121

94-
function globalrefactor(old, new, mod)
122+
function globalrefactor(old, new, mod, expr)
95123
entrypath, line = moduledefinition(mod)
96124
files = modulefiles(entrypath)
97125

98126
with_logger(JunoProgressLogger()) do
99-
refactorfiles(old, new, mod, files)
127+
refactorfiles(old, new, mod, files, expr)
100128
end
101129
end
102130

103-
function refactorfiles(old, new, mod, files)
104-
id = "global_rename_refactor_progress"
105-
@info "Start global rename refactoring" progress=0 _id=id
131+
function refactorfiles(old, new, mod, files, expr)
132+
ismacro = CSTParser.defines_macro(expr)
133+
oldsym = ismacro ? Symbol("@" * old) : Symbol(old)
134+
newsym = ismacro ? Symbol("@" * new) : Symbol(new)
106135

107-
oldsym = Symbol(old)
108-
newsym = Symbol(new)
109136
total = length(files)
110-
111137
# TODO: enable line location information (the upstream needs to be enhanced)
112138
refactoredfiles = Set{String}()
113139

140+
id = "global_rename_refactor_progress"
141+
@info "Start global rename refactoring" progress=0 _id=id
142+
114143
for (i, file) enumerate(files)
115144
@info "Refactoring: $file ($i / $total)" progress=i/total _id=id
145+
116146
MacroTools.sourcewalk(file) do ex
117-
return if ex === oldsym
147+
if ex === oldsym
118148
push!(refactoredfiles, fullpath(file))
119149
newsym
150+
# handle dot (module) accessor
120151
elseif @capture(ex, m_.$oldsym) && getfield′(mod, Symbol(m)) isa Module
121152
push!(refactoredfiles, fullpath(file))
122153
Expr(:., m, newsym)
154+
# macro case
155+
elseif ismacro && @capture(ex, macro $(Symbol(old))(args__) body_ end)
156+
push!(refactoredfiles, fullpath(file))
157+
Expr(:macro, :($(Symbol(new))($(args...))), :($body))
123158
else
124159
ex
125160
end

0 commit comments

Comments
 (0)