Skip to content

Commit 3dc91cf

Browse files
Docsystem Improvements
Moves a small docsystem component into coreimg which helps simplify the docs bootstrapping. `@doc`, `@__doc__`, and `@doc_str` are now defined in `Core` rather than in both `Core` and `Base`. From some adhoc timings compiling base it seems to have reduced it slightly somehow. Bare docstrings are parsed as Core.@doc "..." f rather than @doc "..." f This avoids the unlikely case of a user redefining `@doc` in their own module. Seems like something that could be classed as surprising behaviour and should be disallowed. Adjusts the keyword docs in `basedocs.jl` to use `@doc` rather than side-stepping it entirely. Improves the file and line info captured by `@doc` so that docs can be located correctly in both `Base` and packages. Interpolation in docstrings is now lazy and only occurs during display; though cached for repeated calls. Instead of storing the raw string it is stored as a `SimpleVector` containing the string parts and interpolated values. This allows for context dependent autogenerated content in docstrings, such as method signatures, etc. by defining methods of `Docs.formatdoc` as follows: immutable __SIGNATURE__ end function Docs.formatdoc(buffer, d, part::Type{__SIGNATURE__}) # inspect the source code stored in docstring `d` and generate a user-friendly # signature to replace `part` in the final docstring. # ... end and used as """ $__SIGNATURE__ ... """ function foo(x, y, z = 1) # ... end Other possibilities aside from `__SIGNATURE__` might be `__FIELDS__` for embedding the field docs of a type into the main docstring. Neither of these are included in this commit.
1 parent 5bbe070 commit 3dc91cf

File tree

12 files changed

+744
-785
lines changed

12 files changed

+744
-785
lines changed

base/boot.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,19 @@ Array{T}(::Type{T}, m::Int) = Array{T,1}(m)
322322
Array{T}(::Type{T}, m::Int,n::Int) = Array{T,2}(m,n)
323323
Array{T}(::Type{T}, m::Int,n::Int,o::Int) = Array{T,3}(m,n,o)
324324

325+
# docsystem basics
326+
macro doc(x...)
327+
atdoc(x...)
328+
end
329+
macro __doc__(x)
330+
Expr(:escape, Expr(:block, Expr(:meta, :doc), x))
331+
end
332+
macro doc_str(s)
333+
Expr(:escape, s)
334+
end
335+
atdoc = (str, expr) -> Expr(:escape, expr)
336+
atdoc!(λ) = global atdoc = λ
337+
325338
module TopModule
326339
# this defines the types that lowering expects to be defined in a (top) module
327340
# that are usually inherited from Core, but could be defined custom for a module

base/coreimg.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@ print(x::ANY) = show(x)
1616
println(x::ANY) = ccall(:jl_, Void, (Any,), x) # includes a newline
1717
print(a::ANY...) = for x=a; print(x); end
1818

19-
# Doc macro shim.
20-
macro doc(str, def) Expr(:escape, def) end
21-
2219
## Load essential files and libraries
2320
include("essentials.jl")
2421
include("generator.jl")
@@ -67,6 +64,9 @@ include("intset.jl")
6764
include("dict.jl")
6865
include("iterator.jl")
6966

67+
# core docsystem
68+
include("docs/core.jl")
69+
7070
# compiler
7171
include("inference.jl")
7272

base/docs/Docs.jl

