Skip to content

Commit

Permalink
Print the value's type help for undocumented bindings
Browse files Browse the repository at this point in the history
add fallback output for macros and update tests

closes JuliaLang#13002
  • Loading branch information
jakebolewski committed Sep 10, 2015
1 parent 7cd05db commit b6cc2ed
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 35 deletions.
106 changes: 80 additions & 26 deletions base/docs/Docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,54 @@ function doc(obj)
end
end

function macrosummary(name::Symbol, func::Function)
parts = ["""
No documentation found.
"""]
if isdefined(func,:code) && func.code != nothing
lam = Base.uncompressed_ast(func.code)
io = IOBuffer()
write(io, name, '(')
nargs = length(lam.args[1])
for (i,arg) in enumerate(lam.args[1])
argname, argtype = arg.args
if argtype === :Any || argtype === :ANY
write(io, argname)
elseif isa(argtype,Expr) && argtype.head === :... &&
(argtype.args[end] === :Any || argtype.args[end] === :ANY)
write(io, argname, "...")
else
write(io, argname, "::", argtype)
end
i < nargs && write(io, ',')
end
write(io, ')')
push!(parts, string("```julia\n", takebuf_string(io), "\n```"))
end
Markdown.parse(join(parts,'\n'))
end

function doc(b::Binding)
d = invoke(doc, Tuple{Any}, b)
d == nothing ? doc(getfield(b.mod, b.var)) : d
if d == nothing
v = getfield(b.mod,b.var)
d = doc(v)
if d == nothing
# check to see if the binding var is a macro
if startswith(string(b.var),'@')
d = macrosummary(b.var, v)
else
T = typeof(v)
d = catdoc(Markdown.parse("""
No documentation found.
`$(b.mod === Main ? b.var : join((b.mod, b.var),'.'))` is of type `$T`:
"""), typesummary(typeof(v)))
end
end
end
return d
end

# Function / Method support
Expand Down Expand Up @@ -154,7 +199,7 @@ type_morespecific(a::Type, b::Type) =
# 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't document a method when the function already has metadata")
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
Expand Down Expand Up @@ -232,45 +277,54 @@ function doc!(t::DataType, data, fields)
td.fields = fields
end

function doc!(f::DataType, sig::ANY, data, source)
td = get!(meta(), f, TypeDoc())
isa(td, TypeDoc) || error("Can't document a method when the type already has metadata")
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(f::DataType)
function doc(T::DataType)
docs = []
for mod in modules
if haskey(meta(mod), f)
fd = meta(mod)[f]
if isa(fd, TypeDoc)
length(docs) == 0 && fd.main !== nothing && push!(docs, fd.main)
for m in fd.order
push!(docs, fd.meta[m])
if haskey(meta(mod), T)
Td = meta(mod)[T]
if isa(Td, TypeDoc)
if length(docs) == 0 && Td.main !== nothing
push!(docs, Td.main)
end
for m in Td.order
push!(docs, Td.meta[m])
end
elseif length(docs) == 0
return fd
return Td
end
end
end
isempty(docs) ? typesummary(f) : catdoc(docs...)
if isempty(docs)
catdoc(Markdown.parse("""
No documentation found.
"""), typesummary(T))
else
catdoc(docs...)
end
end

function typesummary(f::DataType)
function typesummary(T::DataType)
parts = [
"""
No documentation found.
**Summary:**
```julia
$(f.abstract ? "abstract" : f.mutable ? "type" : "immutable") $f <: $(super(f))
$(T.abstract ? "abstract" : T.mutable ? "type" : "immutable") $T <: $(super(T))
```
"""
]
if !isempty(fieldnames(f))
pad = maximum([length(string(f)) for f in fieldnames(f)])
fields = ["$(rpad(f, pad)) :: $(t)" for (f, t) in zip(fieldnames(f), f.types)]
if !isempty(fieldnames(T))
pad = maximum([length(string(f)) for f in fieldnames(T)])
fields = ["$(rpad(f, pad)) :: $(t)" for (f,t) in zip(fieldnames(T), T.types)]
push!(parts,
"""
**Fields:**
Expand All @@ -279,16 +333,16 @@ function typesummary(f::DataType)
```
""")
end
if !isempty(subtypes(f))
if !isempty(subtypes(T))
push!(parts,
"""
**Subtypes:**
```julia
$(join(subtypes(f), "\n"))
$(join(subtypes(T),'\n'))
```
""")
end
Markdown.parse(join(parts, "\n"))
Markdown.parse(join(parts,'\n'))
end

