Skip to content

Commit

Permalink
Support prerendering of code blocks.
Browse files Browse the repository at this point in the history
  • Loading branch information
fredrikekre committed Jul 5, 2021
1 parent 87cd85e commit 445cfda
Showing 1 changed file with 84 additions and 7 deletions.
91 changes: 84 additions & 7 deletions src/Writers/HTMLWriter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,10 @@ struct HTML <: Documenter.Writer
highlights :: Vector{String}
mathengine :: Union{MathEngine,Nothing}
footer :: Union{Markdown.MD, Nothing}
prerender :: Bool
node :: Union{Cmd,String,Nothing}
highlightjs :: Union{String,Nothing}

lang :: String
warn_outdated :: Bool

Expand All @@ -403,12 +407,54 @@ struct HTML <: Documenter.Writer
highlights :: Vector{String} = String[],
mathengine :: Union{MathEngine,Nothing} = KaTeX(),
footer :: Union{String, Nothing} = "Powered by [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl) and the [Julia Programming Language](https://julialang.org/).",
# Code block prerendering
prerender :: Bool = false,
node :: Union{Cmd,String,Nothing} = nothing,
highlightjs :: Union{String,Nothing} = nothing,

# deprecated keywords
edit_branch :: Union{String, Nothing, Default} = Default(nothing),
lang :: String = "en",
warn_outdated :: Bool = true
warn_outdated :: Bool = true,
)
collapselevel >= 1 || throw(ArgumentError("collapselevel must be >= 1"))
if prerender
node = node === nothing ? Sys.which("node") : node
if node === nothing
@error "HTMLWriter: no node executable given or found on the system. Setting `prerender=false`."
prerender = false
else
if !success(`$node --version`)
@error "HTMLWriter: bad node executable at $node. Setting `prerender=false`."
prerender = false
end
end
end
if prerender && highlightjs === nothing
# Try to download
curl = Sys.which("curl")
if curl === nothing
@error "HTMLWriter: no highlight.js file given and no curl executable found " *
"on the system. Setting `prerender=false`."
prerender = false
else
@debug "HTMLWriter: downloading highlightjs"
r = Utilities.JSDependencies.RequireJS([])
RD.highlightjs!(r, highlights)
libs = sort!(collect(r.libraries); by = first) # puts highlight first
key = join((x.first for x in libs), ',')
highlightjs = get!(HLJSFILES, key) do
path, io = mktemp()
for lib in libs
println(io, "// $(lib.first)")
run(pipeline(`$(curl) -fsSL $(lib.second.url)`; stdout=io))
println(io)
end
close(io)
return path
end
end
end
assets = map(assets) do asset
isa(asset, HTMLAsset) && return asset
isa(asset, AbstractString) && return HTMLAsset(assetclass(asset), asset, true)
Expand All @@ -434,10 +480,14 @@ struct HTML <: Documenter.Writer
end
isa(edit_link, Default) && (edit_link = edit_link[])
new(prettyurls, disable_git, edit_link, canonical, assets, analytics,
collapselevel, sidebar_sitename, highlights, mathengine, footer, lang, warn_outdated)
collapselevel, sidebar_sitename, highlights, mathengine, footer,
prerender, node, highlightjs, lang, warn_outdated)
end
end

# Cache of downloaded highlight.js bundles
const HLJSFILES = Dict{String,String}()

"Provides a namespace for remote dependencies."
module RD
using JSON
Expand Down Expand Up @@ -479,7 +529,7 @@ module RD
"highlight",
"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/$(hljs_version)/highlight.min.js"
))
prepend!(languages, ["julia", "julia-repl"])
languages = ["julia", "julia-repl", languages...]
for language in languages
language = jsescape(language)
push!(r, RemoteLibrary(
Expand Down Expand Up @@ -657,7 +707,9 @@ function render(doc::Documents.Document, settings::HTML=HTML())
RD.jquery, RD.jqueryui, RD.headroom, RD.headroom_jquery,
])
RD.mathengine!(r, settings.mathengine)
RD.highlightjs!(r, settings.highlights)
if !settings.prerender
RD.highlightjs!(r, settings.highlights)
end
for filename in readdir(joinpath(ASSETS, "js"))
path = joinpath(ASSETS, "js", filename)
endswith(filename, ".js") && isfile(path) || continue
Expand Down Expand Up @@ -1358,7 +1410,7 @@ end

function domify(ctx, navnode, node)
fixlinks!(ctx, navnode, node)
mdconvert(node, Markdown.MD(); footnotes=ctx.footnotes)
mdconvert(node, Markdown.MD(); footnotes=ctx.footnotes, settings=ctx.settings)
end

function domify(ctx, navnode, anchor::Anchors.Anchor)
Expand Down Expand Up @@ -1656,14 +1708,39 @@ mdconvert(b::Markdown.BlockQuote, parent; kwargs...) = Tag(:blockquote)(mdconver

mdconvert(b::Markdown.Bold, parent; kwargs...) = Tag(:strong)(mdconvert(b.text, parent; kwargs...))

function mdconvert(c::Markdown.Code, parent::MDBlockContext; kwargs...)
function mdconvert(c::Markdown.Code, parent::MDBlockContext; settings::HTML, kwargs...)
@tags pre code
language = Utilities.codelang(c.language)
class = isempty(language) ? "nohighlight" : "language-$(language)"
pre(code[".$class"](c.code))
if settings.prerender && !(isempty(language) || language == "nohighlight")
r = hljs_prerender(c, settings)
r !== nothing && return r
end
return pre(code[".$class"](c.code))
end
mdconvert(c::Markdown.Code, parent; kwargs...) = Tag(:code)(c.code)

function hljs_prerender(c::Markdown.Code, settings)
@tags pre code
lang = Utilities.codelang(c.language)
hljs = settings.highlightjs
js = """
const hljs = require('$(hljs)');
console.log(hljs.highlight($(repr(c.code)), {'language': "$(lang)"}).value);
"""
out, err = IOBuffer(), IOBuffer()
try
run(pipeline(`$(settings.node) -e "$(js)"`; stdout=out, stderr=err))
str = String(take!(out))
# prepend nohighlight to stop runtime highlighting
# return pre(code[".nohighlight $(lang) .hljs"](Tag(Symbol("#RAW#"))(str)))
return pre(code[".language-$(lang) .hljs"](Tag(Symbol("#RAW#"))(str)))
catch e
@error "HTMLWriter: prerendering failed" exception=e stderr=String(take!(err))
end
return nothing
end

mdconvert(h::Markdown.Header{N}, parent; kwargs...) where {N} = DOM.Tag(Symbol("h$N"))(mdconvert(h.text, h; kwargs...))

mdconvert(::Markdown.HorizontalRule, parent; kwargs...) = Tag(:hr)()
Expand Down

0 comments on commit 445cfda

Please sign in to comment.