Lines changed: 56 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,14 @@ will cause that specific method to be documented, as opposed to the whole functi
5252
docs are concatenated together in the order they were defined to provide docs for the
5353
function.
5454
"""
55-
:(Base.DocBootstrap.@doc)
55+
:(Core.@doc)
5656

5757
include("bindings.jl")
5858

5959
import Base.Markdown: @doc_str, MD
6060
import Base.Meta: quot, isexpr
6161
import Base: Callable
62+
import Core.Inference.CoreDocs: lazy_iterpolate
6263

6364
export doc
6465

@@ -99,7 +100,7 @@ function argtype(expr::Expr)
99100
isexpr(expr, :(...)) && return :(Vararg{$(argtype(expr.args[1]))})
100101
argtype(expr.args[1])
101102
end
102-
argtype(::Symbol) = :Any
103+
argtype(other) = :Any
103104

104105
function typevars(expr::Expr)
105106
isexpr(expr, :curly) && return [tvar(x) for x in expr.args[2:end]]
@@ -126,13 +127,9 @@ The `.data` fields stores several values related to the docstring, such as: path
126127
linenumber, source code, and fielddocs.
127128
"""
128129
type DocStr
129-
text :: UTF8String
130+
text :: Core.SimpleVector
130131
object :: Nullable
131132
data :: Dict{Symbol, Any}
132-
133-
DocStr(text::AbstractString, data) = new(text, Nullable(), data)
134-
DocStr(object, data) = new("", Nullable(object), data)
135-
DocStr(docstr::DocStr, data) = docstr
136133
end
137134

138135
function docstr(binding::Binding, typesig::ANY = Union{})
@@ -147,7 +144,13 @@ function docstr(binding::Binding, typesig::ANY = Union{})
147144
end
148145
error("could not find matching docstring for '$binding :: $typesig'.")
149146
end
150-
docstr(object, data = Dict()) = DocStr(object, data)
147+
docstr(object, data = Dict()) = _docstr(object, data)
148+
149+
_docstr(vec::Core.SimpleVector, data) = DocStr(vec, Nullable(), data)
150+
_docstr(str::AbstractString, data) = DocStr(Core.svec(str), Nullable(), data)
151+
_docstr(object, data) = DocStr(Core.svec(), Nullable(object), data)
152+
153+
_docstr(doc::DocStr, data) = (doc.data = merge(data, doc.data); doc)
151154

152155
macro ref(x)
153156
binding = bindingexpr(namify(x))
@@ -157,9 +160,18 @@ end
157160

158161
docexpr(args...) = Expr(:call, docstr, args...)
159162

163+
function formatdoc(d::DocStr)
164+
buffer = IOBuffer()
165+
for part in d.text
166+
formatdoc(buffer, d, part)
167+
end
168+
Markdown.parse(seekstart(buffer))
169+
end
170+
@noinline formatdoc(buffer, d, part) = print(buffer, part)
171+
160172
function parsedoc(d::DocStr)
161173
if isnull(d.object)
162-
md = Markdown.parse(d.text)
174+
md = formatdoc(d)
163175
md.meta[:module] = d.data[:module]
164176
md.meta[:path] = d.data[:path]
165177
d.object = Nullable(md)
@@ -282,7 +294,10 @@ function fielddoc(binding::Binding, field::Symbol)
282294
multidoc = dict[binding]
283295
if haskey(multidoc.docs, Union{})
284296
fields = multidoc.docs[Union{}].data[:fields]
285-
haskey(fields, field) && return Markdown.parse(fields[field])
297+
if haskey(fields, field)
298+
doc = fields[field]
299+
return isa(doc, Markdown.MD) ? doc : Markdown.parse(doc)
300+
end
286301
end
287302
end
288303
end
@@ -377,7 +392,7 @@ isdoc(s::AbstractString) = true
377392

378393
isdoc(x) = isexpr(x, :string) ||
379394
(isexpr(x, :macrocall) && x.args[1] == symbol("@doc_str")) ||
380-
(isexpr(x, :call) && x.args[1] == Expr(:., Base.Markdown, QuoteNode(:doc_str)))
395+
(isexpr(x, :call) && x.args[1] == Base.Markdown.doc_str)
381396

