1+ module CustomDocs
2+
3+ import Base: Docs
4+ import Documenter
5+ import Documenter: DocSystem, Markdown, MarkdownAST, doccat
6+ import Documenter. Selectors: order, matcher, runner
7+
8+ # This is a bit of a hack to let us insert documentation blocks with custom content.
9+ # It means we can document Python things directly in the documentation source, and they
10+ # are searchable.
11+ #
12+ # It's a hack because of the `doccat` overload, requiring a special kind of "signature"
13+ # to embed the information we want.
14+ #
15+ # The first line is of the form "name - category", the rest is Markdown documentation.
16+ # For example:
17+ # ```@customdoc
18+ # foo - Function
19+ # Documentation for `foo`.
20+ # ```
21+ struct CustomCat{cat} end
22+
23+ # help?> Documenter.doccat
24+ # Returns the category name of the provided Object.
25+ doccat (:: Docs.Binding , :: Type{CustomCat{cat}} ) where {cat} = string (cat)
26+
27+ abstract type CustomDocExpander <: Documenter.Expanders.ExpanderPipeline end
28+
29+ order (:: Type{CustomDocExpander} ) = 20.0
30+
31+ function matcher (:: Type{CustomDocExpander} , node, page, doc)
32+ return Documenter. iscode (node, " @customdoc" )
33+ end
34+
35+ # source:
36+ # https://github.com/JuliaDocs/Documenter.jl/blob/7d3dc2ceef39a62edf2de7081e2d3aaf9be8d7c3/src/expander_pipeline.jl#L353
37+ function runner (:: Type{CustomDocExpander} , node, page, doc)
38+ @assert node. element isa MarkdownAST. CodeBlock
39+
40+ block = node. element
41+
42+ name, cat, body = _parse_docs (block. code)
43+
44+ binding = DocSystem. binding (Main, name)
45+
46+ docsnodes = MarkdownAST. Node[]
47+
48+ object = Documenter. Object (binding, CustomCat{cat})
49+
50+ docstr = Markdown. parse (body):: Markdown.MD
51+ result = Docs. docstr (
52+ body,
53+ Dict {Symbol,Any} ( # NOTE: Not sure about what to put here.
54+ :module => Main, # This is supposed to be tracking python code.
55+ :path => " " ,
56+ :linenumber => 0
57+ )
58+ ):: Docs.DocStr
59+
60+ # NOTE: This was modified because the original Documenter.create_docsnode was generating unreachable links
61+ # Also, the original implementation required docstr, result to be vectors.
62+
63+ docsnode = _create_docsnode (docstr, result, object, page, doc)
64+
65+ # Track the order of insertion of objects per-binding.
66+ push! (get! (doc. internal. bindings, binding, Documenter. Object[]), object)
67+
68+ doc. internal. objects[object] = docsnode. element
69+
70+ push! (docsnodes, docsnode)
71+
72+ node. element = Documenter. DocsNodesBlock (block)
73+
74+ push! (node. children, docsnode)
75+
76+ return nothing
77+ end
78+
79+ function _parse_docs (code:: AbstractString )
80+ m = match (r" ^(.+?)\s *-\s *(.+?)\s *(\n [\s\S ]*)$" , strip (code))
81+
82+ if isnothing (m)
83+ error (
84+ """
85+ Invalid docstring:
86+ $(code)
87+ """
88+ )
89+ end
90+
91+ name = Symbol (m[1 ])
92+ cat = Symbol (m[2 ])
93+ body = strip (something (m[3 ], " " ))
94+
95+ return (name, cat, body)
96+ end
97+
98+ # source:
99+ # https://github.com/JuliaDocs/Documenter.jl/blob/7d3dc2ceef39a62edf2de7081e2d3aaf9be8d7c3/src/expander_pipeline.jl#L959-L960
100+ function _create_docsnode (docstring, result, object, page, doc)
101+ # Generate a unique name to be used in anchors and links for the docstring.
102+ # NOTE: The way this is being slugified is causing problems:
103+ # slug = Documenter.slugify(object)
104+ slug = Documenter. slugify (string (object. binding))
105+
106+ anchor = Documenter. anchor_add! (doc. internal. docs, object, slug, page. build)
107+ docsnode = Documenter. DocsNode (anchor, object, page)
108+
109+ # Convert docstring to MarkdownAST, convert Heading elements, and push to DocsNode
110+
111+ ast = convert (MarkdownAST. Node, docstring)
112+
113+ doc. user. highlightsig && Documenter. highlightsig! (ast)
114+
115+ # The following 'for' corresponds to the old dropheaders() function
116+ for headingnode in ast. children
117+ headingnode. element isa MarkdownAST. Heading || continue
118+
119+ boldnode = MarkdownAST. Node (MarkdownAST. Strong ())
120+
121+ for textnode in collect (headingnode. children)
122+ push! (boldnode. children, textnode)
123+ end
124+
125+ headingnode. element = MarkdownAST. Paragraph ()
126+
127+ push! (headingnode. children, boldnode)
128+ end
129+
130+ push! (docsnode. mdasts, ast)
131+ push! (docsnode. results, result)
132+ push! (docsnode. metas, docstring. meta)
133+
134+ return MarkdownAST. Node (docsnode)
135+ end
136+
137+ end # module CustomDocs
0 commit comments