Skip to content

Commit

Permalink
Merge FuncDoc and TypeDoc
Browse files Browse the repository at this point in the history
Combines the two composite docstring containers used in `Base.Docs`.
They have rather similar fields and in all uses have been treated in
a in a similar manner.

Since neither `FuncDoc` nor `TypeDoc` were exported and weren't exposed
to users of `at-doc` (this always returns a `Markdown.MD` object), this
shouldn't be a breaking change.
  • Loading branch information
MichaelHatherly committed Dec 30, 2015
1 parent 95ee269 commit 4bd7f65
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 101 deletions.
110 changes: 56 additions & 54 deletions base/docs/Docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ include("bindings.jl")

import Base.Markdown: @doc_str, MD
import Base.Meta: quot, isexpr
import Base: Callable

export doc

Expand Down Expand Up @@ -241,32 +242,55 @@ typevars(::Symbol) = []
tvar(x::Expr) = :($(x.args[1]) = TypeVar($(quot(x.args[1])), $(x.args[2]), true))
tvar(s::Symbol) = :($(s) = TypeVar($(quot(s)), Any, true))

type FuncDoc
main

"""
MultiDoc
Stores a collection of docstrings for related objects, ie. a `Function`/`DataType` and
associated `Method` objects.
Each documented object in a `MultiDoc` is referred to by it's signature which is represented
by a `Union` of `Tuple` types. For example the following `Method` definition
f(x, y) = ...
is stored as `Tuple{Any, Any}` in the `MultiDoc` while
f{T}(x::T, y = ?) = ...
is stored as `Union{Tuple{T}, Tuple{T, Any}}`.
Note: The `Function`/`DataType` object's signature is always `Union{}`.
"""
type MultiDoc
"Sorted (via `type_morespecific`) vector of object signatures."
order::Vector{Type}
meta::ObjectIdDict
"Documentation for each object. Keys are signatures."
docs::ObjectIdDict
"Source `Expr` for each object. As with `.docs` the keys are signatures."
source::ObjectIdDict
end
"Stores the documentation for individual fields of a type."
fields::Dict{Symbol, Any}

FuncDoc() = FuncDoc(nothing, [], ObjectIdDict(), ObjectIdDict())
MultiDoc() = new([], ObjectIdDict(), ObjectIdDict(), Dict())
end

# handles the :(function foo end) form
function doc!(f::Function, data)
doc!(f, Union{}, data, nothing)
function doc!::Callable, sig::ANY, docstr, expr::Expr, fields::Dict)
m = get!(meta(), λ, MultiDoc())
if !haskey(m.docs, sig)
push!(m.order, sig)
sort!(m.order, lt = type_morespecific)
end
m.docs[sig] = docstr
m.fields = fields
return m
end
doc!::Callable, docstr) = doc!(λ, Union{}, docstr, :(), Dict())
doc!::Callable, docstr, fields) = doc!(λ, Union{}, docstr, :(), fields)
doc!::Callable, sig::ANY, docstr, expr) = doc!(λ, sig, docstr, expr, Dict())

type_morespecific(a::Type, b::Type) =
(ccall(:jl_type_morespecific, Int32, (Any,Any), a, b) > 0)

# handles the :(function foo(x...); ...; end) form
function doc!(f::Function, sig::ANY, data, source)
fd = get!(meta(), f, FuncDoc())
isa(fd, FuncDoc) || error("can not document a method when the function already has metadata")
haskey(fd.meta, sig) || push!(fd.order, sig)
sort!(fd.order, lt=type_morespecific)
fd.meta[sig] = data
fd.source[sig] = source
end
type_morespecific(a::Type, b::Type) = ccall(:jl_type_morespecific, Int32, (Any,Any), a, b) > 0

