Skip to content

Commit

Permalink
Update Documenter -> v1 (#372)
Browse files Browse the repository at this point in the history
* Update `Documenter -> v1`

* Fix docs block dropdown

* Fix slug for `@customdocs`

* Add preview for documentation PR

* Refactor + Add comments + Trigger Preview
  • Loading branch information
pedromxavier authored Sep 30, 2023
1 parent 52b0afb commit 2ab25aa
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 32 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
30 changes: 30 additions & 0 deletions .github/workflows/docscleanuo.yml
Original file line number Diff line number Diff line change
@@ -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 "[email protected]"
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
2 changes: 1 addition & 1 deletion docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d"

[compat]
Documenter = "0.27"
Documenter = "1"
137 changes: 137 additions & 0 deletions docs/customdocs.jl
Original file line number Diff line number Diff line change
@@ -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
36 changes: 5 additions & 31 deletions docs/make.jl
Original file line number Diff line number Diff line change
@@ -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" => [
Expand All @@ -55,5 +28,6 @@ makedocs(
)

deploydocs(
repo = "github.com/JuliaPy/PythonCall.jl.git",
repo = raw"github.com/JuliaPy/PythonCall.jl.git",
push_preview = true
)

0 comments on commit 2ab25aa

Please sign in to comment.