Skip to content

Commit e2da219

Browse files
committed
global refactoring
1 parent 65826db commit e2da219

File tree

2 files changed

+88
-39
lines changed

2 files changed

+88
-39
lines changed

src/refactor.jl

Lines changed: 60 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,8 @@ function refactor(
1919
column = 1, row = 1, startrow = 0, context = "",
2020
mod = "Main",
2121
)
22-
expr = CSTParser.parse(context, true)
23-
bind = CSTParser.bindingof(expr)
24-
2522
# local refactor only if `old` is really a local binding
23+
bind = CSTParser.bindingof(CSTParser.parse(context))
2624
if bind === nothing || old != bind.name
2725
try
2826
refactored = localrefactor(old, new, path, column, row, startrow, context)
@@ -33,52 +31,75 @@ function refactor(
3331
end
3432

3533
try
36-
refactored = localrefactor(old, new, path, column, row, startrow, context)
37-
isempty(refactored) || return Dict(:text => refactored)
34+
mod = getmodule(mod)
35+
val = getfield′(mod, old)
36+
result = globalrefactor(old, new, mod, val)
37+
return result isa String ? Dict(:error => result) : Dict(:error => false)
3838
catch err
3939
@error err
4040
end
4141

42-
# try
43-
# globalrefactor(old, new, path, mod) && return nothing
44-
# catch err
45-
# @error err
46-
# end
47-
48-
return Dict(:error => true, :msg => "no refactor")
42+
return Dict(:error => "Rename refactoring failed: `$old` -> `$new`")
4943
end
5044

51-
function localrefactor(old, new, path, column, row, startrow, context)
52-
old = first(split(old, '.')) # ignore dot accessors
53-
position = row - startrow
45+
# local refactor
46+
# --------------
5447

55-
return if old map(l -> l[:name], locals(context, position, column))
48+
function localrefactor(old, new, path, column, row, startrow, context)
49+
return if old map(l -> l[:name], locals(context, row - startrow, column))
5650
oldsym = Symbol(old)
57-
quote
58-
MacroTools.textwalk($context) do ex
59-
@capture(ex, $oldsym) ? Symbol($new) : ex
60-
end
61-
end |> eval
51+
newsym = Symbol(new)
52+
MacroTools.textwalk(context) do sym
53+
sym === oldsym ? newsym : sym
54+
end
6255
else
6356
""
6457
end
6558
end
6659

67-
# mod = getmodule(m)
68-
# parentfile, modulefiles = modulefiles(mod)
69-
# sourcewalk("../packages/Atom/src/goto.jl") do x
70-
# isshort = MacroTools.isshortdef(x)
71-
# ex = MacroTools.shortdef(x)
72-
# if @capture(ex, locals(args__) = body_)
73-
# return if isshort
74-
# :(newlocals(args...) = body)
75-
# else
76-
# :(function newlocals(args...)
77-
# body
78-
# end)
79-
# end
80-
# end
81-
# return x
82-
# isstruct = MacroTools.isstructdef(x)
83-
# if @capture(x, struct )
84-
# end
60+
# global refactor
61+
# ---------------
62+
63+
globalrefactor(old, new, mod, @nospecialize(val)) = _globalrefactor(old, new, mod) # general case
64+
function globalrefactor(old, new, mod, val::Undefined)
65+
Symbol(old) in keys(Docs.keywords) ?
66+
"Keywords can't be renamed: `$old`" :
67+
_globalrefactor(old, new, mod)
68+
end
69+
70+
function _globalrefactor(old, new, mod)
71+
entrypath, line = moduledefinition(mod)
72+
files = modulefiles(entrypath)
73+
74+
with_logger(JunoProgressLogger()) do
75+
refactorfiles(old, new, mod, files)
76+
end
77+
end
78+
79+
function refactorfiles(old, new, obj, files)
80+
id = "global_rename_refactor_progress"
81+
@info "Start global rename refactoring" progress=0 _id=id
82+
83+
oldsym = Symbol(old)
84+
newsym = Symbol(new)
85+
modulesyms = Set(Symbol.(Base.loaded_modules_array()))
86+
total = length(files)
87+
88+
for (i, file) enumerate(files)
89+
@info "Refactoring: $file ($i / $total)" progress=i/total _id=id
90+
MacroTools.sourcewalk(file) do ex
91+
return if ex === oldsym
92+
newsym
93+
elseif @capture(ex, obj_.$oldsym)
94+
if obj in modulesyms
95+
@warn "Came across a global rename refactoring across different modules: `$obj.$old` -> `$obj.$new`"
96+
end
97+
Expr(:., obj, newsym)
98+
else
99+
ex
100+
end
101+
end
102+
end
103+
104+
@info "Finish global rename refactoring" progress=1 _id=id
105+
end

src/utils.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,34 @@ function modulefiles(mod::Module)
204204
return fixpath(parentfile), [fixpath(mf[2]) for mf in included_files]
205205
end
206206

207+
"""
208+
included_files = modulefiles(entrypath::String)::Vector{String}
209+
210+
Return all the file paths that can be reached via [`include`](@ref) calls.
211+
Note this function currently only looks for static _toplevel_ calls.
212+
"""
213+
function modulefiles(entrypath::String, files = [])
214+
push!(files, entrypath)
215+
216+
text = read(entrypath, String)
217+
parsed = CSTParser.parse(text, true)
218+
items = toplevelitems(parsed, text)
219+
220+
for item in items
221+
if item isa ToplevelCall
222+
expr = item.expr
223+
if isinclude(expr)
224+
nextfile = expr.args[3].val
225+
nextentrypath = joinpath(dirname(entrypath), nextfile)
226+
isfile(nextentrypath) || continue
227+
modulefiles(nextentrypath, files)
228+
end
229+
end
230+
end
231+
232+
return files
233+
end
234+
207235
function moduledefinition(mod::Module) # NOTE: added when adapted
208236
evalmethod = first(methods(getfield(mod, :eval)))
209237
parentfile = String(evalmethod.file)

0 commit comments

Comments
 (0)