|
15 | 15 | # sys.path.insert(0, os.path.abspath('.'))
|
16 | 16 |
|
17 | 17 | import pathlib
|
| 18 | +from docutils import nodes |
| 19 | +from docutils.utils import unescape |
| 20 | +from docutils.parsers.rst import Directive, directives |
| 21 | +from sphinx.util.nodes import split_explicit_title, set_source_info |
18 | 22 |
|
19 | 23 | # -- Project information -----------------------------------------------------
|
20 | 24 | project = 'BTRFS'
|
|
73 | 77 | ]
|
74 | 78 |
|
75 | 79 | extensions = [ 'sphinx_rtd_theme' ]
|
| 80 | + |
| 81 | +# Cross reference with document and label |
| 82 | +# Syntax: :docref`Title <rawdocname:label>` |
| 83 | +# Backends: html, man, others |
| 84 | +# - title is mandatory, for manual page backend will append "in rawdocname" if it's in another document |
| 85 | +# - label is not yet validated, can be duplicate in more documents |
| 86 | +# - rawdocname is without extension |
| 87 | +def role_docref(name, rawtext, text, lineno, inliner, options={}, content=[]): |
| 88 | + env = inliner.document.settings.env |
| 89 | + text = unescape(text) |
| 90 | + has_explicit_title, title, target = split_explicit_title(text) |
| 91 | + if not has_explicit_title: |
| 92 | + msg = inliner.reporter.error(f"docref requires title: {rawtext}", line=lineno) |
| 93 | + prb = inliner.problematic(rawtext, rawtext, msg) |
| 94 | + return [prb], [msg] |
| 95 | + |
| 96 | + try: |
| 97 | + docname, label = target.split(':', 1) |
| 98 | + except ValueError: |
| 99 | + msg = inliner.reporter.error(f"invalid docref syntax {target}", line=lineno) |
| 100 | + prb = inliner.problematic(rawtext, rawtext, msg) |
| 101 | + return [prb], [msg] |
| 102 | + |
| 103 | + # inliner.reporter.warning(f"DBG: docname={docname} label={label} env.docname={env.docname} title={title}") |
| 104 | + |
| 105 | + # Validate doc |
| 106 | + if docname not in env.found_docs: |
| 107 | + docs = list(env.found_docs) |
| 108 | + msg = inliner.reporter.error(f"document not found {docname} (%s" % (docs), line=lineno) |
| 109 | + prb = inliner.problematic(rawtext, rawtext, msg) |
| 110 | + return [prb], [msg] |
| 111 | + |
| 112 | + # TODO: validate label |
| 113 | + |
| 114 | + suffix = '' |
| 115 | + if env.app.builder.name == 'html': |
| 116 | + suffix = '.html' |
| 117 | + elif env.app.builder.name == 'man': |
| 118 | + suffix = '//' |
| 119 | + |
| 120 | + titlesuffix = '' |
| 121 | + if docname != env.docname: |
| 122 | + titlesuffix = f" (in {docname})" |
| 123 | + |
| 124 | + try: |
| 125 | + ref_node = nodes.reference(rawtext, title + titlesuffix, |
| 126 | + refuri=f"{docname}{suffix}#{label}", **options) |
| 127 | + except ValueError: |
| 128 | + msg = inliner.reporter.error('invalid cross reference %r' % text, line=lineno) |
| 129 | + prb = inliner.problematic(rawtext, rawtext, msg) |
| 130 | + return [prb], [msg] |
| 131 | + return [ref_node], [] |
| 132 | + |
| 133 | +# Directive to define a label that can appear multiple time (e.g. from an included |
| 134 | +# document), no warnings |
| 135 | +# Must be used in connection with :docref: to link to the containing rather than |
| 136 | +# included document |
| 137 | +# Syntax: .. duplabel:: label-name |
| 138 | +# Backends: all |
| 139 | +class DupLabelDirective(Directive): |
| 140 | + required_arguments = 1 |
| 141 | + |
| 142 | + def run(self): |
| 143 | + label = self.arguments[0] |
| 144 | + target_node = nodes.target('', '', ids=[label]) |
| 145 | + env = self.state.document.settings.env |
| 146 | + line_number = self.state.document.current_line |
| 147 | + env.domaindata['std']['labels'][label] = (env.docname, label, line_number) |
| 148 | + set_source_info(self, target_node) |
| 149 | + return [target_node] |
| 150 | + |
| 151 | +def setup(app): |
| 152 | + app.add_role('docref', role_docref) |
| 153 | + app.add_directive('duplabel', DupLabelDirective) |
0 commit comments