Skip to content

Commit

Permalink
Rebase and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
thofma committed Sep 9, 2018
1 parent 0f6a96e commit b79d0d2
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 175 deletions.
173 changes: 0 additions & 173 deletions base/docs/Docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -254,179 +254,6 @@ function getdoc end
getdoc(@nospecialize(x), @nospecialize(sig)) = getdoc(x)
getdoc(@nospecialize(x)) = nothing

"""
Docs.doc(binding, sig)
Return all documentation that matches both `binding` and `sig`.
If `getdoc` returns a non-`nothing` result on the value of the binding, then a
dynamic docstring is returned instead of one based on the binding itself.
"""
function doc(binding::Binding, sig::Type = Union{})
if defined(binding)
result = getdoc(resolve(binding), sig)
result === nothing || return result
end
results, groups, sigs = DocStr[], MultiDoc[], Any[]
# Lookup `binding` and `sig` for matches in all modules of the docsystem.
for mod in modules
dict = meta(mod)
if haskey(dict, binding)
multidoc = dict[binding]
push!(groups, multidoc)
for msig in multidoc.order
if (isconcrete(sig) && sig <: msig) || (!isconcrete(sig) && msig <: sig) || sig == Union{}
push!(sigs, msig)
push!(results, multidoc.docs[msig])
end
end
end
end
if isempty(groups)
# When no `MultiDoc`s are found that match `binding` then we check whether `binding`
# is an alias of some other `Binding`. When it is we then re-run `doc` with that
# `Binding`, otherwise if it's not an alias then we generate a summary for the
# `binding` and display that to the user instead.
alias = aliasof(binding)
alias == binding ? summarize(alias, sig) : doc(alias, sig)
else
# There was at least one match for `binding` while searching. If there weren't any
# matches for `sig` then we concatenate *all* the docs from the matching `Binding`s.
if isempty(results)
for group in groups, each in group.order
push!(results, group.docs[each])
end
elseif sig != Union{}
# If the signature is abstract, only show more specific methods
morespec = [x <: sig && x != Union{} for x in sigs]
if !any(morespec)
# If not any method is more specific that sig, show the most specific one
results = DocStr[results[first(sortperm(sigs, lt = (a,b) -> a <: b))]]
else
results = results[morespec]
end
end
# Get parsed docs and concatenate them.
md = catdoc(map(parsedoc, results)...)
# Save metadata in the generated markdown.
if isa(md, Markdown.MD)
md.meta[:results] = results
md.meta[:binding] = binding
md.meta[:typesig] = sig
end
return md
end
end

# Some additional convenience `doc` methods that take objects rather than `Binding`s.
doc(obj::UnionAll) = doc(Base.unwrap_unionall(obj))
doc(object, sig::Type = Union{}) = doc(aliasof(object, typeof(object)), sig)
doc(object, sig...) = doc(object, Tuple{sig...})

"""
Docs.fielddoc(binding, field)
Return documentation for a particular `field` of a type if it exists.
"""
function fielddoc(binding::Binding, field::Symbol)
for mod in modules
dict = meta(mod)
if haskey(dict, binding)
multidoc = dict[binding]
if haskey(multidoc.docs, Union{})
fields = multidoc.docs[Union{}].data[:fields]
if haskey(fields, field)
doc = fields[field]
return isa(doc, Markdown.MD) ? doc : Markdown.parse(doc)
end
end
end
end
fields = join(["`$f`" for f in fieldnames(resolve(binding))], ", ", ", and ")
fields = isempty(fields) ? "no fields" : "fields $fields"
Markdown.parse("`$(resolve(binding))` has $fields.")
end

# As with the additional `doc` methods, this converts an object to a `Binding` first.
fielddoc(object, field::Symbol) = fielddoc(aliasof(object, typeof(object)), field)

# Object Summaries.
# =================

function summarize(binding::Binding, sig)
io = IOBuffer()
println(io, "No documentation found.\n")
if defined(binding)
summarize(io, resolve(binding), binding)
else
println(io, "Binding `", binding, "` does not exist.")
end
md = Markdown.parse(seekstart(io))
# Save metadata in the generated markdown.
md.meta[:results] = DocStr[]
md.meta[:binding] = binding
md.meta[:typesig] = sig
return md
end

