Skip to content

Commit

Permalink
Implement signatures_at
Browse files Browse the repository at this point in the history
Closes #3
  • Loading branch information
timholy committed Mar 3, 2019
1 parent 16b061a commit 6178cb8
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 1 deletion.
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
19 changes: 19 additions & 0 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,25 @@ 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

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

0 comments on commit 6178cb8

Please sign in to comment.