Skip to content

Commit 55cbac6

Browse files
authored
Merge pull request #29 from timholy/teh/fix_REPL
Avoid abspath for REPL-defined "paths"
2 parents d832e9f + de31a82 commit 55cbac6

File tree

4 files changed

+53
-9
lines changed

4 files changed

+53
-9
lines changed

.travis.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ notifications:
2020
script:
2121
- export JULIA_PROJECT=""
2222
- julia --project -e 'using Pkg; Pkg.build(); Pkg.test();'
23+
- julia -e 'using Pkg; Pkg.develop(PackageSpec(path="."));
24+
Pkg.add(PackageSpec(url="https://github.com/timholy/Revise.jl"));
25+
Pkg.test("Revise")'
2326

2427
after_success:
2528
- julia -e 'import Pkg; Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())'

src/CodeTracking.jl

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,9 @@ Return the signatures of all methods whose definition spans the specified locati
114114
Returns `nothing` if there are no methods at that location.
115115
"""
116116
function signatures_at(filename::AbstractString, line::Integer)
117-
filename = abspath(filename)
117+
if !startswith(filename, "REPL[")
118+
filename = abspath(filename)
119+
end
118120
if occursin(juliabase, filename)
119121
rpath = postpath(filename, juliabase)
120122
id = PkgId(Base)
@@ -186,17 +188,18 @@ see [`definition(Expr, method::Method)`](@ref) instead.
186188
"""
187189
function definition(::Type{String}, method::Method)
188190
file, line = whereis(method)
189-
src = read(file, String)
191+
src = src_from_file_or_REPL(file)
190192
eol = isequal('\n')
191193
linestarts = Int[]
192-
istart = 0
194+
istart = 1
193195
for i = 1:line-1
194-
push!(linestarts, istart+1)
195-
istart = findnext(eol, src, istart+1)
196+
push!(linestarts, istart)
197+
istart = findnext(eol, src, istart) + 1
196198
end
197199
ex, iend = Meta.parse(src, istart)
198200
if isfuncexpr(ex)
199-
return src[istart+1:iend-1], line
201+
iend = min(iend, lastindex(src))
202+
return strip(src[istart:iend], '\n'), line
200203
end
201204
# The function declaration was presumably on a previous line
202205
lineindex = lastindex(linestarts)

src/utils.jl

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,35 @@ fileline(lnn::LineNumberNode) = String(lnn.file), lnn.line
3939
# This is piracy, but it's not ambiguous in terms of what it should do
4040
Base.convert(::Type{LineNumberNode}, lin::LineInfoNode) = LineNumberNode(lin.line, lin.file)
4141

42+
# This regex matches the pseudo-file name of a REPL history entry.
43+
const rREPL = r"^REPL\[(\d+)\]$"
44+
45+
"""
46+
src = src_from_file_or_REPL(origin::AbstractString, repl = Base.active_repl)
47+
48+
Read the source for a function from `origin`, which is either the name of a file
49+
or "REPL[\$i]", where `i` is an integer specifying the particular history entry.
50+
Methods defined at the REPL use strings of this form in their `file` field.
51+
52+
If you happen to have a file where the name matches `REPL[\$i]`, first pass it through
53+
`abspath`.
54+
"""
55+
function src_from_file_or_REPL(origin::AbstractString, args...)
56+
# This Varargs design prevents an unnecessary error when Base.active_repl is undefined
57+
# and `origin` does not match "REPL[$i]"
58+
m = match(rREPL, origin)
59+
if m !== nothing
60+
return _src_from_file_or_REPL(origin, args...)
61+
end
62+
return read(origin, String)
63+
end
64+
65+
function _src_from_file_or_REPL(origin::AbstractString, repl = Base.active_repl)
66+
hist_idx = parse(Int, m.captures[1])
67+
hp = repl.interface.modes[1].hist
68+
return hp.history[hp.start_idx+hist_idx]
69+
end
70+
4271
function basepath(id::PkgId)
4372
id.name ("Main", "Base", "Core") && return ""
4473
loc = Base.locate_package(id)

test/runtests.jl

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@ isdefined(Main, :Revise) ? includet("script.jl") : include("script.jl")
3131

3232
m = first(methods(f2))
3333
src, line = definition(String, m)
34-
@test src == """
35-
f2(x, y) = x + y
36-
"""
34+
@test src == "f2(x, y) = x + y"
3735
@test line == 7
3836

3937
info = CodeTracking.PkgFiles(Base.PkgId(CodeTracking))
@@ -62,6 +60,17 @@ isdefined(Main, :Revise) ? includet("script.jl") : include("script.jl")
6260
# Test with broken lookup
6361
CodeTracking.method_lookup_callback[] = m -> error("oops")
6462
@test whereis(m) == ("REPL[1]", 1)
63+
# Test with definition(String, m)
64+
if isdefined(Base, :active_repl)
65+
hp = Base.active_repl.interface.modes[1].hist
66+
fstr = "__fREPL__(x::Int16) = 0"
67+
histidx = length(hp.history) + 1 - hp.start_idx
68+
ex = Base.parse_input_line(fstr; filename="REPL[$histidx]")
69+
f = Core.eval(Main, ex)
70+
push!(hp.history, fstr)
71+
@test definition(String, first(methods(f))) == (fstr, 1)
72+
pop!(hp.history)
73+
end
6574

6675
m = first(methods(Test.eval))
6776
@test occursin(Sys.STDLIB, whereis(m)[1])

0 commit comments

Comments
 (0)