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
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ julia> definition(m, String)
"red(c::AbstractRGB ) = c.r\n"
```

or to find the method-signatures at a particular location:

```julia
julia> signatures_at(ColorTypes, "src/traits.jl", 14)
1-element Array{Any,1}:
Tuple{typeof(red),AbstractRGB}

julia> signatures_at("/home/tim/.julia/packages/ColorTypes/BsAWO/src/traits.jl", 14)
1-element Array{Any,1}:
Tuple{typeof(red),AbstractRGB}
```

## A few details

CodeTracking won't do anything *useful* unless the user is also running Revise,
Expand Down
46 changes: 45 additions & 1 deletion src/CodeTracking.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ using Base: PkgId
using Core: LineInfoNode
using UUIDs

export whereis, definition, pkgfiles
export whereis, definition, pkgfiles, signatures_at

include("pkgfiles.jl")
include("utils.jl")
Expand All @@ -18,6 +18,7 @@ const method_info = IdDict{Type,Tuple{LineNumberNode,Expr}}()
const _pkgfiles = Dict{PkgId,PkgFiles}()

const method_lookup_callback = Ref{Any}(nothing)
const expressions_callback = Ref{Any}(nothing)

### Public API

Expand Down Expand Up @@ -68,7 +69,50 @@ function whereis(lineinfo, method::Method)
return file, lineinfo.line-method.line+line1
end

"""
sigs = signatures_at(filename, line)

Return the signatures of all methods whose definition spans the specified location.
`line` must correspond to a line in the method body (not the signature or final `end`).

Returns `nothing` if there are no methods at that location.
"""
function signatures_at(filename::AbstractString, line::Integer)
for (id, pkgfls) in _pkgfiles
if startswith(filename, basedir(pkgfls))
return signatures_at(id, relpath(filename, basedir(pkgfls)), line)
end
end
error("$filename not found, perhaps the package is not loaded")
end

"""
sigs = signatures_at(mod::Module, relativepath, line)

For a package that defines module `mod`, return the signatures of all methods whose definition
spans the specified location. `relativepath` indicates the path of the file relative to
the packages top-level directory, e.g., `"src/utils.jl"`.
`line` must correspond to a line in the method body (not the signature or final `end`).

Returns `nothing` if there are no methods at that location.
"""
function signatures_at(mod::Module, relpath::AbstractString, line::Integer)
id = PkgId(mod)
return signatures_at(id, relpath, line)
end

function signatures_at(id::PkgId, relpath::AbstractString, line::Integer)
expressions = expressions_callback[]
expressions === nothing && error("cannot look up methods by line number, try `using Revise` before loading other packages")
for (mod, exsigs) in Base.invokelatest(expressions, id, relpath)
for (ex, sigs) in exsigs
lr = linerange(ex)
lr === nothing && continue
line ∈ lr && return sigs
end
end
return nothing
end

"""
src = definition(method::Method, String)
Expand Down
20 changes: 20 additions & 0 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,26 @@ function isfuncexpr(ex)
return false
end

function linerange(def::Expr)
start, haslinestart = findline(def, identity)
stop, haslinestop = findline(def, Iterators.reverse)
(haslinestart & haslinestop) && return start:stop
return nothing
end
linerange(arg) = linerange(convert(Expr, arg)) # Handle Revise's RelocatableExpr

function findline(ex, order)
ex.head == :line && return ex.args[1], true
for a in order(ex.args)
a isa LineNumberNode && return a.line, true
if a isa Expr
ln, hasline = findline(a, order)
hasline && return ln, true
end
end
return 0, false
end

fileline(lin::LineInfoNode) = String(lin.file), lin.line
fileline(lnn::LineNumberNode) = String(lnn.file), lnn.line

Expand Down
2 changes: 2 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Note: some of CodeTracking's functionality can only be tested by Revise

using CodeTracking
using Test
# Note: ColorTypes needs to be installed, but note the intentional absence of `using ColorTypes`
Expand Down