Skip to content

Commit 5b337ef

Browse files
committed
various refactors
- add TODO comment to refactor module-detection strategy - add tests for current module-detection approaches - improve utility tests
1 parent 7908500 commit 5b337ef

6 files changed

Lines changed: 158 additions & 58 deletions

File tree

src/goto.jl

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,6 @@ function searchtoplevelitems(mod::Module, text::String, path::Nothing)
165165
return pathitemsmaps
166166
end
167167

168-
# TODO:
169-
# use the module detection logic below for general module auto-detection,
170-
# e.g.: `module` handler and such
171-
172168
# sub entry method
173169
function _searchtoplevelitems(mod::Module, pathitemsmaps::PathItemsMaps)
174170
entrypath, paths = modulefiles(mod) # Revise-like approach

src/refactor.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ function _globalrenamerefactor(old, new, mod, expr, files)
166166
if ex === oldsym
167167
push!(modifiedfiles, fullpath(file))
168168
newsym
169-
# handle dot (module) accessor
169+
# handle dot accessor
170170
elseif @capture(ex, m_.$oldsym) && getfield′(mod, Symbol(m)) isa Module
171171
push!(modifiedfiles, fullpath(file))
172172
Expr(:., m, newsym)

src/utils.jl

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -230,13 +230,36 @@ urigoto(mod, word) = "atom://julia-client/?goto=true&mod=$(mod)&word=$(word)"
230230
urimoduleinfo(mod) = "atom://julia-client/?moduleinfo=true&mod=$(mod)"
231231

232232
#=
233-
module file detections
233+
utilities to find module files
234234
235235
adapted from https://github.com/timholy/Revise.jl/tree/b0c5c864ea78b93caaa820cb9cfc45eca47f43ff
236+
237+
# TODO:
238+
# use the module detection logic below for general module auto-detection instead of CodeTools.jl
239+
# e.g.: `module` handler and such
236240
=#
237241

238242
using Base: PkgId, UUID
239243

