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