From 057435b514e1feccfa4db5907be16a21095088a5 Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Wed, 12 Jul 2023 12:51:18 +0200 Subject: [PATCH 1/5] Add basic `@evalraw` functionality --- src/Expanders.jl | 49 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/Expanders.jl b/src/Expanders.jl index b7bedaa329..98eb895eae 100644 --- a/src/Expanders.jl +++ b/src/Expanders.jl @@ -180,6 +180,7 @@ Markdown.parse("![Plot](plot.svg)") """ abstract type EvalBlocks <: NestedExpanderPipeline end +abstract type EvalRawBlocks <: NestedExpanderPipeline end abstract type RawBlocks <: NestedExpanderPipeline end """ @@ -246,7 +247,8 @@ Selectors.order(::Type{ContentsBlocks}) = 7.0 Selectors.order(::Type{ExampleBlocks}) = 8.0 Selectors.order(::Type{REPLBlocks}) = 9.0 Selectors.order(::Type{SetupBlocks}) = 10.0 -Selectors.order(::Type{RawBlocks}) = 11.0 +Selectors.order(::Type{EvalRawBlocks}) = 11.0 +Selectors.order(::Type{RawBlocks}) = 12.0 Selectors.matcher(::Type{TrackHeaders}, node, page, doc) = isa(node.element, MarkdownAST.Heading) Selectors.matcher(::Type{MetaBlocks}, node, page, doc) = iscode(node, "@meta") @@ -258,6 +260,7 @@ Selectors.matcher(::Type{ContentsBlocks}, node, page, doc) = iscode(node, "@cont Selectors.matcher(::Type{ExampleBlocks}, node, page, doc) = iscode(node, r"^@example") Selectors.matcher(::Type{REPLBlocks}, node, page, doc) = iscode(node, r"^@repl") Selectors.matcher(::Type{SetupBlocks}, node, page, doc) = iscode(node, r"^@setup") +Selectors.matcher(::Type{EvalRawBlocks}, node, page, doc) = iscode(node, r"^@evalraw") Selectors.matcher(::Type{RawBlocks}, node, page, doc) = iscode(node, r"^@raw") # Default Expander. @@ -882,6 +885,50 @@ function Selectors.runner(::Type{SetupBlocks}, node, page, doc) node.element = Documenter.SetupNode(x.info, x.code) end +# @evalraw +# ----- + +function Selectors.runner(::Type{EvalRawBlocks}, node, page, doc) + @assert node.element isa MarkdownAST.CodeBlock + x = node.element + + m = match(r"@evalraw[ ](.+)$", x.info) + m === nothing && error("invalid '@evalraw ' syntax: $(x.info)") + + # Bail early if in draft mode + if Documenter.is_draft(doc, page) + @debug "Skipping evaluation of @eval block in draft mode:\n$(x.code)" + create_draft_result!(node; blocktype="@eval") + return + end + sandbox = Module(:EvalBlockSandbox) + lines = Documenter.find_block_in_file(x.code, page.source) + linenumbernode = LineNumberNode(lines === nothing ? 0 : lines.first, + basename(page.source)) + @debug "Evaluating @evalraw block:\n$(x.code)" + cd(page.workdir) do + result = nothing + for (ex, str) in Documenter.parseblock(x.code, doc, page; keywords = false, + linenumbernode = linenumbernode) + try + result = Core.eval(sandbox, ex) + catch err + bt = Documenter.remove_common_backtrace(catch_backtrace()) + @docerror(doc, :eval_block, + """ + failed to evaluate `@evalraw` block in $(Documenter.locrepr(page.source)) + ```$(x.info) + $(x.code) + ``` + """, exception = (err, bt)) + end + end + result isa String || error("Only String output supported") + # TODO: make result a child node + node.element = Documenter.RawNode(Symbol(m[1]), result) + end +end + # @raw # ---- From bec1c88af2a39d4cd4ac805db4bde08b8738fbcd Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Wed, 12 Jul 2023 13:07:08 +0200 Subject: [PATCH 2/5] fix names --- src/Expanders.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Expanders.jl b/src/Expanders.jl index 98eb895eae..645e490255 100644 --- a/src/Expanders.jl +++ b/src/Expanders.jl @@ -897,11 +897,11 @@ function Selectors.runner(::Type{EvalRawBlocks}, node, page, doc) # Bail early if in draft mode if Documenter.is_draft(doc, page) - @debug "Skipping evaluation of @eval block in draft mode:\n$(x.code)" - create_draft_result!(node; blocktype="@eval") + @debug "Skipping evaluation of @evalraw block in draft mode:\n$(x.code)" + create_draft_result!(node; blocktype="@evalraw") return end - sandbox = Module(:EvalBlockSandbox) + sandbox = Module(:EvalRawBlockSandbox) lines = Documenter.find_block_in_file(x.code, page.source) linenumbernode = LineNumberNode(lines === nothing ? 0 : lines.first, basename(page.source)) From 62682c2c672039ecc4defa55b635584aec843d5a Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 14 Jul 2023 13:22:19 +0200 Subject: [PATCH 3/5] add UseShowMethods and HTMLWriter --- src/Expanders.jl | 2 ++ src/documents.jl | 5 +++++ src/html/HTMLWriter.jl | 11 ++++++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Expanders.jl b/src/Expanders.jl index 645e490255..8c03b2a313 100644 --- a/src/Expanders.jl +++ b/src/Expanders.jl @@ -610,6 +610,8 @@ function Selectors.runner(::Type{EvalBlocks}, node, page, doc) nothing elseif isa(result, Markdown.MD) convert(Node, result) + elseif isa(result, Documenter.UseShowMethods) + Node(result) else # TODO: we could handle the cases where the user provides some of the Markdown library # objects, like Paragraph. diff --git a/src/documents.jl b/src/documents.jl index d3fa118154..abad61acc0 100644 --- a/src/documents.jl +++ b/src/documents.jl @@ -902,6 +902,11 @@ end MarkdownAST.iscontainer(::MultiOutput) = true MarkdownAST.can_contain(::MultiOutput, ::Union{MultiOutputElement,MarkdownAST.CodeBlock}) = true +struct UseShowMethods <: AbstractDocumenterBlock + element :: Dict{MIME, Any} + UseShowMethods(x) = new(display_dict(x)) +end + # In the SetupBlocks expander, we map @setup nodes to Markdown.MD() objects struct SetupNode <: AbstractDocumenterBlock name :: String diff --git a/src/html/HTMLWriter.jl b/src/html/HTMLWriter.jl index 8ad994de6d..61afc9efcf 100644 --- a/src/html/HTMLWriter.jl +++ b/src/html/HTMLWriter.jl @@ -1689,7 +1689,15 @@ function domify_doc(dctx::DCtx, node::Node) end function domify(dctx::DCtx, ::Node, evalnode::Documenter.EvalNode) - isnothing(evalnode.result) ? DOM.Node[] : domify(dctx, evalnode.result.children) + result = evalnode.result + ret = if result === Nothing + DOM.Node[] + elseif isempty(result.children) + domify(dctx, result) + else + domify(dctx, result.children) + end + return ret end # nothing to show for MetaNodes, so we just return an empty list @@ -2122,6 +2130,7 @@ end # Select the "best" representation for HTML output. domify(dctx::DCtx, node::Node, ::Documenter.MultiOutput) = domify(dctx, node.children) domify(dctx::DCtx, node::Node, moe::Documenter.MultiOutputElement) = Base.invokelatest(domify, dctx, node, moe.element) +domify(dctx::DCtx, node::Node, usm::Documenter.UseShowMethods) = Base.invokelatest(domify, dctx, node, usm.element) function domify(dctx::DCtx, node::Node, d::Dict{MIME,Any}) rawhtml(code) = Tag(Symbol("#RAW#"))(code) From e00e90507165d4c7f179a5dd9c9c3c4b7a651ebf Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 14 Jul 2023 13:55:13 +0200 Subject: [PATCH 4/5] add methods to LatexWriter.jl --- src/latex/LaTeXWriter.jl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/latex/LaTeXWriter.jl b/src/latex/LaTeXWriter.jl index b79c3fe3e1..99b207fa63 100644 --- a/src/latex/LaTeXWriter.jl +++ b/src/latex/LaTeXWriter.jl @@ -403,7 +403,12 @@ end function latex(io::Context, node::Node, evalnode::Documenter.EvalNode) if evalnode.result !== nothing - latex(io, evalnode.result.children, toplevel = true) + result = evalnode.result + if isempty(result.children) + latex(io, result) + else + latex(io, result.children, toplevel = true) + end end end @@ -413,6 +418,9 @@ latex(io::Context, node::Node, ::Documenter.MultiOutput) = latex(io, node.childr function latex(io::Context, node::Node, moe::Documenter.MultiOutputElement) Base.invokelatest(latex, io, node, moe.element) end +function latex(io::Context, node::Node, usm::Documenter.UseShowMethods) + Base.invokelatest(latex, io, node, usm.element) +end function latex(io::Context, ::Node, d::Dict{MIME,Any}) filename = String(rand('a':'z', 7)) if haskey(d, MIME"image/png"()) From 523adb223664ed47a5ba07e4ebcdac0a3a4b3639 Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 14 Jul 2023 14:02:30 +0200 Subject: [PATCH 5/5] remove hanging first implementation --- src/Expanders.jl | 49 +----------------------------------------------- 1 file changed, 1 insertion(+), 48 deletions(-) diff --git a/src/Expanders.jl b/src/Expanders.jl index 8c03b2a313..4ed851a004 100644 --- a/src/Expanders.jl +++ b/src/Expanders.jl @@ -180,7 +180,6 @@ Markdown.parse("![Plot](plot.svg)") """ abstract type EvalBlocks <: NestedExpanderPipeline end -abstract type EvalRawBlocks <: NestedExpanderPipeline end abstract type RawBlocks <: NestedExpanderPipeline end """ @@ -247,8 +246,7 @@ Selectors.order(::Type{ContentsBlocks}) = 7.0 Selectors.order(::Type{ExampleBlocks}) = 8.0 Selectors.order(::Type{REPLBlocks}) = 9.0 Selectors.order(::Type{SetupBlocks}) = 10.0 -Selectors.order(::Type{EvalRawBlocks}) = 11.0 -Selectors.order(::Type{RawBlocks}) = 12.0 +Selectors.order(::Type{RawBlocks}) = 11.0 Selectors.matcher(::Type{TrackHeaders}, node, page, doc) = isa(node.element, MarkdownAST.Heading) Selectors.matcher(::Type{MetaBlocks}, node, page, doc) = iscode(node, "@meta") @@ -260,7 +258,6 @@ Selectors.matcher(::Type{ContentsBlocks}, node, page, doc) = iscode(node, "@cont Selectors.matcher(::Type{ExampleBlocks}, node, page, doc) = iscode(node, r"^@example") Selectors.matcher(::Type{REPLBlocks}, node, page, doc) = iscode(node, r"^@repl") Selectors.matcher(::Type{SetupBlocks}, node, page, doc) = iscode(node, r"^@setup") -Selectors.matcher(::Type{EvalRawBlocks}, node, page, doc) = iscode(node, r"^@evalraw") Selectors.matcher(::Type{RawBlocks}, node, page, doc) = iscode(node, r"^@raw") # Default Expander. @@ -887,50 +884,6 @@ function Selectors.runner(::Type{SetupBlocks}, node, page, doc) node.element = Documenter.SetupNode(x.info, x.code) end -# @evalraw -# ----- - -function Selectors.runner(::Type{EvalRawBlocks}, node, page, doc) - @assert node.element isa MarkdownAST.CodeBlock - x = node.element - - m = match(r"@evalraw[ ](.+)$", x.info) - m === nothing && error("invalid '@evalraw ' syntax: $(x.info)") - - # Bail early if in draft mode - if Documenter.is_draft(doc, page) - @debug "Skipping evaluation of @evalraw block in draft mode:\n$(x.code)" - create_draft_result!(node; blocktype="@evalraw") - return - end - sandbox = Module(:EvalRawBlockSandbox) - lines = Documenter.find_block_in_file(x.code, page.source) - linenumbernode = LineNumberNode(lines === nothing ? 0 : lines.first, - basename(page.source)) - @debug "Evaluating @evalraw block:\n$(x.code)" - cd(page.workdir) do - result = nothing - for (ex, str) in Documenter.parseblock(x.code, doc, page; keywords = false, - linenumbernode = linenumbernode) - try - result = Core.eval(sandbox, ex) - catch err - bt = Documenter.remove_common_backtrace(catch_backtrace()) - @docerror(doc, :eval_block, - """ - failed to evaluate `@evalraw` block in $(Documenter.locrepr(page.source)) - ```$(x.info) - $(x.code) - ``` - """, exception = (err, bt)) - end - end - result isa String || error("Only String output supported") - # TODO: make result a child node - node.element = Documenter.RawNode(Symbol(m[1]), result) - end -end - # @raw # ----