244+
"""
245+
entrypath, line = moduledefinition(mod::Module)
246+
247+
Returns an entry file of `mod`, and its definition line.
248+
249+
!!! note
250+
This function works for non-precompiled packages.
251+
"""
252+
function moduledefinition(mod::Module) # NOTE: added when adapted
253+
evalmethod = first(methods(getfield(mod, :eval)))
254+
parentfile = String(evalmethod.file)
255+
line = evalmethod.line
256+
id = Base.PkgId(mod)
257+
if id.name == "Base" || id.name == "Core" || Symbol(id.name) stdlib_names # NOTE: "Core" is added when adapted
258+
parentfile = normpath(Base.find_source_file(parentfile))
259+
end
260+
fixpath(parentfile), line
261+
end
262+
240263
"""
241264
parentfile, included_files = modulefiles(mod::Module)
242265
@@ -261,13 +284,16 @@ function modulefiles(mod::Module)
261284
return fixpath(parentfile), [fixpath(mf[2]) for mf in included_files]
262285
end
263286

287+
# TODO: excludes files in submodules
288+
# TODO: looks for non-toplevel `include` calls
264289
"""
265290
included_files = modulefiles(entrypath::String)::Vector{String}
266291
267-
Return all the file paths that can be reached via [`include`](@ref) calls.
268-
Note this function currently only looks for static _toplevel_ calls.
292+
Returns all the files that can be reached via [`include`](@ref) calls from `entrypath`.
293+
Note this function currently only looks for static toplevel calls (i.e. miss the calls
294+
in not in toplevel scope), and can include files in the submodules as well.
269295
"""
270-
function modulefiles(entrypath::String, files = [])
296+
function modulefiles(entrypath::String, files = Vector{String}())
271297
isfile′(entrypath) || return files
272298

273299
push!(files, entrypath)
@@ -291,17 +317,6 @@ function modulefiles(entrypath::String, files = [])
291317
return files
292318
end
293319

294-
function moduledefinition(mod::Module) # NOTE: added when adapted
295-
evalmethod = first(methods(getfield(mod, :eval)))
296-
parentfile = String(evalmethod.file)
297-
line = evalmethod.line
298-
id = Base.PkgId(mod)
299-
if id.name == "Base" || id.name == "Core" || Symbol(id.name) stdlib_names # NOTE: "Core" is added when adapted
300-
parentfile = normpath(Base.find_source_file(parentfile))
301-
end
302-
fixpath(parentfile), line
303-
end
304-
305320
# Fix paths to files that define Julia (base and stdlibs)
306321
function fixpath(
307322
filename::AbstractString;

test/goto.jl

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
@testset "goto symbols" begin
2-
using Atom: modulegotoitems, realpath′, toplevelgotoitems, SYMBOLSCACHE,
2+
using Atom: modulegotoitems, toplevelgotoitems, SYMBOLSCACHE,
33
regeneratesymbols, methodgotoitems, globalgotoitems
44
using CSTParser
55

@@ -51,19 +51,18 @@
5151

5252
@testset "module goto" begin
5353
let item = modulegotoitems("Atom", Main)[1]
54-
@test item.file == realpath′(joinpath(@__DIR__, "..", "src", "Atom.jl"))
54+
@test item.file == joinpath′(atomjldir, "Atom.jl")
5555
@test item.line == 3
5656
end
5757
let item = modulegotoitems("Junk2", Main.Junk)[1]
58-
@test item.file == joinpath(@__DIR__, "fixtures", "Junk.jl")
58+
@test item.file == joinpath(junkpath)
5959
@test item.line == 14
6060
end
6161
end
6262

6363
@testset "goto toplevel symbols" begin
6464
## where Revise approach works, i.e.: precompiled modules
65-
let dir = realpath′(joinpath(@__DIR__, "..", "src"))
66-
path = joinpath(dir, "comm.jl")
65+
let path = joinpath′(atomjldir, "comm.jl")
6766
text = read(path, String)
6867
mod = Atom
6968
key = "Atom"
@@ -79,23 +78,8 @@
7978
# check caching works
8079
@test haskey(SYMBOLSCACHE, key)
8180

82-
# check the Revise-like approach finds all the included files
83-
let numfiles = 0
84-
debuggerpath = realpath′(joinpath(@__DIR__, "..", "src", "debugger"))
85-
profilerpath = realpath′(joinpath(@__DIR__, "..", "src", "profiler"))
86-
for (d, ds, fs) walkdir(dir)
87-
if d (debuggerpath, profilerpath)
88-
numfiles += 1 # debugger.jl / traceur.jl (in Atom module)
89-
continue
90-
end
91-
for f fs
92-
if endswith(f, ".jl") # .jl check is needed for travis, who creates hoge.cov files
93-
numfiles += 1
94-
end
95-
end
96-
end
97-
@test length(SYMBOLSCACHE[key]) == numfiles
98-
end
81+
# check the Revise-like approach finds all files in Atom module
82+
@test length(SYMBOLSCACHE[key]) == length(atommodfiles)
9983

10084
# when `path` isn't given, i.e. via docpane / workspace
10185
let items = toplevelgotoitems(word, mod, "", nothing) .|> Dict
@@ -106,11 +90,18 @@
10690

10791
# same as above, but without any previous cache -- falls back to CSTPraser-based module-walk
10892
delete!(SYMBOLSCACHE, key)
93+
10994
let items = toplevelgotoitems(word, mod, "", nothing) .|> Dict
11095
@test !isempty(items)
11196
@test items[1][:file] == path
11297
@test items[1][:text] == word
11398
end
99+
100+
# check CSTPraser-based module-walk finds all the included files
101+
# currently broken:
102+
# - files in submodules are included
103+
# - webio.jl is excluded since `include("webio.jl")` is a toplevel call
104+
@test_broken length(SYMBOLSCACHE[key]) == length(atommoddir)
114105
end
115106

116107
## where the Revise-like approach doesn't work, e.g. non-precompiled modules
@@ -143,15 +134,15 @@
143134
# handle dot accessors gracefully
144135
let
145136
# can access the non-exported (non-method) bindings in the other module
146-
path = realpath′(joinpath(@__DIR__, "..", "src", "goto.jl"))
137+
path = joinpath′(@__DIR__, "..", "src", "goto.jl")
147138
text = read(@__FILE__, String)
148139
items = Dict.(toplevelgotoitems("Atom.SYMBOLSCACHE", Main, text, @__FILE__))
149140
@test !isempty(items)
150141
@test items[1][:file] == path
151142
@test items[1][:text] == "SYMBOLSCACHE"
152143

153144
# handle if a module is duplicated
154-
path = realpath′(joinpath(@__DIR__, "..", "src", "comm.jl"))
145+
path = joinpath′(@__DIR__, "..", "src", "comm.jl")
155146
text = read(path, String)
156147
items = Dict.(toplevelgotoitems("Atom.handlers", Atom, text, path))
157148
@test !isempty(items)

test/runtests.jl

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,50 @@ using Lazy
44
import JSON
55

66

7+
joinpath′(files...) = Atom.fullpath(joinpath(files...))
8+
9+
atomjldir = joinpath′(@__DIR__, "..", "src")
10+
11+
webiofile = joinpath′(atomjldir, "display", "webio.jl")
12+
13+
# files in `Atom` module (except files in its submodules)
14+
atommodfiles = let
15+
files = []
16+
debuggerdir = joinpath′(atomjldir, "debugger")
17+
profilerdir = joinpath′(atomjldir, "profiler")
18+
for (d, ds, fs) in walkdir(atomjldir)
19+
# NOTE: update directories below when you create an new submodule
20+
# the 2 files below are in Atom module
21+
if d == debuggerdir
22+
push!(files, joinpath′(d, "debugger.jl"))
23+
continue
24+
end
25+
if d == profilerdir
26+
push!(files, joinpath′(d, "profiler.jl"))
27+
push!(files, joinpath′(d, "traceur.jl"))
28+
continue
29+
end
30+
for f in fs
31+
# NOTE: currently both Revise-like and CSTPraser-based approach fails
32+
# to detect display/webio.jl as a file in Atom module
33+
f == "webio.jl" && continue
34+
35+
# .jl check is needed for travis, who creates hoge.cov files
36+
endswith(f, ".jl") && push!(files, joinpath′(d, f))
37+
end
38+
end
39+
files
40+
end
41+
742
# mock a listener
843
Core.eval(Atom, Meta.parse("sock = IOBuffer()"))
944
readmsg() = JSON.parse(String(take!(Atom.sock)))
1045

1146
# mock Module
12-
junkpath = joinpath(@__DIR__, "fixtures", "Junk.jl")
47+
junkpath = joinpath(@__DIR__, "fixtures", "Junk.jl")
1348
include(junkpath)
1449

50+
1551
include("./misc.jl") # basics
1652
include("./utils.jl")
1753
include("./eval.jl")

test/utils.jl

Lines changed: 75 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ end
4848
@test Atom.finddevpackages() isa AbstractDict
4949
end
5050

51-
#TODO: baselink, edit
51+
# TODO: baselink, edit
5252

5353
cd(old_pwd)
5454
end
@@ -79,18 +79,80 @@ end
7979
end
8080
end
8181

82-
@testset "limiting excessive strings" begin
83-
using Atom: strlimit
82+
@testset "is utilities" begin
83+
@testset "iskeyword" begin
84+
using Atom: iskeyword
8485

85-
# only including ASCII
86-
@test strlimit("julia", 5) == "julia"
87-
@test strlimit("julia", 4) == "jul…"
88-
@test strlimit("Julia in the Nutshell", 21, " ...") == "Julia in the Nutshell"
89-
@test strlimit("Julia in the Nutshell", 20, " ...") == "Julia in the Nut ..."
86+
@test iskeyword(:begin)
87+
@test iskeyword("begin")
88+
@test !iskeyword(:iskeyword)
89+
end
90+
end
91+
92+
@testset "string utilities" begin
93+
@testset "limiting excessive strings" begin
94+
using Atom: strlimit
95+
96+
# only including ASCII
97+
@test strlimit("julia", 5) == "julia"
98+
@test strlimit("julia", 4) == "jul…"
99+
@test strlimit("Julia in the Nutshell", 21, " ...") == "Julia in the Nutshell"
100+
@test strlimit("Julia in the Nutshell", 20, " ...") == "Julia in the Nut ..."
101+
102+
# including Unicode: should respect _length_ of strings, not code units
103+
@test strlimit("jμλια", 5) == "jμλια"
104+
@test strlimit("jμλια", 4) == "jμλ…"
105+
@test strlimit("Jμλια in the Nutshell", 21, " ...") == "Jμλια in the Nutshell"
106+
@test strlimit("Jμλια in the Nutshell", 20, " ...") == "Jμλια in the Nut ..."
107+
end
108+
end
90109

91-
# including Unicode: should respect _length_ of strings, not code units
92-
@test strlimit("jμλια", 5) == "jμλια"
93-
@test strlimit("jμλια", 4) == "jμλ…"
94-
@test strlimit("Jμλια in the Nutshell", 21, " ...") == "Jμλια in the Nutshell"
95-
@test strlimit("Jμλια in the Nutshell", 20, " ...") == "Jμλια in the Nut ..."
110+
@testset "module utilties" begin
111+
import Atom: moduledefinition, modulefiles
112+
113+
@testset "find module definiton location" begin
114+
let (path, line) = moduledefinition(Atom)
115+
@test path == joinpath′(@__DIR__, "..", "src", "Atom.jl")
116+
@test line == 4
117+
end
118+
let (path, line) = moduledefinition(Junk)
119+
@test path == joinpath′(@__DIR__, "fixtures", "Junk.jl")
120+
@test line == 1
121+
end
122+
let (path, line) = moduledefinition(Junk.Junk2)
123+
@test path == joinpath′(@__DIR__, "fixtures", "Junk.jl")
124+
@test line == 15
125+
end
126+
end
127+
128+
@testset "find module files" begin
129+
## Revise-like module file detection
130+
# works for precompiled packages
131+
let (parentfile, included_files) = modulefiles(Atom)
132+
expected = Set(atommodfiles)
133+
actual = Set((parentfile, included_files...))
134+
@test actual == expected
135+
136+
# can't detect display/webio.jl
137+
@test_broken webiofile in modfiles
138+
end
139+
140+
# fails for non-precompiled packages
141+
@test_broken junkpath == modulefiles(Junk)[1]
142+
143+
## CSTPraser-based module file detection
144+
let included_files = normpath.(modulefiles(joinpath′(atomjldir, "Atom.jl")))
145+
# finds all the files in Atom module except display/webio.jl
146+
for f in atommodfiles
147+
f == webiofile && continue
148+
@test f in included_files
149+
end
150+
151+
# can't exclude files in the submodules
152+
@test_broken length(atommodfiles) == length(included_files)
153+
154+
# can't look for non-toplevel `include` calls
155+
@test_broken webiofile in included_files
156+
end
157+
end
96158
end

0 commit comments

Comments
 (0)