From 2ab25aac5c196d4ea93c672fd86f6c4350bdd515 Mon Sep 17 00:00:00 2001 From: Pedro Maciel Xavier <31925649+pedromxavier@users.noreply.github.com> Date: Sat, 30 Sep 2023 12:06:40 -0300 Subject: [PATCH] Update `Documenter -> v1` (#372) * Update `Documenter -> v1` * Fix docs block dropdown * Fix slug for `@customdocs` * Add preview for documentation PR * Refactor + Add comments + Trigger Preview --- .github/workflows/docs.yml | 4 + .github/workflows/docscleanuo.yml | 30 +++++++ docs/Project.toml | 2 +- docs/customdocs.jl | 137 ++++++++++++++++++++++++++++++ docs/make.jl | 36 ++------ 5 files changed, 177 insertions(+), 32 deletions(-) create mode 100644 .github/workflows/docscleanuo.yml create mode 100644 docs/customdocs.jl diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 6eb68a52..8d57d8fa 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -6,9 +6,13 @@ on: - main tags: - '*' + pull_request: + types: [opened, synchronize, reopened] + workflow_dispatch: jobs: docs: + if: ${{ !(github.event_name == 'pull_request') || contains(github.event.pull_request.labels.*.name, 'documentation') }} name: Documentation runs-on: ubuntu-latest steps: diff --git a/.github/workflows/docscleanuo.yml b/.github/workflows/docscleanuo.yml new file mode 100644 index 00000000..daf8cca8 --- /dev/null +++ b/.github/workflows/docscleanuo.yml @@ -0,0 +1,30 @@ +name: Documentation Preview Cleanup + +on: + pull_request: + types: [closed] + +jobs: + doc-preview-cleanup: + runs-on: ubuntu-latest + steps: + - name: Checkout gh-pages branch + uses: actions/checkout@v2 + with: + ref: gh-pages + + - name: Delete preview and history + run: | + git config user.name "Documenter.jl" + git config user.email "documenter@juliadocs.github.io" + git rm -rf "previews/PR$PRNUM" + git commit -m "delete preview" + git branch gh-pages-new $(echo "delete history" | git commit-tree HEAD^{tree}) + env: + PRNUM: ${{ github.event.number }} + + - name: Push changes + run: | + git push --force origin gh-pages-new:gh-pages + +# Workflow copied from https://github.com/CliMA/TimeMachine.jl diff --git a/docs/Project.toml b/docs/Project.toml index b550c8d2..c74d338c 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -3,4 +3,4 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d" [compat] -Documenter = "0.27" +Documenter = "1" diff --git a/docs/customdocs.jl b/docs/customdocs.jl new file mode 100644 index 00000000..8b02ea6f --- /dev/null +++ b/docs/customdocs.jl @@ -0,0 +1,137 @@ +module CustomDocs + +import Base: Docs +import Documenter +import Documenter: DocSystem, Markdown, MarkdownAST, doccat +import Documenter.Selectors: order, matcher, runner + +# This is a bit of a hack to let us insert documentation blocks with custom content. +# It means we can document Python things directly in the documentation source, and they +# are searchable. +# +# It's a hack because of the `doccat` overload, requiring a special kind of "signature" +# to embed the information we want. +# +# The first line is of the form "name - category", the rest is Markdown documentation. +# For example: +# ```@customdoc +# foo - Function +# Documentation for `foo`. +# ``` +struct CustomCat{cat} end + +# help?> Documenter.doccat +# Returns the category name of the provided Object. +doccat(::Docs.Binding, ::Type{CustomCat{cat}}) where {cat} = string(cat) + +abstract type CustomDocExpander <: Documenter.Expanders.ExpanderPipeline end + +order(::Type{CustomDocExpander}) = 20.0 + +function matcher(::Type{CustomDocExpander}, node, page, doc) + return Documenter.iscode(node, "@customdoc") +end + +# source: +# https://github.com/JuliaDocs/Documenter.jl/blob/7d3dc2ceef39a62edf2de7081e2d3aaf9be8d7c3/src/expander_pipeline.jl#L353 +function runner(::Type{CustomDocExpander}, node, page, doc) + @assert node.element isa MarkdownAST.CodeBlock + + block = node.element + + name, cat, body = _parse_docs(block.code) + + binding = DocSystem.binding(Main, name) + + docsnodes = MarkdownAST.Node[] + + object = Documenter.Object(binding, CustomCat{cat}) + + docstr = Markdown.parse(body)::Markdown.MD + result = Docs.docstr( + body, + Dict{Symbol,Any}( # NOTE: Not sure about what to put here. + :module => Main, # This is supposed to be tracking python code. + :path => "", + :linenumber => 0 + ) + )::Docs.DocStr + + # NOTE: This was modified because the original Documenter.create_docsnode was generating unreachable links + # Also, the original implementation required docstr, result to be vectors. + + docsnode = _create_docsnode(docstr, result, object, page, doc) + + # Track the order of insertion of objects per-binding. + push!(get!(doc.internal.bindings, binding, Documenter.Object[]), object) + + doc.internal.objects[object] = docsnode.element + + push!(docsnodes, docsnode) + + node.element = Documenter.DocsNodesBlock(block) + + push!(node.children, docsnode) + + return nothing +end + +function _parse_docs(code::AbstractString) + m = match(r"^(.+?)\s*-\s*(.+?)\s*(\n[\s\S]*)$", strip(code)) + + if isnothing(m) + error( + """ + Invalid docstring: + $(code) + """ + ) + end + + name = Symbol(m[1]) + cat = Symbol(m[2]) + body = strip(something(m[3], "")) + + return (name, cat, body) +end + +# source: +# https://github.com/JuliaDocs/Documenter.jl/blob/7d3dc2ceef39a62edf2de7081e2d3aaf9be8d7c3/src/expander_pipeline.jl#L959-L960 +function _create_docsnode(docstring, result, object, page, doc) + # Generate a unique name to be used in anchors and links for the docstring. + # NOTE: The way this is being slugified is causing problems: + # slug = Documenter.slugify(object) + slug = Documenter.slugify(string(object.binding)) + + anchor = Documenter.anchor_add!(doc.internal.docs, object, slug, page.build) + docsnode = Documenter.DocsNode(anchor, object, page) + + # Convert docstring to MarkdownAST, convert Heading elements, and push to DocsNode + + ast = convert(MarkdownAST.Node, docstring) + + doc.user.highlightsig && Documenter.highlightsig!(ast) + + # The following 'for' corresponds to the old dropheaders() function + for headingnode in ast.children + headingnode.element isa MarkdownAST.Heading || continue + + boldnode = MarkdownAST.Node(MarkdownAST.Strong()) + + for textnode in collect(headingnode.children) + push!(boldnode.children, textnode) + end + + headingnode.element = MarkdownAST.Paragraph() + + push!(headingnode.children, boldnode) + end + + push!(docsnode.mdasts, ast) + push!(docsnode.results, result) + push!(docsnode.metas, docstring.meta) + + return MarkdownAST.Node(docsnode) +end + +end # module CustomDocs \ No newline at end of file diff --git a/docs/make.jl b/docs/make.jl index 0fc86ceb..7b47eb88 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,38 +1,11 @@ -using Documenter, PythonCall, Markdown +using Documenter, PythonCall -# This is a bit of a hack to let us insert documentation blocks with custom content. -# It means we can document Python things directly in the documentation source, and they -# are searchable. -# -# It's a hack because of the `doccat` overload, requiring a special kind of "signature" -# to embed the information we want. -# -# The first line is of the form "name - category", the rest is Markdown documentation. -# For example: -# ```@customdoc -# foo - Function -# Documentation for `foo`. -# ``` -struct CustomCat{cat} end -Documenter.Utilities.doccat(::Base.Docs.Binding, ::Type{CustomCat{cat}}) where {cat} = string(cat) -struct CustomDocBlocks <: Documenter.Expanders.ExpanderPipeline end -Documenter.Expanders.Selectors.order(::Type{CustomDocBlocks}) = 20.0 -Documenter.Expanders.Selectors.matcher(::Type{CustomDocBlocks}, node, page, doc) = Documenter.Expanders.iscode(node, "@customdoc") -Documenter.Expanders.Selectors.runner(::Type{CustomDocBlocks}, x, page, doc) = begin - header, rest = split(x.code, "\n", limit=2) - docstr = Markdown.parse(rest) - name, cat = split(header, "-", limit=2) - binding = Docs.Binding(Main, Symbol(strip(name))) - object = Documenter.Utilities.Object(binding, CustomCat{Symbol(strip(cat))}) - slug = Documenter.Utilities.slugify(strip(name)) - anchor = Documenter.Anchors.add!(doc.internal.docs, object, slug, page.build) - node = Documenter.Documents.DocsNode(docstr, anchor, object, page) - page.mapping[x] = node -end +include("customdocs.jl") makedocs( sitename = "PythonCall & JuliaCall", modules = [PythonCall], + warnonly = [:missing_docs], # avoid raising error when docs are missing pages = [ "Home" => "index.md", "The Julia module PythonCall" => [ @@ -55,5 +28,6 @@ makedocs( ) deploydocs( - repo = "github.com/JuliaPy/PythonCall.jl.git", + repo = raw"github.com/JuliaPy/PythonCall.jl.git", + push_preview = true )