fix incorrect folder name for julia-0.6.x
Former-commit-id: ef2c7401e0876f22d2f7762d182cfbcd5a7d9c70
This commit is contained in:
455
julia-0.6.3/share/julia/base/docs/utils.jl
Normal file
455
julia-0.6.3/share/julia/base/docs/utils.jl
Normal file
@@ -0,0 +1,455 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
# Text / HTML objects
|
||||
|
||||
import Base: print, show, ==, hash
|
||||
|
||||
export HTML, @html_str
|
||||
|
||||
export HTML, Text, apropos
|
||||
|
||||
"""
|
||||
`HTML(s)`: Create an object that renders `s` as html.
|
||||
|
||||
HTML("<div>foo</div>")
|
||||
|
||||
You can also use a stream for large amounts of data:
|
||||
|
||||
HTML() do io
|
||||
println(io, "<div>foo</div>")
|
||||
end
|
||||
"""
|
||||
mutable struct HTML{T}
|
||||
content::T
|
||||
end
|
||||
|
||||
function HTML(xs...)
|
||||
HTML() do io
|
||||
for x in xs
|
||||
show(io, MIME"text/html"(), x)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
show(io::IO, ::MIME"text/html", h::HTML) = print(io, h.content)
|
||||
show(io::IO, ::MIME"text/html", h::HTML{<:Function}) = h.content(io)
|
||||
|
||||
"""
|
||||
@html_str -> Docs.HTML
|
||||
|
||||
Create an `HTML` object from a literal string.
|
||||
"""
|
||||
macro html_str(s)
|
||||
:(HTML($s))
|
||||
end
|
||||
|
||||
function catdoc(xs::HTML...)
|
||||
HTML() do io
|
||||
for x in xs
|
||||
show(io, MIME"text/html"(), x)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
export Text, @text_str
|
||||
|
||||
"""
|
||||
`Text(s)`: Create an object that renders `s` as plain text.
|
||||
|
||||
Text("foo")
|
||||
|
||||
You can also use a stream for large amounts of data:
|
||||
|
||||
Text() do io
|
||||
println(io, "foo")
|
||||
end
|
||||
"""
|
||||
mutable struct Text{T}
|
||||
content::T
|
||||
end
|
||||
|
||||
print(io::IO, t::Text) = print(io, t.content)
|
||||
print(io::IO, t::Text{<:Function}) = t.content(io)
|
||||
show(io::IO, t::Text) = print(io, t)
|
||||
|
||||
==(t1::T, t2::T) where {T<:Union{HTML,Text}} = t1.content == t2.content
|
||||
hash(t::T, h::UInt) where {T<:Union{HTML,Text}} = hash(T, hash(t.content, h))
|
||||
|
||||
"""
|
||||
@text_str -> Docs.Text
|
||||
|
||||
Create a `Text` object from a literal string.
|
||||
"""
|
||||
macro text_str(s)
|
||||
:(Text($s))
|
||||
end
|
||||
|
||||
function catdoc(xs::Text...)
|
||||
Text() do io
|
||||
for x in xs
|
||||
show(io, MIME"text/plain"(), x)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# REPL help
|
||||
|
||||
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 = Base.syntax_deprecation_warnings(false) do
|
||||
parse(line, raise = false)
|
||||
end
|
||||
# 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)
|
||||
|
||||
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 ")
|
||||
Markdown.with_output_format(:cyan, io) do io
|
||||
println(io, s)
|
||||
end
|
||||
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)
|
||||
for (k,v) in Base.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, "\"")
|
||||
Markdown.with_output_format(:cyan, io) do io
|
||||
print(io, s)
|
||||
end
|
||||
print(io, "\" can be typed by ")
|
||||
Markdown.with_output_format(:cyan, io) do io
|
||||
print(io, latex, "<tab>")
|
||||
end
|
||||
println(io, '\n')
|
||||
elseif any(c -> haskey(symbols_latex, string(c)), s)
|
||||
print(io, "\"")
|
||||
Markdown.with_output_format(:cyan, io) do io
|
||||
print(io, s)
|
||||
end
|
||||
print(io, "\" can be typed by ")
|
||||
Markdown.with_output_format(:cyan, io) do io
|
||||
for c in s
|
||||
cstr = string(c)
|
||||
if haskey(symbols_latex, cstr)
|
||||
print(io, symbols_latex[cstr], "<tab>")
|
||||
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)
|
||||
($(isdefined(s) || haskey(keywords, s))) || repl_corrections($io, $str)
|
||||
$(_repl(s))
|
||||
end
|
||||
end
|
||||
isregex(x) = isexpr(x, :macrocall, 2) && x.args[1] === Symbol("@r_str") && !isempty(x.args[2])
|
||||
repl(io::IO, ex::Expr) = isregex(ex) ? :(apropos($io, $ex)) : _repl(ex)
|
||||
repl(io::IO, str::AbstractString) = :(apropos($io, $str))
|
||||
repl(io::IO, other) = :(@doc $(esc(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 = :(@doc $(esc(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] == ' ' shift!(chars) end # skip spaces
|
||||
if lowercase(char) == lowercase(chars[1]) && (!acronym || !isalpha(lastc))
|
||||
push!(is, i)
|
||||
shift!(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}(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 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)
|
||||
Markdown.with_output_format(:fade, io) do io
|
||||
for (i, char) = enumerate(match)
|
||||
if i in is
|
||||
Markdown.with_output_format(print, :bold, io, char)
|
||||
else
|
||||
print(io, char)
|
||||
end
|
||||
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 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(current_module()))
|
||||
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", "importall", "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->!ismatch(r"#", x), map(string, names))
|
||||
|
||||
accessible(mod::Module) =
|
||||
[filter!(s->Base.isdeprecated(mod, s), names(mod, true, true));
|
||||
map(names, moduleusings(mod))...;
|
||||
builtins] |> unique |> filtervalid
|
||||
|
||||
completions(name) = fuzzysort(name, accessible(current_module()))
|
||||
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(search(haystack, needle))
|
||||
docsearch(haystack::Symbol, needle) = docsearch(string(haystack), needle)
|
||||
docsearch(::Void, needle) = false
|
||||
function docsearch(haystack::Array, needle)
|
||||
for elt in haystack
|
||||
docsearch(elt, needle) && return true
|
||||
end
|
||||
false
|
||||
end
|
||||
function docsearch(haystack, needle)
|
||||
Base.warn_once("unable to search documentation of type $(typeof(haystack))")
|
||||
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(x::ANY) = string(x) # for random objects interpolated into the docstring
|
||||
stripmd(x::AbstractString) = x # base case
|
||||
stripmd(x::Void) = " "
|
||||
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
|
||||
Reference in New Issue
Block a user