From b6cc2ed35141cf05771108463f58106e72eb04f5 Mon Sep 17 00:00:00 2001 From: Jake Bolewski Date: Mon, 7 Sep 2015 22:52:39 -0400 Subject: [PATCH] Print the value's type help for undocumented bindings add fallback output for macros and update tests closes #13002 --- base/docs/Docs.jl | 106 ++++++++++++++++++++++++++++++++++------------ test/docs.jl | 35 +++++++++++---- 2 files changed, 106 insertions(+), 35 deletions(-) diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl index 00fdbd705c058..c9435b6ba0c2e 100644 --- a/base/docs/Docs.jl +++ b/base/docs/Docs.jl @@ -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 @@ -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 @@ -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:** @@ -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, :.) && @@ -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′)) : @@ -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 diff --git a/test/docs.jl b/test/docs.jl index 50cbc744b1303..64af508276ff5 100644 --- a/test/docs.jl +++ b/test/docs.jl @@ -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 @@ -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" @@ -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 @@ -317,7 +334,7 @@ end end -@test @doc(Undocumented.A) == doc""" +@test docstrings_equal(@doc(Undocumented.A), doc""" No documentation found. **Summary:** @@ -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:** @@ -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:** @@ -369,7 +386,7 @@ one :: Any two :: UTF8String three :: Float64 ``` -""" +""") # Bindings.