382397
function unblock(ex)
383398
isexpr(ex, :block) || return ex
@@ -430,7 +445,8 @@ function metadata(expr)
430445
# Source code for the object being documented.
431446
push!(args, :($(Pair)(:source, $(quot(expr)))))
432447
# Filename and linenumber of the docstring.
433-
push!(args, :($(Pair)(:path, $(Base).@__FILE__)), :($(Pair)(:linenumber, @__LINE__)))
448+
push!(args, :($(Pair)(:path, $(Base).@__FILE__)))
449+
push!(args, :($(Pair)(:linenumber, $(unsafe_load(cglobal(:jl_lineno, Int))))))
434450
# Module in which the docstring is defined.
435451
push!(args, :($(Pair)(:module, $(current_module)())))
436452
# Field docs for concrete types.
@@ -451,9 +467,14 @@ function metadata(expr)
451467
:($(Dict)($(args...)))
452468
end
453469

470+
function keyworddoc(str, def)
471+
docstr = esc(docexpr(lazy_iterpolate(str), metadata(def)))
472+
:($(keywords)[$(esc(quot(def.name)))] = $docstr)
473+
end
474+
454475
function objectdoc(str, def, expr, sig = :(Union{}))
455476
binding = esc(bindingexpr(namify(expr)))
456-
docstr = esc(docexpr(str, metadata(expr)))
477+
docstr = esc(docexpr(lazy_iterpolate(str), metadata(expr)))
457478
quote
458479
$(esc(def))
459480
$(doc!)($binding, $docstr, $(esc(sig)))
@@ -480,7 +501,7 @@ end
480501
# Shares a single doc, `meta`, between several expressions from the tuple expression `ex`.
481502
function multidoc(meta, ex, define)
482503
out = Expr(:toplevel)
483-
str = docexpr(meta, metadata(ex))
504+
str = docexpr(lazy_iterpolate(meta), metadata(ex))
484505
ref = Ref{DocStr}()
485506
for (n, arg) in enumerate(ex.args)
486507
# The first `arg` to be documented needs to also create the docstring for the group.
@@ -508,7 +529,7 @@ more than one expression is marked then the same docstring is applied to each ex
508529
509530
`@__doc__` has no effect when a macro that uses it is not documented.
510531
"""
511-
:(Base.@__doc__)
532+
:(Core.@__doc__)
512533

513534
function __doc__!(meta, def, define)
514535
# Two cases must be handled here to avoid redefining all definitions contained in `def`:
@@ -575,6 +596,13 @@ function docm(meta, ex, define = true)
575596
# Initalise the module's docstring storage.
576597
initmeta()
577598

599+
# Keywords using the `@kw_str` macro in `base/docs/basedocs.jl`.
600+
#
601+
# "..."
602+
# kw"if", kw"else"
603+
#
604+
isa(x, Base.BaseDocs.Keyword) ? keyworddoc(meta, x) :
605+
578606
# Method / macro definitions and "call" syntax.
579607
#
580608
# function f(...) ... end
@@ -631,7 +659,7 @@ function docerror(ex)
631659
if isexpr(ex, :macrocall)
632660
txt *= "\n\n'$(ex.args[1])' not documentable. See 'Base.@__doc__' docs for details."
633661
end
634-
:(error($txt, "\n"))
662+
:($(error)($txt, "\n"))
635663
end
636664

637665
function docm(ex)
@@ -652,20 +680,21 @@ function docm(ex)
652680
end
653681
end
654682

655-
# Swap out the bootstrap macro with the real one
656-
657-
Base.DocBootstrap.setexpand!(docm)
658-
659-
# Names are resolved relative to the Base module, so inject the ones we need there.
660-
661-
eval(Base, :(import .Docs: @doc_str))
662-
663-
Base.DocBootstrap.loaddocs()
664-
665683
# MD support
666-
667684
catdoc(md::MD...) = MD(md...)
668685

669686
include("utils.jl")
670687

688+
# Swap out the bootstrap macro with the real one.
689+
Core.atdoc!(docm)
690+
691+
function loaddocs(docs)
692+
for (mod, ex, str, file, line) in docs
693+
data = Dict(:path => string(file), :linenumber => line)
694+
doc = docstr(str, data)
695+
eval(mod, :(@doc($doc, $ex, false)))
696+
end
697+
empty!(docs)
698+
end
699+
671700
end

0 commit comments

Comments
 (0)