function summarize(io::IO, λ::Function, binding)
kind = startswith(string(binding.var), '@') ? "macro" : "`Function`"
println(io, "`", binding, "` is a ", kind, ".")
println(io, "```\n", methods(λ), "\n```")
end

function summarize(io::IO, T::DataType, binding)
println(io, "# Summary")
println(io, "```")
println(io,
T.abstract ? "abstract type" :
T.mutable ? "mutable struct" :
Base.isstructtype(T) ? "struct" : "primitive type",
" ", T, " <: ", supertype(T)
)
println(io, "```")
if !T.abstract && T.name !== Tuple.name && !isempty(fieldnames(T))
println(io, "# Fields")
println(io, "```")
pad = maximum(length(string(f)) for f in fieldnames(T))
for (f, t) in zip(fieldnames(T), T.types)
println(io, rpad(f, pad), " :: ", t)
end
println(io, "```")
end
if !isempty(subtypes(T))
println(io, "# Subtypes")
println(io, "```")
for t in subtypes(T)
println(io, t)
end
println(io, "```")
end
if supertype(T) != Any
println(io, "# Supertype Hierarchy")
println(io, "```")
Base.show_supertypes(io, T)
println(io)
println(io, "```")
end
end

function summarize(io::IO, m::Module, binding)
readme = Pkg.dir(string(m), "README.md")
if isfile(readme)
println(io, "Displaying the `README.md` for the module instead.\n")
println(io, "---\n")
println(io, read(readme, String))
else
println(io, "No docstring or `README.md` found for module `", m, "`.\n")
end
end

function summarize(io::IO, ::T, binding) where T
println(io, "`", binding, "` is of type `", T, "`.\n")
summarize(io, T, binding)
end

# Utilities.
# ==========

Expand Down
17 changes: 15 additions & 2 deletions stdlib/REPL/src/docview.jl
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,19 @@ function doc(binding::Binding, sig::Type = Union{})
result = getdoc(resolve(binding), sig)
result === nothing || return result
end
results, groups = DocStr[], MultiDoc[]
results, groups, sigs = DocStr[], MultiDoc[], Any[]
# Lookup `binding` and `sig` for matches in all modules of the docsystem.
for mod in modules
dict = meta(mod)
if haskey(dict, binding)
multidoc = dict[binding]
push!(groups, multidoc)
for msig in multidoc.order
sig <: msig && push!(results, multidoc.docs[msig])
if typeintersect(msig, sig) != Union{}
#if (isconcretetype(sig) && sig <: msig) || (!isconcretetype(sig) && msig <: sig) || sig == Union{}
push!(sigs, msig)
push!(results, multidoc.docs[msig])
end
end
end
end
Expand All @@ -112,6 +116,15 @@ function doc(binding::Binding, sig::Type = Union{})
for group in groups, each in group.order
push!(results, group.docs[each])
end
elseif sig != Union{}
# If the signature is abstract, only show more specific methods
morespec = [x <: sig && x != Union{} for x in sigs]
if !any(morespec)
# If not any method is more specific that sig, show the most specific one
results = DocStr[results[first(sortperm(sigs, lt = (a,b) -> a <: b))]]
else
results = results[morespec]
end
end
# Get parsed docs and concatenate them.
md = catdoc(map(parsedoc, results)...)
Expand Down
54 changes: 54 additions & 0 deletions test/docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1145,3 +1145,57 @@ end
end
@test M27832.xs == ":(\$(Expr(:\$, :fn)))"
Core.atdoc!(_last_atdoc)

# issue #20064

"""
f_20064
"""
function f_20064 end

"""
Number
"""
f_20064(x::Number) = x

"""
Real
"""
f_20064(x::Real) = x

"""
Int
"""
f_20064(x::Int) = x

"""
Real, Int
"""
f_20064(x::Real, y::Int) = x

@test docstrings_equal(Docs.doc(f_20064, Tuple{Int}),
doc"""
Int
""")

@test docstrings_equal(Docs.doc(f_20064, Tuple{Float64}),
doc"""
Real
""")

@test docstrings_equal(Docs.doc(f_20064, Tuple{Float64, Int}),
doc"""
Real, Int
""")

@test docstrings_equal(Docs.doc(f_20064, Tuple{Real}),
doc"""
Real
Int
""")

@test docstrings_equal(Docs.doc(f_20064, Tuple{Real, Real}),
doc"""
Real, Int
""")

0 comments on commit b79d0d2

Please sign in to comment.