Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion src/CodeTracking.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,25 @@ const method_info = IdDict{Type,Union{Missing,Tuple{LineNumberNode,Expr}}}()

const _pkgfiles = Dict{PkgId,PkgFiles}()

# Callback for method-lookup. `lookupfunc = method_lookup_callback[]` must have the form
# ret = lookupfunc(method)
# where `ret` is either `nothing` or `(lnn, def)`. `lnn` is a LineNumberNode (or any valid
# input to `CodeTracking.fileline`) and `def` is the expression defining the method.
const method_lookup_callback = Ref{Any}(nothing)
const expressions_callback = Ref{Any}(nothing)

# Callback for `signatures_at` (lookup by file/lineno). `lookupfunc = expressions_callback[]`
# must have the form
# mod, exsigs = lookupfunc(id, relpath)
# where
# id is the PkgId of the corresponding package
# relpath is the path of the file from the basedir of `id`
# mod is the "active" module at that point in the source
# exsigs is a ex=>sigs dictionary, where `ex` is the source expression and `sigs`
# a list of method-signatures defined by that expression.
const expressions_callback = Ref{Any}(nothing)

const juliabase = joinpath("julia", "base")
const juliastdlib = joinpath("julia", "stdlib", "v$(VERSION.major).$(VERSION.minor)")

### Public API

Expand Down Expand Up @@ -97,6 +114,22 @@ Return the signatures of all methods whose definition spans the specified locati
Returns `nothing` if there are no methods at that location.
"""
function signatures_at(filename::AbstractString, line::Integer)
if occursin(juliabase, filename)
rpath = postpath(filename, juliabase)
id = PkgId(Base)
return signatures_at(id, rpath, line)
elseif occursin(juliastdlib, filename)
rpath = postpath(filename, juliastdlib)
spath = splitpath(rpath)
libname = spath[1]
project = Base.active_project()
id = PkgId(Base.project_deps_get(project, libname), libname)
return signatures_at(id, joinpath(spath[2:end]...), line)
end
if startswith(filename, "REPL[")
id = PkgId("@REPL")
return signatures_at(id, filename, line)
end
for (id, pkgfls) in _pkgfiles
if startswith(filename, basedir(pkgfls)) || id.name == "Main"
bdir = basedir(pkgfls)
Expand Down
8 changes: 8 additions & 0 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,11 @@ function maybe_fixup_stdlib_path(path)
end
return path
end

function postpath(filename, pre)
idx = findfirst(pre, filename)
idx === nothing && error(pre, " not found in ", filename)
post = filename[first(idx) + length(pre) : end]
post[1:1] == Base.Filesystem.path_separator && return post[2:end]
return post
end
14 changes: 14 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,17 @@ include("script.jl")
file, line = whereis(lin, m)
@test endswith(file, String(lin.file))
end

@testset "With Revise" begin
if isdefined(Main, :Revise)
m = @which gcd(10, 20)
sigs = signatures_at(Base.find_source_file(String(m.file)), m.line)
@test !isempty(sigs)

m = first(methods(edit))
sigs = signatures_at(String(m.file), m.line)
@test !isempty(sigs)
sigs = signatures_at(Base.find_source_file(String(m.file)), m.line)
@test !isempty(sigs)
end
end