"""
`catdoc(xs...)`: Combine the documentation metadata `xs` into a single meta object.
Expand Down Expand Up @@ -298,46 +322,19 @@ function field_meta(def)
return dict_expr(meta)
end

type TypeDoc
main
fields::Dict{Symbol, Any}
order::Vector{Type}
meta::ObjectIdDict
end

TypeDoc() = TypeDoc(nothing, Dict(), [], ObjectIdDict())

function doc!(t::DataType, data, fields)
td = get!(meta(), t, TypeDoc())
td.main = data
td.fields = fields
end

function doc!(T::DataType, sig::ANY, data, source)
td = get!(meta(), T, TypeDoc())
if !isa(td, TypeDoc)
error("can not document a method when the type already has metadata")
end
!haskey(td.meta, sig) && push!(td.order, sig)
td.meta[sig] = data
end

function doc(obj::Base.Callable, sig::Type = Union)
isgeneric(obj) && sig !== Union && isempty(methods(obj, sig)) && return nothing
results, groups = [], []
for m in modules
if haskey(meta(m), obj)
docs = meta(m)[obj]
if isa(docs, FuncDoc) || isa(docs, TypeDoc)
if isa(docs, MultiDoc)
push!(groups, docs)
for msig in docs.order
if sig <: msig
push!(results, (msig, docs.meta[msig]))
push!(results, (msig, docs.docs[msig]))
end
end
if isempty(results) && docs.main !== nothing
push!(results, (Union{}, docs.main))
end
else
push!(results, (Union{}, docs))
end
Expand All @@ -346,7 +343,7 @@ function doc(obj::Base.Callable, sig::Type = Union)
# If all method signatures are Union{} ( ⊥ ), concat all docstrings.
if isempty(results)
for group in groups
append!(results, [group.meta[s] for s in reverse(group.order)])
append!(results, [group.docs[s] for s in reverse(group.order)])
end
else
sort!(results, lt = (a, b) -> type_morespecific(first(a), first(b)))
Expand Down Expand Up @@ -394,12 +391,17 @@ isfield(x) = isexpr(x, :.) &&

function fielddoc(T, k)
for mod in modules
if haskey(meta(mod), T) && isa(meta(mod)[T], TypeDoc) && haskey(meta(mod)[T].fields, k)
return meta(mod)[T].fields[k]
docs = meta(mod)
if haskey(docs, T) && isa(docs[T], MultiDoc)
fields = docs[T].fields
if haskey(fields, k)
return fields[k]
end
end
end
Text(sprint(io -> (print(io, "$T has fields: ");
print_joined(io, fieldnames(T), ", ", " and "))))
end
fields = join(["`$f`" for f in fieldnames(T)], ", ", ", and ")
fields = isempty(fields) ? "no fields" : "fields $fields"
Markdown.parse("`$T` has $fields.")
end

# Generic Callables
Expand Down
13 changes: 2 additions & 11 deletions base/docs/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -319,20 +319,11 @@ function docsearch(haystack, needle)
end

## Searching specific documentation objects
function docsearch(haystack::TypeDoc, needle)
docsearch(haystack.main, needle) && return true
function docsearch(haystack::MultiDoc, needle)
for v in values(haystack.fields)
docsearch(v, needle) && return true
end
for v in values(haystack.meta)
docsearch(v, needle) && return true
end
false
end

function docsearch(haystack::FuncDoc, needle)
docsearch(haystack.main, needle) && return true
for v in values(haystack.meta)
for v in values(haystack.docs)
docsearch(v, needle) && return true
end
false
Expand Down
8 changes: 4 additions & 4 deletions doc/genstdlib.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ function add_all_docs_meta(m::Module,meta)
exports = names(m)
for (obj, d) in meta
isexported = sym_exported(obj, m, exports)
if isa(d, Base.Docs.FuncDoc) || isa(d, Base.Docs.TypeDoc)
add_all_docs(d.meta, (obj, isexported))
if isa(d, Base.Docs.MultiDoc)
add_all_docs(d.docs, (obj, isexported))
else
all_docs[d] = (obj, isexported)
end
Expand Down Expand Up @@ -75,8 +75,8 @@ function find_docs(v)
for mod in keys(mod_added)
try
meta = Docs.meta(mod)[v]
if isa(meta, Base.Docs.FuncDoc) || isa(meta, Base.Docs.TypeDoc)
append!(docs, collect(values(meta.meta)))
if isa(meta, Base.Docs.MultiDoc)
append!(docs, collect(values(meta.docs)))
else
push!(docs, meta)
end
Expand Down
67 changes: 35 additions & 32 deletions test/docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -145,52 +145,55 @@ end
@test meta(DocsTest)[DocsTest].meta[:module] == DocsTest

let f = DocsTest.f
funcdoc = meta(DocsTest)[f]
@test funcdoc.main == nothing
@test docstrings_equal(funcdoc.meta[Tuple{Any}], doc"f-1")
@test docstrings_equal(funcdoc.meta[Tuple{Any,Any}], doc"f-2")
md = meta(DocsTest)[f]
@test docstrings_equal(md.docs[Tuple{Any}], doc"f-1")
@test docstrings_equal(md.docs[Tuple{Any,Any}], doc"f-2")
end

let s = DocsTest.s
funcdoc = meta(DocsTest)[s]
@test funcdoc.main == nothing
@test docstrings_equal(funcdoc.meta[Tuple{Any,}], doc"s-1")
@test docstrings_equal(funcdoc.meta[Tuple{Any,Any}], doc"s-2")
md = meta(DocsTest)[s]
@test docstrings_equal(md.docs[Tuple{Any,}], doc"s-1")
@test docstrings_equal(md.docs[Tuple{Any,Any}], doc"s-2")
end

let g = DocsTest.g
funcdoc = meta(DocsTest)[g]
@test docstrings_equal(funcdoc.meta[Union{}], doc"g")
md = meta(DocsTest)[g]
@test docstrings_equal(md.docs[Union{}], doc"g")
end

let h = DocsTest.h
funcdoc = meta(DocsTest)[h]
md = meta(DocsTest)[h]
sig = Union{Tuple{}, Tuple{Any}, Tuple{Any, Any}, Tuple{Any, Any, Any}}
@test docstrings_equal(funcdoc.meta[sig], doc"h/0-3")
@test docstrings_equal(md.docs[sig], doc"h/0-3")
end

let AT = DocsTest.AT
@test meta(DocsTest)[AT] == doc"AT"
md = meta(DocsTest)[AT]
@test docstrings_equal(md.docs[Union{}], doc"AT")
end

let BT = DocsTest.BT
@test meta(DocsTest)[BT] == doc"BT"
md = meta(DocsTest)[BT]
@test docstrings_equal(md.docs[Union{}], doc"BT")
end

@test meta(DocsTest)[DocsTest.BT2] == doc"BT2"
let BT2 = DocsTest.BT2
md = meta(DocsTest)[BT2]
@test docstrings_equal(md.docs[Union{}], doc"BT2")
end

let T = DocsTest.T
typedoc = meta(DocsTest)[T]
@test docstrings_equal(typedoc.main, doc"T")
@test docstrings_equal(typedoc.fields[:x], doc"T.x")
@test docstrings_equal(typedoc.fields[:y], doc"T.y")
md = meta(DocsTest)[T]
@test docstrings_equal(md.docs[Union{}], doc"T")
@test docstrings_equal(md.fields[:x], doc"T.x")
@test docstrings_equal(md.fields[:y], doc"T.y")
end

let IT = DocsTest.IT
typedoc = meta(DocsTest)[IT]
@test docstrings_equal(typedoc.main, doc"IT")
@test docstrings_equal(typedoc.fields[:x], doc"IT.x")
@test docstrings_equal(typedoc.fields[:y], doc"IT.y")
md = meta(DocsTest)[IT]
@test docstrings_equal(md.docs[Union{}], doc"IT")
@test docstrings_equal(md.fields[:x], doc"IT.x")
@test docstrings_equal(md.fields[:y], doc"IT.y")
end

@test @doc(DocsTest.TA) == doc"TA"
Expand Down Expand Up @@ -306,17 +309,17 @@ end

end

let funcdoc = meta(MacroGenerated)[MacroGenerated.f]
@test funcdoc.order == [Tuple{Any}]
@test funcdoc.meta[Tuple{Any}] == doc"f"
let md = meta(MacroGenerated)[MacroGenerated.f]
@test md.order == [Tuple{Any}]
@test md.docs[Tuple{Any}] == doc"f"
end

@test isdefined(MacroGenerated, :_f)

let funcdoc = meta(MacroGenerated)[MacroGenerated.g]
@test funcdoc.order == [Tuple{Any}, Tuple{Any, Any}]
@test funcdoc.meta[Tuple{Any}] == doc"g"
@test funcdoc.meta[Tuple{Any, Any}] == doc"g"
let md = meta(MacroGenerated)[MacroGenerated.g]
@test md.order == [Tuple{Any}, Tuple{Any, Any}]
@test md.docs[Tuple{Any}] == doc"g"
@test md.docs[Tuple{Any, Any}] == doc"g"
end

@test isdefined(MacroGenerated, :_g)
Expand Down Expand Up @@ -374,8 +377,8 @@ read(x) = x

end

let fd = Base.Docs.meta(I11798)[I11798.read],
d1 = fd.meta[fd.order[1]],
let md = Base.Docs.meta(I11798)[I11798.read],
d1 = md.docs[md.order[1]],
d2 = doc"read"
@test docstrings_equal(d1,d2)
end
Expand Down

0 comments on commit 4bd7f65

Please sign in to comment.