Initial support for about(::Module)
Co-authored-by: Sasha Demin <demin@lix.polytechnique.fr>
This commit is contained in:
parent
4967bcd976
commit
966b91b7c0
|
@ -8,7 +8,14 @@ InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
|
|||
JuliaSyntaxHighlighting = "dc6e5ff7-fb65-4e79-a425-ec3bc9c03011"
|
||||
StyledStrings = "f489334b-da3d-4c2e-b8f0-e476e12c162b"
|
||||
|
||||
[weakdeps]
|
||||
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
|
||||
|
||||
[extensions]
|
||||
PkgExt = "Pkg"
|
||||
|
||||
[compat]
|
||||
InteractiveUtils = "1.11.0"
|
||||
JuliaSyntaxHighlighting = "1.11.0"
|
||||
Pkg = "1.11.0"
|
||||
StyledStrings = "1.11.0"
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
module PkgExt
|
||||
|
||||
using Pkg
|
||||
using StyledStrings
|
||||
import About: about_pkg, columnlist
|
||||
|
||||
function about_pkg(io::IO, pkg::Base.PkgId, mod::Module)
|
||||
isnothing(pkgversion(mod)) ||
|
||||
print(io, styled" Version {about_module:$(pkgversion(mod))}")
|
||||
srcdir = pkgdir(mod)
|
||||
if isnothing(srcdir)
|
||||
print(io, styled" (builtin)")
|
||||
else
|
||||
srcdir = Base.fixup_stdlib_path(srcdir)
|
||||
srcdir = something(Base.find_source_file(srcdir), srcdir)
|
||||
srcdir = contractuser(srcdir)
|
||||
print(io, styled" loaded from {light,underline:$srcdir}")
|
||||
end
|
||||
println(io)
|
||||
isnothing(srcdir) && return
|
||||
manifest_file = Pkg.Types.manifestfile_path(pkgdir(mod))
|
||||
thedeps = if !isnothing(manifest_file) && isfile(manifest_file)
|
||||
Pkg.Types.read_manifest(manifest_file).deps
|
||||
else
|
||||
Pkg.dependencies()
|
||||
end
|
||||
directdeps = if haskey(thedeps, pkg.uuid)
|
||||
listdeps(thedeps, pkg.uuid)
|
||||
else
|
||||
collect(keys(thedeps))
|
||||
end
|
||||
isempty(directdeps) && return
|
||||
depstrs = map(directdeps) do dep
|
||||
nindirect = length(alldeps(thedeps, dep))
|
||||
if nindirect > 0
|
||||
styled"$(thedeps[dep].name) {shadow:(+$nindirect)}"
|
||||
else
|
||||
styled"$(thedeps[dep].name)"
|
||||
end
|
||||
end
|
||||
indirect_depcount = length(alldeps(thedeps, pkg.uuid) ∪ directdeps) - length(depstrs)
|
||||
indirect_info = if indirect_depcount > 0
|
||||
styled" {shadow:(+$indirect_depcount indirectly)}"
|
||||
else styled"" end
|
||||
println(io, styled"\n{bold:Directly depends on {emphasis:$(length(directdeps))} \
|
||||
package$(ifelse(length(directdeps) == 1, \"\", \"s\"))}$indirect_info:")
|
||||
columnlist(io, depstrs)
|
||||
end
|
||||
|
||||
function listdeps(deps::Dict{Base.UUID, Pkg.Types.PackageEntry}, pkg::Base.UUID)
|
||||
if haskey(deps, pkg)
|
||||
collect(values(deps[pkg].deps))
|
||||
else
|
||||
Base.UUID[]
|
||||
end
|
||||
end
|
||||
|
||||
function listdeps(deps::Dict{Base.UUID, Pkg.API.PackageInfo}, pkg::Base.UUID)
|
||||
if haskey(deps, pkg)
|
||||
collect(values(deps[pkg].dependencies))
|
||||
else
|
||||
Base.UUID[]
|
||||
end
|
||||
end
|
||||
|
||||
function alldeps(deps::Dict{Base.UUID, <:Union{Pkg.Types.PackageEntry, Pkg.API.PackageInfo}}, pkg::Base.UUID)
|
||||
depcheck = listdeps(deps, pkg)
|
||||
depcollection = Set{Base.UUID}()
|
||||
while !isempty(depcheck)
|
||||
id = popfirst!(depcheck)
|
||||
id in depcollection && continue
|
||||
append!(depcheck, listdeps(deps, id))
|
||||
push!(depcollection, id)
|
||||
end
|
||||
collect(depcollection)
|
||||
end
|
||||
|
||||
end
|
11
src/utils.jl
11
src/utils.jl
|
@ -21,6 +21,9 @@ function cpad(s, n::Integer, pad::Union{AbstractString, AbstractChar}=' ', r::Ro
|
|||
rpad(lpad(s, div(n+textwidth(s), 2, r), pad), n, pad)
|
||||
end
|
||||
|
||||
splural(n::Int) = ifelse(n == 1, "", "s")
|
||||
splural(c::Vector) = splural(length(c))
|
||||
|
||||
function struncate(str::AbstractString, maxwidth::Int, joiner::AbstractString = "…", mode::Symbol = :center)
|
||||
textwidth(str) <= maxwidth && return str
|
||||
left, right = firstindex(str) - 1, lastindex(str) + 1
|
||||
|
@ -41,16 +44,18 @@ end
|
|||
function columnlist(io::IO, entries::Vector{<:AbstractString};
|
||||
maxcols::Int=8, maxwidth::Int=last(displaysize(io)),
|
||||
prefix::AbstractString = S"{emphasis:•} ", spacing::Int=2)
|
||||
isempty(entries) && return
|
||||
thecolumns = Vector{eltype(entries)}[]
|
||||
thecolwidths = Int[]
|
||||
for ncols in 1:maxcols
|
||||
columns = Vector{eltype(entries)}[]
|
||||
for col in Iterators.partition(entries, length(entries) ÷ ncols)
|
||||
for col in Iterators.partition(entries, div(length(entries), ncols, RoundUp))
|
||||
push!(columns, collect(col))
|
||||
end
|
||||
widths = map.(textwidth, columns)
|
||||
colwidths = map(maximum, widths)
|
||||
if sum(colwidths) + ncols * textwidth(prefix) + (1 - ncols) * spacing > maxwidth
|
||||
layoutwidth = sum(colwidths) + ncols * textwidth(prefix) + (ncols - 1) * spacing
|
||||
if layoutwidth > maxwidth
|
||||
break
|
||||
else
|
||||
thecolumns, thecolwidths = columns, colwidths
|
||||
|
@ -115,6 +120,8 @@ function wraplines(content::Union{Annot, SubString{<:Annot}}, width::Integer = 8
|
|||
most_recent_break_opportunity = lastwrap + nextbreak
|
||||
end
|
||||
i = most_recent_break_opportunity
|
||||
else
|
||||
i = nextind(s, most_recent_break_opportunity)
|
||||
end
|
||||
push!(lines, content[nextind(s, lastwrap):prevind(s, most_recent_break_opportunity)])
|
||||
lastwrap = most_recent_break_opportunity
|
||||
|
|
|
@ -110,6 +110,82 @@ function memorylayout(io::IO, value::T) where {T}
|
|||
memorylayout(io, T)
|
||||
end
|
||||
|
||||
# ------------------
|
||||
# Modules
|
||||
# ------------------
|
||||
|
||||
function about(io::IO, mod::Module)
|
||||
pkg = nothing
|
||||
for (bpkg, m) in Base.loaded_modules
|
||||
if m == mod
|
||||
pkg = bpkg
|
||||
break
|
||||
end
|
||||
end
|
||||
!isnothing(pkg) && !applicable(about_pkg, io, pkg, mod) &&
|
||||
Base.require(Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg"))
|
||||
print(io, S"{bold:Module {about_module:$mod}}")
|
||||
if !isnothing(pkg)
|
||||
println(io, S" {shadow:[$(something(pkg.uuid, \"no uuid\"))]}")
|
||||
Base.invokelatest(about_pkg, io, pkg, mod)
|
||||
else
|
||||
println(io)
|
||||
end
|
||||
function classify(m::Module, name::Symbol)
|
||||
val = getglobal(mod, name)
|
||||
order, kind, face, parent = if val isa Module
|
||||
0, :module, :about_module, val
|
||||
elseif val isa Function && first(String(name)) == '@'
|
||||
1, :macro, :julia_macro, parentmodule(val)
|
||||
elseif val isa Function
|
||||
2, :function, :julia_funcall, parentmodule(val)
|
||||
elseif val isa Type
|
||||
3, :type, :julia_type, if val isa UnionAll || val isa Union
|
||||
m else parentmodule(val) end
|
||||
else
|
||||
4, :value, :julia_identifier, if Base.issingletontype(typeof(val))
|
||||
parentmodule(typeof(m))
|
||||
else
|
||||
m
|
||||
end
|
||||
end
|
||||
while parentmodule(parent) ∉ (parent, Main)
|
||||
parent = parentmodule(parent)
|
||||
end
|
||||
(; name, str = S"{code,$face:$name}", kind, parent, order)
|
||||
end
|
||||
classify(m::Module, names::Vector{Symbol}) =
|
||||
sort(map(Base.Fix1(classify, m), names), by=x->x.order)
|
||||
allnames = classify(mod, names(mod))
|
||||
exports = similar(allnames, 0)
|
||||
reexports = similar(allnames, 0)
|
||||
publics = similar(allnames, 0)
|
||||
for exp in allnames
|
||||
if exp.parent === mod && Base.isexported(mod, exp.name)
|
||||
push!(exports, exp)
|
||||
elseif exp.parent === mod && Base.ispublic(mod, exp.name)
|
||||
push!(publics, exp)
|
||||
elseif exp.parent !== mod
|
||||
push!(reexports, exp)
|
||||
end
|
||||
end
|
||||
if !isempty(exports)
|
||||
println(io, S"\n{bold:Exports {emphasis:$(length(exports))} name$(splural(exports)):}")
|
||||
columnlist(io, map(x->x.str, exports))
|
||||
end
|
||||
if !isempty(reexports)
|
||||
parents = join(sort(map(p->S"{about_module:$p}", unique(map(x->x.parent, reexports)))), ", ")
|
||||
println(io, S"\n{bold:Re-exports {emphasis:$(length(reexports))} name$(splural(reexports))} (from $parents){bold::}")
|
||||
columnlist(io, map(x->x.str, reexports))
|
||||
end
|
||||
if !isempty(publics)
|
||||
println(io, S"\n{bold:Public API ({emphasis:$(length(publics))} name$(splural(publics))):}")
|
||||
columnlist(io, map(x->x.str, publics))
|
||||
end
|
||||
end
|
||||
|
||||
function about_pkg end # Implemented in `../ext/PkgExt.jl`
|
||||
|
||||
# ------------------
|
||||
# Numeric types
|
||||
# ------------------
|
||||
|
@ -375,7 +451,4 @@ function elaboration(io::IO, char::Char)
|
|||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
# TODO struct
|
||||
|
|
Loading…
Reference in New Issue