@@ -6,12 +6,12 @@ CodeTracking can be thought of as an extension of InteractiveUtils, and pairs we
66- `definition`: a lower-level variant of the above
77- `pkgfiles`: return information about the source files that define a package
88- `whereis`: Return location information about methods (with Revise, it updates as you edit files)
9- - `signatures_at`: return the signatures of all methods whose definition spans the specified location
9+ - `signatures_at`: return the signatures (and the corresponding method table) of all methods whose definition spans the specified location
1010"""
1111module CodeTracking
1212
1313using Base: PkgId
14- using Core: LineInfoNode
14+ using Core: LineInfoNode, MethodTable
1515using Base. Meta: isexpr
1616using UUIDs
1717using InteractiveUtils
@@ -29,12 +29,15 @@ include("utils.jl")
2929
3030# These values get populated by Revise
3131
32- # `method_info[sig]` is either:
32+ # `method_info[mt => sig]` is either:
3333# - `missing`, to indicate that the method cannot be located
3434# - a list of `(lnn,ex)` pairs. In almost all cases there will be just one of these,
3535# but "mistakes" in moving methods from one file to another can result in more than
36- # definition. The last pair in the list is the currently-active definition.
37- const method_info = IdDict{Type,Union{Missing,Vector{Tuple{LineNumberNode,Expr}}}}()
36+ # one definition. The last pair in the list is the currently-active definition.
37+
38+ const MethodInfoKey = Pair{Union{Nothing, MethodTable}, Type}
39+
40+ const method_info = IdDict{MethodInfoKey,Union{Missing,Vector{Tuple{LineNumberNode,Expr}}}}()
3841
3942const _pkgfiles = Dict {PkgId,PkgFiles} ()
4043
@@ -51,32 +54,33 @@ const method_lookup_callback = Ref{Any}(nothing)
5154# id is the PkgId of the corresponding package
5255# relpath is the path of the file from the basedir of `id`
5356# mod is the "active" module at that point in the source
54- # exsigs is a ex=>sigs dictionary, where `ex` is the source expression and `sigs `
55- # a list of method-signatures defined by that expression.
57+ # exsigs is a `ex => mt_sigs` dictionary, where `ex` is the source expression and `mt_sigs `
58+ # a list of `method_table => signature` pairs defined by that expression.
5659const expressions_callback = Ref {Any} (nothing )
5760
5861const juliabase = joinpath (" julia" , " base" )
5962const juliastdlib = joinpath (" julia" , " stdlib" , " v$(VERSION . major) .$(VERSION . minor) " )
6063
64+ method_table (method:: Method ) = isdefined (method, :external_mt ) ? method. external_mt:: MethodTable : nothing
65+ MethodInfoKey (method:: Method ) = MethodInfoKey (method_table (method), method. sig)
66+
6167# ## Public API
6268
6369"""
6470 filepath, line = whereis(method::Method)
6571
66- Return the file and line of the definition of `method`. The meaning of `line`
67- depends on the Julia version: on Julia 1.5 and higher it is the line number of
68- the method declaration, otherwise it is the first line of the method's body.
72+ Return the file and line of the definition of `method`.
6973"""
7074function whereis (method:: Method )
7175 file, line = String (method. file), method. line
7276 startswith (file, " REPL[" ) && return file, line
73- lin = get (method_info, method. sig , nothing )
77+ lin = get (method_info, MethodInfoKey ( method) , nothing )
7478 if lin === nothing
7579 f = method_lookup_callback[]
7680 if f != = nothing
7781 try
7882 Base. invokelatest (f, method)
79- lin = get (method_info, method. sig , nothing )
83+ lin = get (method_info, MethodInfoKey ( method) , nothing )
8084 catch
8185 end
8286 end
@@ -134,11 +138,9 @@ function whereis(lineinfo::Core.LineInfoNode, method::Method)
134138end
135139
136140"""
137- sigs = signatures_at(filename, line)
141+ mt_sigs = signatures_at(filename, line)
138142
139- Return the signatures of all methods whose definition spans the specified location.
140- Prior to Julia 1.5, `line` must correspond to a line in the method body
141- (not the signature or final `end`).
143+ Return the `method_table => signature` pairs of all methods whose definition spans the specified location.
142144
143145Returns `nothing` if there are no methods at that location.
144146"""
@@ -175,11 +177,11 @@ function signatures_at(filename::AbstractString, line::Integer)
175177end
176178
177179"""
178- sigs = signatures_at(mod::Module, relativepath, line)
180+ mt_sigs = signatures_at(mod::Module, relativepath, line)
179181
180- For a package that defines module `mod`, return the signatures of all methods whose definition
181- spans the specified location. `relativepath` indicates the path of the file relative to
182- the packages top-level directory, e.g., `"src/utils.jl"`.
182+ For a package that defines module `mod`, return the `method_table => signature` pairs of
183+ all methods whose definition spans the specified location. `relativepath` indicates the
184+ path of the file relative to the packages top-level directory, e.g., `"src/utils.jl"`.
183185`line` must correspond to a line in the method body (not the signature or final `end`).
184186
185187Returns `nothing` if there are no methods at that location.
@@ -194,10 +196,10 @@ function signatures_at(id::PkgId, relpath::AbstractString, line::Integer)
194196 expressions === nothing && error (" cannot look up methods by line number, try `using Revise` before loading other packages" )
195197 try
196198 for (mod, exsigs) in Base. invokelatest (expressions, id, relpath)
197- for (ex, sigs ) in exsigs
199+ for (ex, mt_sigs ) in exsigs
198200 lr = linerange (ex)
199201 lr === nothing && continue
200- line ∈ lr && return sigs
202+ line ∈ lr && return mt_sigs
201203 end
202204 end
203205 catch
@@ -298,13 +300,13 @@ See also [`code_expr`](@ref).
298300"""
299301function definition (:: Type{Expr} , method:: Method )
300302 file = String (method. file)
301- def = startswith (file, " REPL[" ) ? nothing : get (method_info, method. sig , nothing )
303+ def = startswith (file, " REPL[" ) ? nothing : get (method_info, MethodInfoKey ( method) , nothing )
302304 if def === nothing
303305 f = method_lookup_callback[]
304306 if f != = nothing
305307 try
306308 Base. invokelatest (f, method)
307- def = get (method_info, method. sig , nothing )
309+ def = get (method_info, MethodInfoKey ( method) , nothing )
308310 catch
309311 end
310312 end
0 commit comments