diff --git a/base/boot.jl b/base/boot.jl index 922f5a3098b9a..83071073d87e9 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -410,9 +410,6 @@ end macro __doc__(x) Expr(:escape, Expr(:block, Expr(:meta, :doc), x)) end -macro doc_str(s) - Expr(:escape, s) -end atdoc = (source, mod, str, expr) -> Expr(:escape, expr) atdoc!(λ) = global atdoc = λ diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl index ac18e985ccec6..d5d56b4e0f3ff 100644 --- a/base/docs/Docs.jl +++ b/base/docs/Docs.jl @@ -60,12 +60,11 @@ function. include("bindings.jl") -import Base.Markdown: @doc_str, MD import Base.Meta: quot, isexpr import Base: Callable, with_output_color import ..CoreDocs: lazy_iterpolate -export doc +export doc, apropos # Basic API / Storage @@ -174,25 +173,6 @@ end docexpr(__source__, __module__, args...) = Expr(:call, docstr, args...) -function formatdoc(d::DocStr) - buffer = IOBuffer() - for part in d.text - formatdoc(buffer, d, part) - end - Markdown.MD(Any[Markdown.parse(seekstart(buffer))]) -end -@noinline formatdoc(buffer, d, part) = print(buffer, part) - -function parsedoc(d::DocStr) - if d.object === nothing - md = formatdoc(d) - md.meta[:module] = d.data[:module] - md.meta[:path] = d.data[:path] - d.object = md - end - d.object -end - """ MultiDoc @@ -270,167 +250,6 @@ function getdoc end getdoc(x, sig) = getdoc(x) getdoc(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 = DocStr[], MultiDoc[] - # 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]) - 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 - 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. # ========== @@ -442,12 +261,6 @@ catdoc(xs...) = vcat(xs...) const keywords = Dict{Symbol, DocStr}() -isdoc(s::AbstractString) = true - -isdoc(x) = isexpr(x, :string) || - (isexpr(x, :macrocall) && x.args[1] === Symbol("@doc_str")) || - (isexpr(x, :call) && x.args[1] === Base.Markdown.doc_str) - function unblock(ex) isexpr(ex, :block) || return ex exs = filter(ex -> !(isa(ex, LineNumberNode) || isexpr(ex, :line)), ex.args) @@ -511,13 +324,20 @@ function metadata(__source__, __module__, expr, ismodule) if isexpr(expr, :struct) # Field docs for concrete types. fields = [] - tmp = nothing + last_docstr = nothing for each in expr.args[3].args - if isdoc(each) - tmp = each - elseif tmp !== nothing && (isa(each, Symbol) || isexpr(each, :(::))) - push!(fields, (namify(each), tmp)) - tmp = nothing + if isa(each, Symbol) || isexpr(each, :(::)) + # a field declaration + if last_docstr !== nothing + push!(fields, (namify(each), last_docstr)) + last_docstr = nothing + end + elseif isexpr(each, :function) || isexpr(each, :(=)) + break + elseif isa(each, String) || isexpr(each, :string) || isexpr(each, :call) || + (isexpr(each, :macrocall) && each.args[1] === Symbol("@doc_str")) + # forms that might be doc strings + last_docstr = each end end dict = :($(Dict)($([:($(Pair)($(quot(f)), $d)) for (f, d) in fields]...))) @@ -654,6 +474,16 @@ isquotedmacrocall(x) = isbasicdoc(x) = isexpr(x, :.) || isa(x, Union{QuoteNode, Symbol}) is_signature(x) = isexpr(x, :call) || (isexpr(x, :(::), 2) && isexpr(x.args[1], :call)) || isexpr(x, :where) +function docm(source::LineNumberNode, mod::Module, ex) + if isexpr(ex, :->) && length(ex.args) > 1 + docm(source, mod, ex.args...) + else + # TODO: this is a shim to continue to allow `@doc` for looking up docstrings + REPL = Base.root_module(Base, :REPL) + return REPL.lookup_doc(ex) + end +end + function docm(source::LineNumberNode, mod::Module, meta, ex, define = true) # Some documented expressions may be decorated with macro calls which obscure the actual # expression. Expand the macro calls and remove extra blocks. @@ -730,27 +560,6 @@ function docerror(ex) :($(error)($txt, "\n")) end -function docm(source::LineNumberNode, mod::Module, ex) - if isexpr(ex, :->) - docm(source, mod, ex.args...) - elseif haskey(keywords, ex) - parsedoc(keywords[ex]) - elseif isa(ex, Union{Expr, Symbol}) - binding = esc(bindingexpr(namify(ex))) - if isexpr(ex, :call) || isexpr(ex, :macrocall) - sig = esc(signature(ex)) - :($(doc)($binding, $sig)) - else - :($(doc)($binding)) - end - else - :($(doc)($(typeof)($(esc(ex))))) - end -end - -# MD support -catdoc(md::MD...) = MD(md...) - include("utils.jl") # Swap out the bootstrap macro with the real one. @@ -775,4 +584,9 @@ function loaddocs(docs) empty!(docs) end +function formatdoc end +function parsedoc end +function apropos end +function doc end + end diff --git a/base/docs/utils.jl b/base/docs/utils.jl index 0ae186f5ea1d9..4d9b2608839fb 100644 --- a/base/docs/utils.jl +++ b/base/docs/utils.jl @@ -6,7 +6,7 @@ import Base: print, show, ==, hash export HTML, @html_str -export HTML, Text, apropos +export HTML, Text """ `HTML(s)`: Create an object that renders `s` as html. @@ -91,374 +91,3 @@ function catdoc(xs::Text...) end end end - -# REPL help - -# This is split into helpmode and _helpmode to easier unittest _helpmode -helpmode(io::IO, line::AbstractString) = :(Base.Docs.insert_hlines($io, $(Base.Docs._helpmode(io, line)))) -helpmode(line::AbstractString) = helpmode(STDOUT, line) - -function _helpmode(io::IO, line::AbstractString) - line = strip(line) - expr = - if haskey(keywords, Symbol(line)) - # Docs for keywords must be treated separately since trying to parse a single - # keyword such as `function` would throw a parse error due to the missing `end`. - Symbol(line) - else - x = Meta.parse(line, raise = false, depwarn = false) - # Retrieving docs for macros requires us to make a distinction between the text - # `@macroname` and `@macroname()`. These both parse the same, but are used by - # the docsystem to return different results. The first returns all documentation - # for `@macroname`, while the second returns *only* the docs for the 0-arg - # definition if it exists. - (isexpr(x, :macrocall, 1) && !endswith(line, "()")) ? quot(x) : x - end - # the following must call repl(io, expr) via the @repl macro - # so that the resulting expressions are evaluated in the Base.Docs namespace - :(Base.Docs.@repl $io $expr) -end -_helpmode(line::AbstractString) = _helpmode(STDOUT, line) - -# Print vertical lines along each docstring if there are multiple docs -function insert_hlines(io::IO, docs) - if !isa(docs, Markdown.MD) || !haskey(docs.meta, :results) || isempty(docs.meta[:results]) - return docs - end - v = Any[] - for (n, doc) in enumerate(docs.content) - push!(v, doc) - n == length(docs.content) || push!(v, Markdown.HorizontalRule()) - end - return Markdown.MD(v) -end - - -function repl_search(io::IO, s) - pre = "search:" - print(io, pre) - printmatches(io, s, completions(s), cols = displaysize(io)[2] - length(pre)) - println(io, "\n") -end -repl_search(s) = repl_search(STDOUT, s) - -function repl_corrections(io::IO, s) - print(io, "Couldn't find ") - print_with_color(:cyan, io, s, '\n') - print_correction(io, s) -end -repl_corrections(s) = repl_corrections(STDOUT, s) - -# inverse of latex_symbols Dict, lazily created as needed -const symbols_latex = Dict{String,String}() -function symbol_latex(s::String) - if isempty(symbols_latex) && isassigned(Base.REPL_MODULE_REF) - for (k,v) in Base.REPL_MODULE_REF[].REPLCompletions.latex_symbols - symbols_latex[v] = k - end - end - return get(symbols_latex, s, "") -end -function repl_latex(io::IO, s::String) - latex = symbol_latex(s) - if !isempty(latex) - print(io, "\"") - print_with_color(:cyan, io, s) - print(io, "\" can be typed by ") - print_with_color(:cyan, io, latex, "") - println(io, '\n') - elseif any(c -> haskey(symbols_latex, string(c)), s) - print(io, "\"") - print_with_color(:cyan, io, s) - print(io, "\" can be typed by ") - with_output_color(:cyan, io) do io - for c in s - cstr = string(c) - if haskey(symbols_latex, cstr) - print(io, symbols_latex[cstr], "") - else - print(io, c) - end - end - end - println(io, '\n') - end -end -repl_latex(s::String) = repl_latex(STDOUT, s) - -macro repl(ex) repl(ex) end -macro repl(io, ex) repl(io, ex) end - -function repl(io::IO, s::Symbol) - str = string(s) - quote - repl_latex($io, $str) - repl_search($io, $str) - $(if !isdefined(Main, s) && !haskey(keywords, s) - :(repl_corrections($io, $str)) - end) - $(_repl(s)) - end -end -isregex(x) = isexpr(x, :macrocall, 3) && x.args[1] === Symbol("@r_str") && !isempty(x.args[3]) -repl(io::IO, ex::Expr) = isregex(ex) ? :(apropos($io, $ex)) : _repl(ex) -repl(io::IO, str::AbstractString) = :(apropos($io, $str)) -repl(io::IO, other) = esc(:(@doc $other)) - -repl(x) = repl(STDOUT, x) - -function _repl(x) - if (isexpr(x, :call) && !any(isexpr(x, :(::)) for x in x.args)) - x.args[2:end] = [:(::typeof($arg)) for arg in x.args[2:end]] - end - docs = esc(:(@doc $x)) - if isfield(x) - quote - if isa($(esc(x.args[1])), DataType) - fielddoc($(esc(x.args[1])), $(esc(x.args[2]))) - else - $docs - end - end - else - docs - end -end - - -# Search & Rescue -# Utilities for correcting user mistakes and (eventually) -# doing full documentation searches from the repl. - -# Fuzzy Search Algorithm - -function matchinds(needle, haystack; acronym = false) - chars = collect(needle) - is = Int[] - lastc = '\0' - for (i, char) in enumerate(haystack) - isempty(chars) && break - while chars[1] == ' ' popfirst!(chars) end # skip spaces - if lowercase(char) == lowercase(chars[1]) && - (!acronym || !isalpha(lastc)) - push!(is, i) - popfirst!(chars) - end - lastc = char - end - return is -end - -longer(x, y) = length(x) ≥ length(y) ? (x, true) : (y, false) - -bestmatch(needle, haystack) = - longer(matchinds(needle, haystack, acronym = true), - matchinds(needle, haystack)) - -avgdistance(xs) = - isempty(xs) ? 0 : - (xs[end] - xs[1] - length(xs)+1)/length(xs) - -function fuzzyscore(needle, haystack) - score = 0. - is, acro = bestmatch(needle, haystack) - score += (acro ? 2 : 1)*length(is) # Matched characters - score -= 2(length(needle)-length(is)) # Missing characters - !acro && (score -= avgdistance(is)/10) # Contiguous - !isempty(is) && (score -= mean(is)/100) # Closer to beginning - return score -end - -function fuzzysort(search, candidates) - scores = map(cand -> (fuzzyscore(search, cand), -levenshtein(search, cand)), candidates) - candidates[sortperm(scores)] |> reverse -end - -# Levenshtein Distance - -function levenshtein(s1, s2) - a, b = collect(s1), collect(s2) - m = length(a) - n = length(b) - d = Matrix{Int}(uninitialized, m+1, n+1) - - d[1:m+1, 1] = 0:m - d[1, 1:n+1] = 0:n - - for i = 1:m, j = 1:n - d[i+1,j+1] = min(d[i , j+1] + 1, - d[i+1, j ] + 1, - d[i , j ] + (a[i] != b[j])) - end - - return d[m+1, n+1] -end - -function levsort(search, candidates) - scores = map(cand -> (levenshtein(search, cand), -fuzzyscore(search, cand)), candidates) - candidates = candidates[sortperm(scores)] - i = 0 - for outer i = 1:length(candidates) - levenshtein(search, candidates[i]) > 3 && break - end - return candidates[1:i] -end - -# Result printing - -function printmatch(io::IO, word, match) - is, _ = bestmatch(word, match) - for (i, char) = enumerate(match) - if i in is - print_with_color(:bold, io, char) - else - print(io, char) - end - end -end - -printmatch(args...) = printfuzzy(STDOUT, args...) - -function printmatches(io::IO, word, matches; cols = displaysize(io)[2]) - total = 0 - for match in matches - total + length(match) + 1 > cols && break - fuzzyscore(word, match) < 0 && break - print(io, " ") - printmatch(io, word, match) - total += length(match) + 1 - end -end - -printmatches(args...; cols = displaysize(STDOUT)[2]) = printmatches(STDOUT, args..., cols = cols) - -function print_joined_cols(io::IO, ss, delim = "", last = delim; cols = displaysize(io)[2]) - i = 0 - total = 0 - for outer i = 1:length(ss) - total += length(ss[i]) - total + max(i-2,0)*length(delim) + (i>1 ? 1 : 0)*length(last) > cols && (i-=1; break) - end - join(io, ss[1:i], delim, last) -end - -print_joined_cols(args...; cols = displaysize(STDOUT)[2]) = print_joined_cols(STDOUT, args...; cols=cols) - -function print_correction(io, word) - cors = levsort(word, accessible(Main)) - pre = "Perhaps you meant " - print(io, pre) - print_joined_cols(io, cors, ", ", " or "; cols = displaysize(io)[2] - length(pre)) - println(io) - return -end - -print_correction(word) = print_correction(STDOUT, word) - -# Completion data - -const builtins = ["abstract type", "baremodule", "begin", "break", - "catch", "ccall", "const", "continue", "do", "else", - "elseif", "end", "export", "finally", "for", "function", - "global", "if", "import", "let", - "local", "macro", "module", "mutable struct", "primitive type", - "quote", "return", "struct", "try", "using", "while"] - -moduleusings(mod) = ccall(:jl_module_usings, Any, (Any,), mod) - -filtervalid(names) = filter(x->!contains(x, r"#"), map(string, names)) - -accessible(mod::Module) = - [filter!(s -> !Base.isdeprecated(mod, s), names(mod, all = true, imported = true)); - map(names, moduleusings(mod))...; - builtins] |> unique |> filtervalid - -completions(name) = fuzzysort(name, accessible(Main)) -completions(name::Symbol) = completions(string(name)) - - -# Searching and apropos - -# Docsearch simply returns true or false if an object contains the given needle -docsearch(haystack::AbstractString, needle) = !isempty(findfirst(needle, haystack)) -docsearch(haystack::Symbol, needle) = docsearch(string(haystack), needle) -docsearch(::Nothing, needle) = false -function docsearch(haystack::Array, needle) - for elt in haystack - docsearch(elt, needle) && return true - end - false -end -function docsearch(haystack, needle) - @warn "Unable to search documentation of type $(typeof(haystack))" maxlog=1 - false -end - -## Searching specific documentation objects -function docsearch(haystack::MultiDoc, needle) - for v in values(haystack.docs) - docsearch(v, needle) && return true - end - false -end - -function docsearch(haystack::DocStr, needle) - docsearch(parsedoc(haystack), needle) && return true - if haskey(haystack.data, :fields) - for doc in values(haystack.data[:fields]) - docsearch(doc, needle) && return true - end - end - false -end - -## Markdown search simply strips all markup and searches plain text version -docsearch(haystack::Markdown.MD, needle) = - docsearch(stripmd(haystack.content), needle) - -""" - stripmd(x) - -Strip all Markdown markup from x, leaving the result in plain text. Used -internally by apropos to make docstrings containing more than one markdown -element searchable. -""" -stripmd(@nospecialize x) = string(x) # for random objects interpolated into the docstring -stripmd(x::AbstractString) = x # base case -stripmd(x::Nothing) = " " -stripmd(x::Vector) = string(map(stripmd, x)...) -stripmd(x::Markdown.BlockQuote) = "$(stripmd(x.content))" -stripmd(x::Markdown.Admonition) = "$(stripmd(x.content))" -stripmd(x::Markdown.Bold) = "$(stripmd(x.text))" -stripmd(x::Markdown.Code) = "$(stripmd(x.code))" -stripmd(x::Markdown.Header) = stripmd(x.text) -stripmd(x::Markdown.HorizontalRule) = " " -stripmd(x::Markdown.Image) = "$(stripmd(x.alt)) $(x.url)" -stripmd(x::Markdown.Italic) = "$(stripmd(x.text))" -stripmd(x::Markdown.LaTeX) = "$(x.formula)" -stripmd(x::Markdown.LineBreak) = " " -stripmd(x::Markdown.Link) = "$(stripmd(x.text)) $(x.url)" -stripmd(x::Markdown.List) = join(map(stripmd, x.items), " ") -stripmd(x::Markdown.MD) = join(map(stripmd, x.content), " ") -stripmd(x::Markdown.Paragraph) = stripmd(x.content) -stripmd(x::Markdown.Footnote) = "$(stripmd(x.id)) $(stripmd(x.text))" -stripmd(x::Markdown.Table) = - join([join(map(stripmd, r), " ") for r in x.rows], " ") - -# Apropos searches through all available documentation for some string or regex -""" - apropos(string) - -Search through all documentation for a string, ignoring case. -""" -apropos(string) = apropos(STDOUT, string) -apropos(io::IO, string) = apropos(io, Regex("\\Q$string", "i")) -function apropos(io::IO, needle::Regex) - for mod in modules - # Module doc might be in README.md instead of the META dict - docsearch(doc(mod), needle) && println(io, mod) - for (k, v) in meta(mod) - docsearch(v, needle) && println(io, k) - end - end -end diff --git a/base/exports.jl b/base/exports.jl index d1aeb9be89694..3ea368ccf985c 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -9,7 +9,6 @@ export Sys, Libc, Docs, - Markdown, Threads, Iterators, Broadcast, @@ -1022,7 +1021,6 @@ export @text_str, @html_str, @doc, - @doc_str, # output @show, diff --git a/base/interactiveutil.jl b/base/interactiveutil.jl index ba68c06606e43..d185ef17a1bfd 100644 --- a/base/interactiveutil.jl +++ b/base/interactiveutil.jl @@ -722,8 +722,9 @@ function runtests(tests = ["all"]; ncores = ceil(Int, Sys.CPU_CORES / 2), end end -# testing +# varinfo +const MARKDOWN_MODULE_REF = Ref{Module}() """ varinfo(m::Module=Main, pattern::Regex=r"") @@ -744,6 +745,10 @@ function varinfo(m::Module=Main, pattern::Regex=r"") pushfirst!(rows, Any["name", "size", "summary"]) + if !isassigned(MARKDOWN_MODULE_REF) + MARKDOWN_MODULE_REF[] = root_module(Base, :Markdown) + end + Markdown = MARKDOWN_MODULE_REF[] return Markdown.MD(Any[Markdown.Table(rows, Symbol[:l, :r, :l])]) end varinfo(pat::Regex) = varinfo(Main, pat) diff --git a/base/precompile.jl b/base/precompile.jl index 0a1707eb8075b..a32964f238e7e 100644 --- a/base/precompile.jl +++ b/base/precompile.jl @@ -447,22 +447,10 @@ precompile(Tuple{typeof(Base.copy), Base.GenericIOBuffer{Array{UInt8, 1}}}) precompile(Tuple{typeof(Base.getindex), Base.Dict{Symbol, Any}, Symbol}) precompile(Tuple{typeof(Core.Compiler.length), Tuple{Core.Compiler.Const, Core.Compiler.Const, DataType}}) precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{Core.Compiler.Const, Core.Compiler.Const, DataType}, Int64}) -precompile(Tuple{typeof(Base.Docs.helpmode), String}) precompile(Tuple{typeof(Base.ht_keyindex), Base.Dict{Symbol, Base.Docs.DocStr}, Symbol}) -precompile(Tuple{typeof(Base.Docs.helpmode), Base.TTY, String}) -precompile(Tuple{getfield(Base.Docs, Symbol("#@repl")), Base.TTY, Symbol}) -precompile(Tuple{typeof(Base.Docs.repl), Base.TTY, Symbol}) -precompile(Tuple{typeof(Base.Docs._repl), Symbol}) precompile(Tuple{getfield(Core, Symbol("#@doc")), Symbol}) precompile(Tuple{typeof(Base.Docs.docm), LineNumberNode, Module, Symbol}) -precompile(Tuple{typeof(Base._setindex!), Base.Dict{Any, Any}, Base.Markdown.Config, Symbol, Int64}) -precompile(Tuple{Type{Base.Markdown.MD}}) -precompile(Tuple{typeof(Base.setindex!), Base.Dict{Any, Any}, Base.Markdown.Config, Symbol}) -precompile(Tuple{typeof(Base.Markdown.skipblank), Base.GenericIOBuffer{Array{UInt8, 1}}}) precompile(Tuple{typeof(Base.vcat), Array{Function, 1}, Array{Function, 1}}) -precompile(Tuple{typeof(Base.Docs.formatdoc), Base.Docs.DocStr}) -precompile(Tuple{typeof(Base.Docs.parsedoc), Base.Docs.DocStr}) -precompile(Tuple{typeof(Base.Docs.repl_latex), Base.TTY, String}) precompile(Tuple{typeof(Base.ht_keyindex), Base.Dict{String, String}, String}) precompile(Tuple{typeof(Base.ht_keyindex), Base.Dict{Symbol, Tuple{String, String}}, Symbol}) precompile(Tuple{typeof(Base.print), Base.TTY, Char, Char}) @@ -471,17 +459,13 @@ precompile(Tuple{typeof(Base.ht_keyindex2!), Base.Dict{String, String}, String}) precompile(Tuple{typeof(Base._setindex!), Base.Dict{String, String}, String, String, Int64}) precompile(Tuple{typeof(Core.Compiler.isbits), Base.Dict{String, String}}) precompile(Tuple{typeof(Base.setindex!), Base.Dict{String, String}, String, String}) -precompile(Tuple{typeof(Base.Docs.symbol_latex), String}) -precompile(Tuple{typeof(Base.Docs.repl_search), Base.TTY, String}) precompile(Tuple{typeof(Base.print), Base.TTY, String}) precompile(Tuple{typeof(Base.print), Base.TTY, Char}) precompile(Tuple{typeof(Base.print), Base.TTY, String, Char}) precompile(Tuple{typeof(Base.mapreduce_impl), typeof(Base.identity), typeof(Base.:(+)), Array{Int64, 1}, Int64, Int64, Int64}) precompile(Tuple{typeof(Base._mapreduce), typeof(Base.identity), typeof(Base.:(+)), Base.IndexLinear, Array{Int64, 1}}) precompile(Tuple{typeof(Base.popfirst!), Array{Char, 1}}) -precompile(Tuple{typeof(Base.Docs.avgdistance), Array{Int64, 1}}) precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{Array{String, 1}}}) -precompile(Tuple{typeof(Base.Docs.accessible), Module}) precompile(Tuple{Type{Base.Generator{I, F} where F where I}, typeof(Base.names), Array{Any, 1}}) precompile(Tuple{typeof(Base._collect), Array{Any, 1}, Base.Generator{Array{Any, 1}, typeof(Base.names)}, Base.EltypeUnknown, Base.HasShape}) precompile(Tuple{typeof(Base.collect_to!), Array{Array{Symbol, 1}, 1}, Base.Generator{Array{Any, 1}, typeof(Base.names)}, Int64, Int64}) @@ -519,8 +503,6 @@ precompile(Tuple{Type{Base.Generator{I, F} where F where I}, typeof(Base.string) precompile(Tuple{typeof(Base._collect), Array{Any, 1}, Base.Generator{Array{Any, 1}, typeof(Base.string)}, Base.EltypeUnknown, Base.HasShape}) precompile(Tuple{typeof(Base.similar), Array{Any, 1}, Type{String}, Tuple{Base.OneTo{Int64}}}) precompile(Tuple{typeof(Base.collect_to!), Array{String, 1}, Base.Generator{Array{Any, 1}, typeof(Base.string)}, Int64, Int64}) -precompile(Tuple{getfield(Base.Docs, Symbol("#kw##matchinds")), Array{Any, 1}, typeof(Base.Docs.matchinds), String, String}) -precompile(Tuple{typeof(Base.Docs.bestmatch), String, String}) precompile(Tuple{typeof(Base.length), Tuple{DataType, DataType}}) precompile(Tuple{Type{BoundsError}, Array{Int64, 2}, Tuple{Base.UnitRange{Int64}, Int64}}) precompile(Tuple{typeof(Base.throw_boundserror), Array{Int64, 2}, Tuple{Base.UnitRange{Int64}, Int64}}) @@ -560,8 +542,6 @@ precompile(Tuple{typeof(Base._unsafe_setindex!), Base.IndexLinear, Array{Int64, precompile(Tuple{Type{BoundsError}, Array{Int64, 2}, Tuple{Int64, Base.UnitRange{Int64}}}) precompile(Tuple{typeof(Base.throw_boundserror), Array{Int64, 2}, Tuple{Int64, Base.UnitRange{Int64}}}) precompile(Tuple{typeof(Base._unsafe_setindex!), Base.IndexLinear, Array{Int64, 2}, Base.UnitRange{Int64}, Int64, Base.UnitRange{Int64}}) -precompile(Tuple{typeof(Base.Docs.levenshtein), String, String}) -precompile(Tuple{typeof(Base.Docs.fuzzyscore), String, String}) precompile(Tuple{typeof(Core.Compiler.copy_exprargs), Array{Any, 1}}) precompile(Tuple{typeof(Core.Compiler.copy), Expr}) precompile(Tuple{typeof(Core.Compiler.copyto!), Array{Any, 1}, Core.Compiler.Generator{Array{Any, 1}, typeof(Core.Compiler.copy_exprs)}}) @@ -609,9 +589,6 @@ precompile(Tuple{typeof(Base.throw_checksize_error), Array{String, 1}, Tuple{Bas precompile(Tuple{typeof(Base._unsafe_getindex), Base.IndexLinear, Array{String, 1}, Array{Int64, 1}}) precompile(Tuple{typeof(Base.reverse), Array{String, 1}}) precompile(Tuple{typeof(Base.reverse), Array{String, 1}, Int64, Int64}) -precompile(Tuple{getfield(Base.Docs, Symbol("#kw##printmatches")), Array{Any, 1}, typeof(Base.Docs.printmatches), Base.TTY, String, Array{String, 1}}) -precompile(Tuple{typeof(Base.Docs.printmatch), Base.TTY, String, String}) -precompile(Tuple{typeof(Base.Docs.doc), Base.Docs.Binding}) precompile(Tuple{typeof(Base.print), Base.GenericIOBuffer{Array{UInt8, 1}}, String, Base.Docs.Binding, String, String, String, Char}) precompile(Tuple{typeof(Base.show_method_table), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.MethodList, Int64, Bool}) precompile(Tuple{typeof(Base.print), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.MethodList}) @@ -624,7 +601,6 @@ precompile(Tuple{typeof(Base.Pkg.dir), String, String}) precompile(Tuple{typeof(Base._setindex!), Base.Dict{Any, Any}, Array{Base.Docs.DocStr, 1}, Symbol, Int64}) precompile(Tuple{typeof(Base._setindex!), Base.Dict{Any, Any}, Base.Docs.Binding, Symbol, Int64}) precompile(Tuple{typeof(Base._setindex!), Base.Dict{Any, Any}, Type{Union{}}, Symbol, Int64}) -precompile(Tuple{typeof(Base._collect), Array{Base.Docs.DocStr, 1}, Base.Generator{Array{Base.Docs.DocStr, 1}, typeof(Base.Docs.parsedoc)}, Base.EltypeUnknown, Base.HasShape}) precompile(Tuple{typeof(Base.ht_keyindex2!), Base.Dict{Union{DataType, typeof(Type)}, Nothing}, typeof(Type)}) precompile(Tuple{typeof(Base._setindex!), Base.Dict{Union{DataType, typeof(Type)}, Nothing}, Nothing, typeof(Type), Int64}) precompile(Tuple{typeof(Base.ht_keyindex2!), Base.Dict{Union{DataType, typeof(Type)}, Nothing}, DataType}) @@ -649,8 +625,6 @@ precompile(Tuple{typeof(Base.setindex!), Base.Dict{Any, Any}, Array{Base.Docs.Do precompile(Tuple{typeof(Base.setindex!), Base.Dict{Any, Any}, Base.Docs.Binding, Symbol}) precompile(Tuple{typeof(Base.setindex!), Base.Dict{Any, Any}, Type{Union{}}, Symbol}) precompile(Tuple{typeof(Base.Docs.aliasof), Base.Docs.Binding}) -precompile(Tuple{typeof(Base.Docs.summarize), Base.Docs.Binding, Type{Union{}}}) -precompile(Tuple{typeof(Base.Docs.doc), Base.Docs.Binding, Type{Union{}}}) precompile(Tuple{typeof(Base.haskey), Base.IdDict, Base.Docs.Binding}) precompile(Tuple{typeof(Base.getindex), Base.IdDict, Base.Docs.Binding}) precompile(Tuple{typeof(Base.push!), Array{Base.Docs.MultiDoc, 1}, Base.Docs.MultiDoc}) @@ -659,43 +633,8 @@ precompile(Tuple{typeof(Base.done), Array{Type{T} where T, 1}, Int64}) precompile(Tuple{typeof(Base.next), Array{Type{T} where T, 1}, Int64}) precompile(Tuple{typeof(Base.getindex), Base.IdDict, Type{Tuple{Any}}}) precompile(Tuple{typeof(Base.push!), Array{Base.Docs.DocStr, 1}, Base.Docs.DocStr}) -precompile(Tuple{typeof(Base.Docs.formatdoc), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Docs.DocStr, String}) -precompile(Tuple{typeof(Base.Markdown.hashheader), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD}) -precompile(Tuple{typeof(Base.Markdown.parseinline), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD, Array{Function, 1}}) -precompile(Tuple{typeof(Base.Markdown.config), Base.Markdown.MD}) -precompile(Tuple{typeof(Base.Markdown.parseinline), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD, Base.Markdown.Config}) -precompile(Tuple{typeof(Base.Markdown.list), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD}) -precompile(Tuple{typeof(Base._searchindex), Base.SubString{String}, String, Int64}) -precompile(Tuple{getfield(Base.Markdown, Symbol("#kw##skipwhitespace")), Array{Any, 1}, typeof(Base.Markdown.skipwhitespace), Base.GenericIOBuffer{Array{UInt8, 1}}}) -precompile(Tuple{typeof(Base.ht_keyindex), Base.Dict{Symbol, Base.Markdown.Config}, Symbol}) -precompile(Tuple{getfield(Base.Markdown, Symbol("#kw##parse")), Array{Any, 1}, typeof(Base.Markdown.parse), Base.GenericIOBuffer{Array{UInt8, 1}}}) -precompile(Tuple{getfield(Base.Markdown, Symbol("#kw##startswith")), Array{Any, 1}, typeof(Base.Markdown.startswith), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Regex}) -precompile(Tuple{typeof(Core.Compiler.isbits), Tuple{Int64, Base.Regex}}) -precompile(Tuple{typeof(Base.contains), String, String}) -precompile(Tuple{typeof(Base.contains), Base.SubString{String}, String}) -precompile(Tuple{typeof(Base.Markdown.pushitem!), Base.Markdown.List, Base.GenericIOBuffer{Array{UInt8, 1}}}) -precompile(Tuple{typeof(Base.Markdown.fencedcode), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD}) -precompile(Tuple{getfield(Base.Markdown, Symbol("#kw##startswith")), Array{Any, 1}, typeof(Base.Markdown.startswith), Base.GenericIOBuffer{Array{UInt8, 1}}, String}) -precompile(Tuple{typeof(Base.skip), Base.GenericIOBuffer{Array{UInt8, 1}}, Int64}) -precompile(Tuple{typeof(Base.lstrip), String, Char}) -precompile(Tuple{typeof(Base.Markdown.blockquote), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD}) -precompile(Tuple{getfield(Base.Markdown, Symbol("#kw##parse")), Array{Any, 1}, typeof(Base.Markdown.parse), String}) -precompile(Tuple{typeof(Base.Markdown.admonition), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD}) -precompile(Tuple{getfield(Base.Markdown, Symbol("#kw##linecontains")), Array{Any, 1}, typeof(Base.Markdown.linecontains), Base.GenericIOBuffer{Array{UInt8, 1}}, String}) -precompile(Tuple{typeof(Base.ucfirst), Base.SubString{String}}) -precompile(Tuple{typeof(Base.Markdown.blocktex), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD}) precompile(Tuple{typeof(Core.Compiler.length), Tuple{DataType, Core.Compiler.Const, DataType}}) precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{DataType, Core.Compiler.Const, DataType}, Int64}) -precompile(Tuple{getfield(Base.Markdown, Symbol("#kw##parse_inline_wrapper")), Array{Any, 1}, typeof(Base.Markdown.parse_inline_wrapper), Base.GenericIOBuffer{Array{UInt8, 1}}, String}) -precompile(Tuple{typeof(Base.Markdown.startswith), Base.GenericIOBuffer{Array{UInt8, 1}}, String}) -precompile(Tuple{typeof(Base.Markdown.blockinterp), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD}) -precompile(Tuple{getfield(Base, Symbol("#kw##parse")), Array{Any, 1}, typeof(Base.parse), Base.GenericIOBuffer{Array{UInt8, 1}}}) -precompile(Tuple{typeof(Base.Markdown.interpinner), Base.GenericIOBuffer{Array{UInt8, 1}}, Bool}) -precompile(Tuple{typeof(Base.Markdown.indentcode), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD}) -precompile(Tuple{typeof(Base.Markdown.footnote), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD}) -precompile(Tuple{Type{Base.Markdown.Footnote}, Nothing, Array{Any, 1}}) -precompile(Tuple{Type{Base.Markdown.Footnote}, Base.SubString{String}, Array{Any, 1}}) -precompile(Tuple{typeof(Base.Markdown.github_table), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD}) precompile(Tuple{typeof(Base.replace), String, Pair{String,String}}) precompile(Tuple{typeof(Base.pop!), Array{Base.SubString{String}, 1}}) precompile(Tuple{typeof(Base.union!), Base.Set{Char}, String}) @@ -705,53 +644,10 @@ precompile(Tuple{typeof(Core.Compiler.length), Tuple{Core.Compiler.Const, Core.C precompile(Tuple{typeof(Core.Compiler.getindex), Tuple{Core.Compiler.Const, Core.Compiler.Const, Core.Compiler.Const, Core.Compiler.Const, Core.Compiler.Const}, Int64}) precompile(Tuple{typeof(Base.throw_boundserror), Base.SubArray{UInt8, 1, Array{UInt8, 1}, Tuple{Base.UnitRange{Int64}}, true}, Tuple{Int64}}) precompile(Tuple{typeof(Base.peek), Base.GenericIOBuffer{Base.SubArray{UInt8, 1, Array{UInt8, 1}, Tuple{Base.UnitRange{Int64}}, true}}}) -precompile(Tuple{typeof(Base.Markdown.parseinline), Base.GenericIOBuffer{Base.SubArray{UInt8, 1, Array{UInt8, 1}, Tuple{Base.UnitRange{Int64}}, true}}, Base.Markdown.MD, Array{Function, 1}}) -precompile(Tuple{typeof(Base.read), Base.GenericIOBuffer{Base.SubArray{UInt8, 1, Array{UInt8, 1}, Tuple{Base.UnitRange{Int64}}, true}}, Type{Char}}) -precompile(Tuple{typeof(Base.Markdown.parseinline), Base.GenericIOBuffer{Base.SubArray{UInt8, 1, Array{UInt8, 1}, Tuple{Base.UnitRange{Int64}}, true}}, Base.Markdown.MD, Base.Markdown.Config}) -precompile(Tuple{typeof(Base.copyto!), Base.IndexLinear, Array{Array{Any, 1}, 1}, Base.IndexLinear, Array{Any, 1}}) -precompile(Tuple{typeof(Base.Markdown.parsealign), Nothing}) -precompile(Tuple{typeof(Base.Markdown.parsealign), Array{Base.SubString{String}, 1}}) -precompile(Tuple{typeof(Base.Markdown.rowlength!), Nothing, Int64}) -precompile(Tuple{typeof(Base.Markdown.rowlength!), Array{Base.SubString{String}, 1}, Int64}) -precompile(Tuple{Type{Base.Markdown.Table}, Array{Any, 1}, Nothing}) -precompile(Tuple{Type{Base.Markdown.Table}, Array{Any, 1}, Array{Symbol, 1}}) -precompile(Tuple{typeof(Base.Markdown.horizontalrule), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD}) -precompile(Tuple{typeof(Core.Compiler.isbits), Base.Markdown.HorizontalRule}) -precompile(Tuple{typeof(Base.Markdown.setextheader), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD}) -precompile(Tuple{typeof(Base.Markdown.paragraph), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD}) -precompile(Tuple{getfield(Base.Markdown, Symbol("#kw##parse")), Array{Any, 1}, typeof(Base.Markdown.parse), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD, Base.Markdown.Config}) -precompile(Tuple{getfield(Base.Markdown, Symbol("#kw##parse")), Array{Any, 1}, typeof(Base.Markdown.parse), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD}) -precompile(Tuple{typeof(Base.Markdown.inline_code), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.MD}) -precompile(Tuple{typeof(Base.Markdown.readuntil), Base.GenericIOBuffer{Array{UInt8, 1}}, String}) -precompile(Tuple{typeof(Base.Markdown.readuntil), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.SubString{String}}) precompile(Tuple{typeof(Base.setindex!), Base.Dict{Any, Any}, Module, Symbol}) precompile(Tuple{typeof(Base._setindex!), Base.Dict{Any, Any}, Module, Symbol, Int64}) precompile(Tuple{typeof(Base.setindex!), Base.Dict{Any, Any}, String, Symbol}) precompile(Tuple{typeof(Base._setindex!), Base.Dict{Any, Any}, String, Symbol, Int64}) -precompile(Tuple{typeof(Base.similar), Array{Base.Docs.DocStr, 1}, Type{Base.Markdown.MD}, Tuple{Base.OneTo{Int64}}}) -precompile(Tuple{typeof(Base.setindex!), Array{Base.Markdown.MD, 1}, Base.Markdown.MD, Int64}) -precompile(Tuple{typeof(Base.collect_to!), Array{Base.Markdown.MD, 1}, Base.Generator{Array{Base.Docs.DocStr, 1}, typeof(Base.Docs.parsedoc)}, Int64, Int64}) -precompile(Tuple{typeof(Base.unsafe_copyto!), Array{Base.Markdown.MD, 1}, Int64, Array{Base.Markdown.MD, 1}, Int64, Int64}) -precompile(Tuple{typeof(Base.Docs.catdoc), Base.Markdown.MD}) -precompile(Tuple{typeof(Base.copyto!), Base.IndexLinear, Array{Any, 1}, Base.IndexLinear, Array{Base.Markdown.MD, 1}}) -precompile(Tuple{Type{Base.Markdown.MD}, Array{Base.Markdown.MD, 1}}) -precompile(Tuple{Type{Base.Markdown.MD}, Base.Markdown.MD}) -precompile(Tuple{typeof(Base.vcat), Base.Markdown.MD}) -precompile(Tuple{typeof(Base.put!), Base.Channel{Any}, Tuple{Base.Markdown.MD, Nothing}}) -precompile(Tuple{typeof(Base.put_buffered), Base.Channel{Any}, Tuple{Base.Markdown.MD, Nothing}}) -precompile(Tuple{typeof(Base.put_unbuffered), Base.Channel{Any}, Tuple{Base.Markdown.MD, Nothing}}) -precompile(Tuple{typeof(Base.start), Tuple{Base.Markdown.MD, Nothing}}) -precompile(Tuple{typeof(Base.indexed_next), Tuple{Base.Markdown.MD, Nothing}, Int64, Int64}) -precompile(Tuple{typeof(Base.Multimedia.display), Base.Markdown.MD}) -precompile(Tuple{typeof(Core.Compiler.eltype), Type{Array{T, 2} where T}}) -precompile(Tuple{getfield(Base.Markdown, Symbol("#kw##wrapped_lines")), Array{Any, 1}, typeof(Base.Markdown.wrapped_lines), String}) -precompile(Tuple{typeof(Base.start), Tuple{Symbol, String}}) -precompile(Tuple{typeof(Base.Markdown.terminline), Base.GenericIOBuffer{Array{UInt8, 1}}, Array{Any, 1}}) -precompile(Tuple{typeof(Base.Markdown.terminline), Base.GenericIOBuffer{Array{UInt8, 1}}, String}) -precompile(Tuple{typeof(Base.Markdown.terminline), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Markdown.Code}) -precompile(Tuple{typeof(Base._search), Base.SubString{String}, String, Int64}) -precompile(Tuple{typeof(Base._split), Base.SubString{String}, String, Int64, Bool, Array{Base.SubString{String}, 1}}) -precompile(Tuple{getfield(Base.Markdown, Symbol("#kw##wrapped_lines")), Array{Any, 1}, typeof(Base.Markdown.wrapped_lines), Base.SubString{String}}) precompile(Tuple{typeof(Base.endof), Array{AbstractString, 1}}) precompile(Tuple{typeof(Base.getindex), Array{AbstractString, 1}, Base.UnitRange{Int64}}) precompile(Tuple{typeof(Base.throw_boundserror), Array{AbstractString, 1}, Tuple{Base.UnitRange{Int64}}}) diff --git a/base/sysimg.jl b/base/sysimg.jl index e940ad18b92f1..917efbdcfeca7 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -74,7 +74,7 @@ convert(::Type{T}, arg) where {T<:VecElement} = T(arg) convert(::Type{T}, arg::T) where {T<:VecElement} = arg # init core docsystem -import Core: @doc, @__doc__, @doc_str, WrappedException +import Core: @doc, @__doc__, WrappedException if isdefined(Core, :Compiler) import Core.Compiler.CoreDocs Core.atdoc!(CoreDocs.docm) @@ -476,9 +476,8 @@ include("deprecated.jl") include("docs/basedocs.jl") # Documentation -- should always be included last in sysimg. -include("markdown/Markdown.jl") include("docs/Docs.jl") -using .Docs, .Markdown +using .Docs isdefined(Core, :Compiler) && Docs.loaddocs(Core.Compiler.CoreDocs.DOCS) function __init__() @@ -526,6 +525,7 @@ Base.require(Base, :SuiteSparse) Base.require(Base, :Test) Base.require(Base, :Unicode) Base.require(Base, :REPL) +Base.require(Base, :Markdown) @eval Base begin @deprecate_binding Test root_module(Base, :Test) true ", run `using Test` instead" @@ -536,6 +536,7 @@ Base.require(Base, :REPL) @deprecate_binding Random root_module(Base, :Random) true ", run `using Random` instead" @deprecate_binding Serializer root_module(Base, :Serialization) true ", run `using Serialization` instead" @deprecate_binding Libdl root_module(Base, :Libdl) true ", run `using Libdl` instead" + @deprecate_binding Markdown root_module(Base, :Markdown) true ", run `using Markdown` instead" # PR #25249 @deprecate_binding SparseArrays root_module(Base, :SparseArrays) true ", run `using SparseArrays` instead" @@ -564,6 +565,8 @@ Base.require(Base, :REPL) @deprecate_binding REPLCompletions root_module(Base, :REPL).REPLCompletions true ", use `REPL.REPLCompletions` instead" @deprecate_binding Terminals root_module(Base, :REPL).Terminals true ", use `REPL.Terminals` instead" + @eval @deprecate_binding $(Symbol("@doc_str")) getfield(root_module(Base, :Markdown), Symbol("@doc_str")) true ", use `Markdown` instead" + @deprecate_stdlib readdlm DelimitedFiles true @deprecate_stdlib writedlm DelimitedFiles true @deprecate_stdlib readcsv DelimitedFiles true diff --git a/doc/src/base/base.md b/doc/src/base/base.md index ff24b70960b08..6bd021c94b183 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -89,7 +89,6 @@ Base.Docs Base.Iterators Base.LibGit2 Base.Libc -Base.Markdown Base.Meta Base.Pkg Base.StackTraces diff --git a/stdlib/Markdown/docs/src/index.md b/stdlib/Markdown/docs/src/index.md new file mode 100644 index 0000000000000..7d82df342b1fa --- /dev/null +++ b/stdlib/Markdown/docs/src/index.md @@ -0,0 +1 @@ +# Markdown diff --git a/base/markdown/Common/Common.jl b/stdlib/Markdown/src/Common/Common.jl similarity index 100% rename from base/markdown/Common/Common.jl rename to stdlib/Markdown/src/Common/Common.jl diff --git a/base/markdown/Common/block.jl b/stdlib/Markdown/src/Common/block.jl similarity index 100% rename from base/markdown/Common/block.jl rename to stdlib/Markdown/src/Common/block.jl diff --git a/base/markdown/Common/inline.jl b/stdlib/Markdown/src/Common/inline.jl similarity index 100% rename from base/markdown/Common/inline.jl rename to stdlib/Markdown/src/Common/inline.jl diff --git a/base/markdown/GitHub/GitHub.jl b/stdlib/Markdown/src/GitHub/GitHub.jl similarity index 100% rename from base/markdown/GitHub/GitHub.jl rename to stdlib/Markdown/src/GitHub/GitHub.jl diff --git a/base/markdown/GitHub/table.jl b/stdlib/Markdown/src/GitHub/table.jl similarity index 100% rename from base/markdown/GitHub/table.jl rename to stdlib/Markdown/src/GitHub/table.jl diff --git a/base/markdown/IPython/IPython.jl b/stdlib/Markdown/src/IPython/IPython.jl similarity index 100% rename from base/markdown/IPython/IPython.jl rename to stdlib/Markdown/src/IPython/IPython.jl diff --git a/base/markdown/Julia/Julia.jl b/stdlib/Markdown/src/Julia/Julia.jl similarity index 100% rename from base/markdown/Julia/Julia.jl rename to stdlib/Markdown/src/Julia/Julia.jl diff --git a/base/markdown/Julia/interp.jl b/stdlib/Markdown/src/Julia/interp.jl similarity index 100% rename from base/markdown/Julia/interp.jl rename to stdlib/Markdown/src/Julia/interp.jl diff --git a/base/markdown/Markdown.jl b/stdlib/Markdown/src/Markdown.jl similarity index 97% rename from base/markdown/Markdown.jl rename to stdlib/Markdown/src/Markdown.jl index 7fa7729b00cb5..0171e360ff404 100644 --- a/base/markdown/Markdown.jl +++ b/stdlib/Markdown/src/Markdown.jl @@ -6,7 +6,6 @@ Tools for working with the Markdown file format. Mainly for documentation. module Markdown import Base: show, ==, with_output_color -import Core: @doc_str include(joinpath("parse", "config.jl")) include(joinpath("parse", "util.jl")) @@ -59,4 +58,8 @@ macro doc_str(s::AbstractString, t...) docexpr(__source__, __module__, s, t...) end +import Base.Docs: catdoc + +catdoc(md::MD...) = MD(md...) + end diff --git a/base/markdown/parse/config.jl b/stdlib/Markdown/src/parse/config.jl similarity index 100% rename from base/markdown/parse/config.jl rename to stdlib/Markdown/src/parse/config.jl diff --git a/base/markdown/parse/parse.jl b/stdlib/Markdown/src/parse/parse.jl similarity index 100% rename from base/markdown/parse/parse.jl rename to stdlib/Markdown/src/parse/parse.jl diff --git a/base/markdown/parse/util.jl b/stdlib/Markdown/src/parse/util.jl similarity index 100% rename from base/markdown/parse/util.jl rename to stdlib/Markdown/src/parse/util.jl diff --git a/base/markdown/render/html.jl b/stdlib/Markdown/src/render/html.jl similarity index 100% rename from base/markdown/render/html.jl rename to stdlib/Markdown/src/render/html.jl diff --git a/base/markdown/render/latex.jl b/stdlib/Markdown/src/render/latex.jl similarity index 100% rename from base/markdown/render/latex.jl rename to stdlib/Markdown/src/render/latex.jl diff --git a/base/markdown/render/plain.jl b/stdlib/Markdown/src/render/plain.jl similarity index 100% rename from base/markdown/render/plain.jl rename to stdlib/Markdown/src/render/plain.jl diff --git a/base/markdown/render/rich.jl b/stdlib/Markdown/src/render/rich.jl similarity index 100% rename from base/markdown/render/rich.jl rename to stdlib/Markdown/src/render/rich.jl diff --git a/base/markdown/render/rst.jl b/stdlib/Markdown/src/render/rst.jl similarity index 100% rename from base/markdown/render/rst.jl rename to stdlib/Markdown/src/render/rst.jl diff --git a/base/markdown/render/terminal/formatting.jl b/stdlib/Markdown/src/render/terminal/formatting.jl similarity index 100% rename from base/markdown/render/terminal/formatting.jl rename to stdlib/Markdown/src/render/terminal/formatting.jl diff --git a/base/markdown/render/terminal/render.jl b/stdlib/Markdown/src/render/terminal/render.jl similarity index 100% rename from base/markdown/render/terminal/render.jl rename to stdlib/Markdown/src/render/terminal/render.jl diff --git a/test/markdown.jl b/stdlib/Markdown/test/runtests.jl similarity index 99% rename from test/markdown.jl rename to stdlib/Markdown/test/runtests.jl index d344e3eabec17..6fb93adcd0eec 100644 --- a/test/markdown.jl +++ b/stdlib/Markdown/test/runtests.jl @@ -1,7 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -using Base.Markdown -import Base.Markdown: MD, Paragraph, Header, Italic, Bold, LineBreak, plain, term, html, rst, Table, Code, LaTeX, Footnote +using Markdown +import Markdown: MD, Paragraph, Header, Italic, Bold, LineBreak, plain, term, html, rst, Table, Code, LaTeX, Footnote import Base: show # Basics diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 9436e1b2732f1..748e354688a4c 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -42,6 +42,8 @@ import ..LineEdit: include("REPLCompletions.jl") using .REPLCompletions +include("docview.jl") + function __init__() Base.REPL_MODULE_REF[] = REPL end @@ -802,7 +804,7 @@ function setup_interface( repl = repl, complete = replc, # When we're done transform the entered line into a call to help("$line") - on_done = respond(Docs.helpmode, repl, julia_prompt, pass_empty=true)) + on_done = respond(helpmode, repl, julia_prompt, pass_empty=true)) # Set up shell mode shell_mode = Prompt("shell> "; diff --git a/stdlib/REPL/src/docview.jl b/stdlib/REPL/src/docview.jl new file mode 100644 index 0000000000000..518a134f39ffa --- /dev/null +++ b/stdlib/REPL/src/docview.jl @@ -0,0 +1,582 @@ +## Code for searching and viewing documentation + +using Markdown + +using Base.Docs: catdoc, modules, DocStr, Binding, MultiDoc, keywords, isfield, namify, bindingexpr, + defined, resolve, getdoc, meta, aliasof, signature + +import Base.Docs: doc, formatdoc, parsedoc, apropos + +using Base: with_output_color + +## Help mode ## + +# This is split into helpmode and _helpmode to easier unittest _helpmode +helpmode(io::IO, line::AbstractString) = :($REPL.insert_hlines($io, $(REPL._helpmode(io, line)))) +helpmode(line::AbstractString) = helpmode(STDOUT, line) + +function _helpmode(io::IO, line::AbstractString) + line = strip(line) + expr = + if haskey(keywords, Symbol(line)) + # Docs for keywords must be treated separately since trying to parse a single + # keyword such as `function` would throw a parse error due to the missing `end`. + Symbol(line) + else + x = Meta.parse(line, raise = false, depwarn = false) + # Retrieving docs for macros requires us to make a distinction between the text + # `@macroname` and `@macroname()`. These both parse the same, but are used by + # the docsystem to return different results. The first returns all documentation + # for `@macroname`, while the second returns *only* the docs for the 0-arg + # definition if it exists. + (isexpr(x, :macrocall, 1) && !endswith(line, "()")) ? quot(x) : x + end + # the following must call repl(io, expr) via the @repl macro + # so that the resulting expressions are evaluated in the Base.Docs namespace + :($REPL.@repl $io $expr) +end +_helpmode(line::AbstractString) = _helpmode(STDOUT, line) + +# Print vertical lines along each docstring if there are multiple docs +function insert_hlines(io::IO, docs) + if !isa(docs, Markdown.MD) || !haskey(docs.meta, :results) || isempty(docs.meta[:results]) + return docs + end + v = Any[] + for (n, doc) in enumerate(docs.content) + push!(v, doc) + n == length(docs.content) || push!(v, Markdown.HorizontalRule()) + end + return Markdown.MD(v) +end + +function formatdoc(d::DocStr) + buffer = IOBuffer() + for part in d.text + formatdoc(buffer, d, part) + end + Markdown.MD(Any[Markdown.parse(seekstart(buffer))]) +end +@noinline formatdoc(buffer, d, part) = print(buffer, part) + +function parsedoc(d::DocStr) + if d.object === nothing + md = formatdoc(d) + md.meta[:module] = d.data[:module] + md.meta[:path] = d.data[:path] + d.object = md + end + d.object +end + +""" + 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 = DocStr[], MultiDoc[] + # 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]) + 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 + 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...}) + +function lookup_doc(ex) + if haskey(keywords, ex) + parsedoc(keywords[ex]) + elseif isa(ex, Union{Expr, Symbol}) + binding = esc(bindingexpr(namify(ex))) + if isexpr(ex, :call) || isexpr(ex, :macrocall) + sig = esc(signature(ex)) + :($(doc)($binding, $sig)) + else + :($(doc)($binding)) + end + else + :($(doc)($(typeof)($(esc(ex))))) + end +end + +# 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 + +# repl search and completions for help + +function repl_search(io::IO, s) + pre = "search:" + print(io, pre) + printmatches(io, s, doc_completions(s), cols = displaysize(io)[2] - length(pre)) + println(io, "\n") +end +repl_search(s) = repl_search(STDOUT, s) + +function repl_corrections(io::IO, s) + print(io, "Couldn't find ") + print_with_color(:cyan, io, s, '\n') + print_correction(io, s) +end +repl_corrections(s) = repl_corrections(STDOUT, s) + +# inverse of latex_symbols Dict, lazily created as needed +const symbols_latex = Dict{String,String}() +function symbol_latex(s::String) + if isempty(symbols_latex) && isassigned(Base.REPL_MODULE_REF) + for (k,v) in Base.REPL_MODULE_REF[].REPLCompletions.latex_symbols + symbols_latex[v] = k + end + end + return get(symbols_latex, s, "") +end +function repl_latex(io::IO, s::String) + latex = symbol_latex(s) + if !isempty(latex) + print(io, "\"") + print_with_color(:cyan, io, s) + print(io, "\" can be typed by ") + print_with_color(:cyan, io, latex, "") + println(io, '\n') + elseif any(c -> haskey(symbols_latex, string(c)), s) + print(io, "\"") + print_with_color(:cyan, io, s) + print(io, "\" can be typed by ") + with_output_color(:cyan, io) do io + for c in s + cstr = string(c) + if haskey(symbols_latex, cstr) + print(io, symbols_latex[cstr], "") + else + print(io, c) + end + end + end + println(io, '\n') + end +end +repl_latex(s::String) = repl_latex(STDOUT, s) + +macro repl(ex) repl(ex) end +macro repl(io, ex) repl(io, ex) end + +function repl(io::IO, s::Symbol) + str = string(s) + quote + repl_latex($io, $str) + repl_search($io, $str) + $(if !isdefined(Main, s) && !haskey(keywords, s) + :(repl_corrections($io, $str)) + end) + $(_repl(s)) + end +end +isregex(x) = isexpr(x, :macrocall, 3) && x.args[1] === Symbol("@r_str") && !isempty(x.args[3]) +repl(io::IO, ex::Expr) = isregex(ex) ? :(apropos($io, $ex)) : _repl(ex) +repl(io::IO, str::AbstractString) = :(apropos($io, $str)) +repl(io::IO, other) = esc(:(@doc $other)) +#repl(io::IO, other) = lookup_doc(other) # TODO + +repl(x) = repl(STDOUT, x) + +function _repl(x) + if (isexpr(x, :call) && !any(isexpr(x, :(::)) for x in x.args)) + x.args[2:end] = [:(::typeof($arg)) for arg in x.args[2:end]] + end + #docs = lookup_doc(x) # TODO + docs = esc(:(@doc $x)) + if isfield(x) + quote + if isa($(esc(x.args[1])), DataType) + fielddoc($(esc(x.args[1])), $(esc(x.args[2]))) + else + $docs + end + end + else + docs + end +end + +""" + 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) + + +# Search & Rescue +# Utilities for correcting user mistakes and (eventually) +# doing full documentation searches from the repl. + +# Fuzzy Search Algorithm + +function matchinds(needle, haystack; acronym = false) + chars = collect(needle) + is = Int[] + lastc = '\0' + for (i, char) in enumerate(haystack) + isempty(chars) && break + while chars[1] == ' ' popfirst!(chars) end # skip spaces + if lowercase(char) == lowercase(chars[1]) && + (!acronym || !isalpha(lastc)) + push!(is, i) + popfirst!(chars) + end + lastc = char + end + return is +end + +longer(x, y) = length(x) ≥ length(y) ? (x, true) : (y, false) + +bestmatch(needle, haystack) = + longer(matchinds(needle, haystack, acronym = true), + matchinds(needle, haystack)) + +avgdistance(xs) = + isempty(xs) ? 0 : + (xs[end] - xs[1] - length(xs)+1)/length(xs) + +function fuzzyscore(needle, haystack) + score = 0. + is, acro = bestmatch(needle, haystack) + score += (acro ? 2 : 1)*length(is) # Matched characters + score -= 2(length(needle)-length(is)) # Missing characters + !acro && (score -= avgdistance(is)/10) # Contiguous + !isempty(is) && (score -= mean(is)/100) # Closer to beginning + return score +end + +function fuzzysort(search, candidates) + scores = map(cand -> (fuzzyscore(search, cand), -levenshtein(search, cand)), candidates) + candidates[sortperm(scores)] |> reverse +end + +# Levenshtein Distance + +function levenshtein(s1, s2) + a, b = collect(s1), collect(s2) + m = length(a) + n = length(b) + d = Matrix{Int}(uninitialized, m+1, n+1) + + d[1:m+1, 1] = 0:m + d[1, 1:n+1] = 0:n + + for i = 1:m, j = 1:n + d[i+1,j+1] = min(d[i , j+1] + 1, + d[i+1, j ] + 1, + d[i , j ] + (a[i] != b[j])) + end + + return d[m+1, n+1] +end + +function levsort(search, candidates) + scores = map(cand -> (levenshtein(search, cand), -fuzzyscore(search, cand)), candidates) + candidates = candidates[sortperm(scores)] + i = 0 + for outer i = 1:length(candidates) + levenshtein(search, candidates[i]) > 3 && break + end + return candidates[1:i] +end + +# Result printing + +function printmatch(io::IO, word, match) + is, _ = bestmatch(word, match) + for (i, char) = enumerate(match) + if i in is + print_with_color(:bold, io, char) + else + print(io, char) + end + end +end + +printmatch(args...) = printfuzzy(STDOUT, args...) + +function printmatches(io::IO, word, matches; cols = displaysize(io)[2]) + total = 0 + for match in matches + total + length(match) + 1 > cols && break + fuzzyscore(word, match) < 0 && break + print(io, " ") + printmatch(io, word, match) + total += length(match) + 1 + end +end + +printmatches(args...; cols = displaysize(STDOUT)[2]) = printmatches(STDOUT, args..., cols = cols) + +function print_joined_cols(io::IO, ss, delim = "", last = delim; cols = displaysize(io)[2]) + i = 0 + total = 0 + for outer i = 1:length(ss) + total += length(ss[i]) + total + max(i-2,0)*length(delim) + (i>1 ? 1 : 0)*length(last) > cols && (i-=1; break) + end + join(io, ss[1:i], delim, last) +end + +print_joined_cols(args...; cols = displaysize(STDOUT)[2]) = print_joined_cols(STDOUT, args...; cols=cols) + +function print_correction(io, word) + cors = levsort(word, accessible(Main)) + pre = "Perhaps you meant " + print(io, pre) + print_joined_cols(io, cors, ", ", " or "; cols = displaysize(io)[2] - length(pre)) + println(io) + return +end + +print_correction(word) = print_correction(STDOUT, word) + +# Completion data + +const builtins = ["abstract type", "baremodule", "begin", "break", + "catch", "ccall", "const", "continue", "do", "else", + "elseif", "end", "export", "finally", "for", "function", + "global", "if", "import", "let", + "local", "macro", "module", "mutable struct", "primitive type", + "quote", "return", "struct", "try", "using", "while"] + +moduleusings(mod) = ccall(:jl_module_usings, Any, (Any,), mod) + +filtervalid(names) = filter(x->!contains(x, r"#"), map(string, names)) + +accessible(mod::Module) = + [filter!(s -> !Base.isdeprecated(mod, s), names(mod, all = true, imported = true)); + map(names, moduleusings(mod))...; + builtins] |> unique |> filtervalid + +doc_completions(name) = fuzzysort(name, accessible(Main)) +doc_completions(name::Symbol) = doc_completions(string(name)) + + +# Searching and apropos + +# Docsearch simply returns true or false if an object contains the given needle +docsearch(haystack::AbstractString, needle) = !isempty(findfirst(needle, haystack)) +docsearch(haystack::Symbol, needle) = docsearch(string(haystack), needle) +docsearch(::Nothing, needle) = false +function docsearch(haystack::Array, needle) + for elt in haystack + docsearch(elt, needle) && return true + end + false +end +function docsearch(haystack, needle) + @warn "Unable to search documentation of type $(typeof(haystack))" maxlog=1 + false +end + +## Searching specific documentation objects +function docsearch(haystack::MultiDoc, needle) + for v in values(haystack.docs) + docsearch(v, needle) && return true + end + false +end + +function docsearch(haystack::DocStr, needle) + docsearch(parsedoc(haystack), needle) && return true + if haskey(haystack.data, :fields) + for doc in values(haystack.data[:fields]) + docsearch(doc, needle) && return true + end + end + false +end + +## doc search + +## Markdown search simply strips all markup and searches plain text version +docsearch(haystack::Markdown.MD, needle) = docsearch(stripmd(haystack.content), needle) + +""" + stripmd(x) + +Strip all Markdown markup from x, leaving the result in plain text. Used +internally by apropos to make docstrings containing more than one markdown +element searchable. +""" +stripmd(@nospecialize x) = string(x) # for random objects interpolated into the docstring +stripmd(x::AbstractString) = x # base case +stripmd(x::Nothing) = " " +stripmd(x::Vector) = string(map(stripmd, x)...) + +stripmd(x::Markdown.BlockQuote) = "$(stripmd(x.content))" +stripmd(x::Markdown.Admonition) = "$(stripmd(x.content))" +stripmd(x::Markdown.Bold) = "$(stripmd(x.text))" +stripmd(x::Markdown.Code) = "$(stripmd(x.code))" +stripmd(x::Markdown.Header) = stripmd(x.text) +stripmd(x::Markdown.HorizontalRule) = " " +stripmd(x::Markdown.Image) = "$(stripmd(x.alt)) $(x.url)" +stripmd(x::Markdown.Italic) = "$(stripmd(x.text))" +stripmd(x::Markdown.LaTeX) = "$(x.formula)" +stripmd(x::Markdown.LineBreak) = " " +stripmd(x::Markdown.Link) = "$(stripmd(x.text)) $(x.url)" +stripmd(x::Markdown.List) = join(map(stripmd, x.items), " ") +stripmd(x::Markdown.MD) = join(map(stripmd, x.content), " ") +stripmd(x::Markdown.Paragraph) = stripmd(x.content) +stripmd(x::Markdown.Footnote) = "$(stripmd(x.id)) $(stripmd(x.text))" +stripmd(x::Markdown.Table) = + join([join(map(stripmd, r), " ") for r in x.rows], " ") + +# Apropos searches through all available documentation for some string or regex +""" + apropos(string) + +Search through all documentation for a string, ignoring case. +""" +apropos(string) = apropos(STDOUT, string) +apropos(io::IO, string) = apropos(io, Regex("\\Q$string", "i")) +function apropos(io::IO, needle::Regex) + for mod in modules + # Module doc might be in README.md instead of the META dict + docsearch(doc(mod), needle) && println(io, mod) + for (k, v) in meta(mod) + docsearch(v, needle) && println(io, k) + end + end +end diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index 87042ba8aff2d..e8c521b0c5f99 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -223,15 +223,10 @@ function _precompile_() precompile(Tuple{typeof(Base.:(==)), Symbol, REPL.LineEdit.Prompt}) precompile(Tuple{typeof(Base.isempty), REPL.LineEdit.PromptState}) precompile(Tuple{typeof(REPL.LineEdit.default_enter_cb), REPL.LineEdit.PromptState}) - precompile(Tuple{typeof(Base.Multimedia.display), REPLDisplay{REPL.LineEditREPL}, Base.Markdown.MD}) precompile(Tuple{typeof(Base.displaysize), REPL.Terminals.TTYTerminal}) - precompile(Tuple{typeof(Base.Markdown.term), REPL.Terminals.TTYTerminal, Array{Any, 1}, Int64}) - precompile(Tuple{typeof(Base.Markdown.term), REPL.Terminals.TTYTerminal, Base.Markdown.MD, Int64}) - precompile(Tuple{typeof(Base.Markdown.term), REPL.Terminals.TTYTerminal, Base.Markdown.Code, Int64}) precompile(Tuple{typeof(Base.write), REPL.Terminals.TTYTerminal, Base.SubString{String}}) precompile(Tuple{typeof(Base.print), REPL.Terminals.TTYTerminal, Base.SubString{String}}) precompile(Tuple{typeof(Base.print), REPL.Terminals.TTYTerminal, Base.SubString{String}, Char}) - precompile(Tuple{typeof(Base.Markdown.term), REPL.Terminals.TTYTerminal, Base.Markdown.Paragraph, Int64}) precompile(Tuple{typeof(Base.Multimedia.display), REPLDisplay{REPL.LineEditREPL}, Array{Int64, 1}}) precompile(Tuple{typeof(Base.Multimedia.display), REPLDisplay{REPL.LineEditREPL}, Base.MIME{Symbol("text/plain")}, Array{Int64, 1}}) precompile(Tuple{typeof(Base.show_delim_array), Base.IOContext{REPL.Terminals.TTYTerminal}, Array{Int64, 1}, String, String, String, Bool, Int64, Int64}) diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index bff4134d65657..53e12742de08e 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -3,6 +3,7 @@ using Test using REPL using Random import REPL.LineEdit +using Markdown const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") isdefined(Main, :TestHelpers) || @eval Main include(joinpath($(BASE_TEST_PATH), "TestHelpers.jl")) @@ -877,3 +878,24 @@ fake_repl() do stdin_write, stdout_read, repl write(stdin_write, '\x04') wait(repltask) end + +# Docs.helpmode tests: we test whether the correct expressions are being generated here, +# rather than complete integration with Julia's REPL mode system. +for (line, expr) in Pair[ + "sin" => :sin, + "Base.sin" => :(Base.sin), + "@time(x)" => Expr(:macrocall, Symbol("@time"), LineNumberNode(1, :none), :x), + "@time" => Expr(:macrocall, Symbol("@time"), LineNumberNode(1, :none)), + ":@time" => Expr(:quote, (Expr(:macrocall, Symbol("@time"), LineNumberNode(1, :none)))), + "@time()" => Expr(:macrocall, Symbol("@time"), LineNumberNode(1, :none)), + "Base.@time()" => Expr(:macrocall, Expr(:., :Base, QuoteNode(Symbol("@time"))), LineNumberNode(1, :none)), + "ccall" => :ccall, # keyword + "while " => :while, # keyword, trailing spaces should be stripped. + "0" => 0, + "\"...\"" => "...", + "r\"...\"" => Expr(:macrocall, Symbol("@r_str"), LineNumberNode(1, :none), "...") + ] + #@test REPL._helpmode(line) == Expr(:macrocall, Expr(:., Expr(:., :Base, QuoteNode(:Docs)), QuoteNode(Symbol("@repl"))), LineNumberNode(119, doc_util_path), STDOUT, expr) + buf = IOBuffer() + @test eval(Base, REPL._helpmode(buf, line)) isa Union{Markdown.MD,Nothing} +end diff --git a/test/choosetests.jl b/test/choosetests.jl index 0dae90ff4e05a..899375ba8aa02 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -49,7 +49,7 @@ function choosetests(choices = []) "euler", "show", "errorshow", "sets", "goto", "llvmcall", "llvmcall2", "grisu", "some", "meta", "stacktraces", "libgit2", "docs", - "markdown", "misc", "threads", + "misc", "threads", "enums", "cmdlineargs", "i18n", "int", "checked", "bitset", "floatfuncs", "compile", "inline", "boundscheck", "error", "ambiguous", "cartesian", "asmvariant", "osutils", diff --git a/test/compile.jl b/test/compile.jl index e21218855576a..96006e71c219a 100644 --- a/test/compile.jl +++ b/test/compile.jl @@ -214,7 +214,7 @@ try Dict(let m = Base.root_module(Base, s) Base.PkgId(m) => Base.module_build_id(m) end for s in - [:Base64, :CRC32c, :Dates, :DelimitedFiles, :Distributed, :FileWatching, + [:Base64, :CRC32c, :Dates, :DelimitedFiles, :Distributed, :FileWatching, :Markdown, :Future, :IterativeEigensolvers, :Libdl, :LinearAlgebra, :Logging, :Mmap, :Printf, :Profile, :Random, :Serialization, :SharedArrays, :SparseArrays, :SuiteSparse, :Test, :Unicode, :REPL])) diff --git a/test/docs.jl b/test/docs.jl index d40357fe3301b..2d6edef57dc11 100644 --- a/test/docs.jl +++ b/test/docs.jl @@ -2,6 +2,11 @@ import Base.Docs: meta, @var, DocStr, parsedoc +using Markdown +using REPL + +using REPL: @repl, repl_latex, _repl, accessible + # For curmod_* include("testenv.jl") @@ -67,6 +72,8 @@ const LINE_NUMBER = @__LINE__() + 1 "DocsTest" module DocsTest +using Markdown + "f-1" function f(x) x @@ -523,6 +530,8 @@ end module I15424 +using REPL + struct LazyHelp text end @@ -541,7 +550,7 @@ function Base.Docs.catdoc(hs::LazyHelp...) end end -Docs.docsearch(haystack::LazyHelp, needle) = Docs.docsearch(haystack.text, needle) +REPL.docsearch(haystack::LazyHelp, needle) = REPL.docsearch(haystack.text, needle) @doc LazyHelp("LazyHelp\n") LazyHelp @doc LazyHelp("LazyHelp(text)\n") LazyHelp(text) @@ -646,17 +655,17 @@ f12593_2() = 1 Docs.doc(getindex, Tuple{Type{Int64},Int})) # test that macro documentation works -@test (Docs.@repl :@assert) !== nothing +@test (@repl :@assert) !== nothing -@test (Docs.@repl 0) !== nothing +@test (@repl 0) !== nothing let t = @doc(DocsTest.t(::Int, ::Int)) - @test docstrings_equal(Docs.@repl(DocsTest.t(0, 0)), t) - @test docstrings_equal(Docs.@repl(DocsTest.t(::Int, ::Int)), t) + @test docstrings_equal(@repl(DocsTest.t(0, 0)), t) + @test docstrings_equal(@repl(DocsTest.t(::Int, ::Int)), t) end # Issue #13467. -@test (Docs.@repl :@r_str) !== nothing +@test (@repl :@r_str) !== nothing # Simple tests for apropos: @test contains(sprint(apropos, "pearson"), "cor") @@ -942,29 +951,9 @@ let x = Binding(Main, :⊕) end doc_util_path = Symbol(joinpath("docs", "utils.jl")) -# Docs.helpmode tests: we test whether the correct expressions are being generated here, -# rather than complete integration with Julia's REPL mode system. -for (line, expr) in Pair[ - "sin" => :sin, - "Base.sin" => :(Base.sin), - "@time(x)" => Expr(:macrocall, Symbol("@time"), LineNumberNode(1, :none), :x), - "@time" => Expr(:macrocall, Symbol("@time"), LineNumberNode(1, :none)), - ":@time" => Expr(:quote, (Expr(:macrocall, Symbol("@time"), LineNumberNode(1, :none)))), - "@time()" => Expr(:macrocall, Symbol("@time"), LineNumberNode(1, :none)), - "Base.@time()" => Expr(:macrocall, Expr(:., :Base, QuoteNode(Symbol("@time"))), LineNumberNode(1, :none)), - "ccall" => :ccall, # keyword - "while " => :while, # keyword, trailing spaces should be stripped. - "0" => 0, - "\"...\"" => "...", - "r\"...\"" => Expr(:macrocall, Symbol("@r_str"), LineNumberNode(1, :none), "...") - ] - @test Docs._helpmode(line) == Expr(:macrocall, Expr(:., Expr(:., :Base, QuoteNode(:Docs)), QuoteNode(Symbol("@repl"))), LineNumberNode(119, doc_util_path), STDOUT, expr) - buf = IOBuffer() - @test eval(Base, Docs._helpmode(buf, line)) isa Union{Base.Markdown.MD,Nothing} -end - -@test sprint(Base.Docs.repl_latex, "√") == "\"√\" can be typed by \\sqrt\n\n" -@test sprint(Base.Docs.repl_latex, "x̂₂") == "\"x̂₂\" can be typed by x\\hat\\_2\n\n" + +@test sprint(repl_latex, "√") == "\"√\" can be typed by \\sqrt\n\n" +@test sprint(repl_latex, "x̂₂") == "\"x̂₂\" can be typed by x\\hat\\_2\n\n" # issue #15684 begin @@ -990,10 +979,20 @@ dynamic_test.x = "test 2" @test @doc(dynamic_test) == "test 2 Union{}" @test @doc(dynamic_test(::String)) == "test 2 Tuple{String}" -@test Docs._repl(:(dynamic_test(1.0))) == Expr(:escape, Expr(:macrocall, Symbol("@doc"), LineNumberNode(214, doc_util_path), :(dynamic_test(::typeof(1.0))))) -@test Docs._repl(:(dynamic_test(::String))) == Expr(:escape, Expr(:macrocall, Symbol("@doc"), LineNumberNode(214, doc_util_path), :(dynamic_test(::String)))) - - +let dt1 = _repl(:(dynamic_test(1.0))) + @test dt1 isa Expr + @test dt1.args[1] isa Expr + @test dt1.args[1].head === :macrocall + @test dt1.args[1].args[1] == Symbol("@doc") + @test dt1.args[1].args[3] == :(dynamic_test(::typeof(1.0))) +end +let dt2 = _repl(:(dynamic_test(::String))) + @test dt2 isa Expr + @test dt2.args[1] isa Expr + @test dt2.args[1].head === :macrocall + @test dt2.args[1].args[1] == Symbol("@doc") + @test dt2.args[1].args[3] == :(dynamic_test(::String)) +end # Equality testing @@ -1090,7 +1089,7 @@ end end struct t_docs_abc end -@test "t_docs_abc" in Docs.accessible(@__MODULE__) +@test "t_docs_abc" in accessible(@__MODULE__) # Call overloading issue #20087 """