isfield(x) = isexpr(x, :.) &&
Expand Down Expand Up @@ -419,7 +473,7 @@ function docm(meta, def, define = true)
isexpr(def′, :function) ? namedoc(meta, def, namify(def′)) :
isexpr(def′, :call) ? funcdoc(meta, nothing, def′) :
isexpr(def′, :type) ? typedoc(meta, def, def′) :
isexpr(def′, :macro) ? vardoc(meta, def, symbol("@", namify(def′))) :
isexpr(def′, :macro) ? vardoc(meta, def, symbol('@',namify(def′))) :
isexpr(def′, :abstract) ? namedoc(meta, def, namify(def′)) :
isexpr(def′, :bitstype) ? namedoc(meta, def, def′.args[2]) :
isexpr(def′, :typealias) ? vardoc(meta, def, namify(def′)) :
Expand All @@ -428,7 +482,7 @@ function docm(meta, def, define = true)
:global) ? vardoc(meta, def, namify(def′)) :
isvar(def′) ? objdoc(meta, def′) :
isexpr(def′, :tuple) ? multidoc(meta, def′.args) :
isa(def′, Expr) ? error("Invalid doc expression $def′") :
isa(def′, Expr) ? error("invalid doc expression $def′") :
objdoc(meta, def′)
end

Expand Down
35 changes: 26 additions & 9 deletions test/docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,13 @@ import .Inner.@m
"Inner.@m"
:@m

type Foo
x
end

# value with no docs
const val = Foo(1.0)

end

import Base.Docs: meta
Expand Down Expand Up @@ -213,6 +220,12 @@ let fields = meta(DocsTest)[DocsTest.FieldDocs].fields
@test haskey(fields, :two) && fields[:two] == doc"two"
end

# test that when no docs exist, they fallback to
# the docs for the typeof(value)
let d1 = @doc(DocsTest.val)
@test d1 !== nothing
end

# Issue #12700.
@test @doc(DocsTest.@m) == doc"Inner.@m"

Expand All @@ -227,7 +240,11 @@ end

@doc "This should document @m1... since its the result of expansion" @m2_11993
@test (@doc @m1_11993) !== nothing
@test (@doc @m2_11993) === nothing
let d = (@doc @m2_11993)
io = IOBuffer()
writemime(io, MIME"text/markdown"(), d)
@test startswith(takebuf_string(io),"No documentation found")
end

@doc "Now @m2... should be documented" :@m2_11993
@test (@doc @m2_11993) !== nothing
Expand Down Expand Up @@ -317,7 +334,7 @@ end

end

@test @doc(Undocumented.A) == doc"""
@test docstrings_equal(@doc(Undocumented.A), doc"""
No documentation found.
**Summary:**
Expand All @@ -330,9 +347,9 @@ abstract Undocumented.A <: Any
Undocumented.B
Undocumented.C
```
"""
""")

@test @doc(Undocumented.B) == doc"""
@test docstrings_equal(@doc(Undocumented.B), doc"""
No documentation found.
**Summary:**
Expand All @@ -344,18 +361,18 @@ abstract Undocumented.B <: Undocumented.A
```julia
Undocumented.D
```
"""
""")

@test @doc(Undocumented.C) == doc"""
@test docstrings_equal(@doc(Undocumented.C), doc"""
No documentation found.
**Summary:**
```julia
type Undocumented.C <: Undocumented.A
```
"""
""")

@test @doc(Undocumented.D) == doc"""
@test docstrings_equal(@doc(Undocumented.D), doc"""
No documentation found.
**Summary:**
Expand All @@ -369,7 +386,7 @@ one :: Any
two :: UTF8String
three :: Float64
```
"""
""")

# Bindings.

Expand Down

0 comments on commit b6cc2ed

Please sign in to comment.