aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Sterba <dsterba@suse.com>2024-02-13 17:50:11 +0100
committerDavid Sterba <dsterba@suse.com>2024-02-13 18:18:29 +0100
commit2700ab22d4b308088d1d6b8bf59680d10c8260c1 (patch)
treedc733a02dd548e50cf487eba608020429a608f86
parentd80dc96460057529b4880f3cfed85f275d2f88b0 (diff)
downloadbtrfs-progs-2700ab22d4b308088d1d6b8bf59680d10c8260c1.tar.gz
btrfs-progs: docs: add document and label reference extensions
Sphinx/RST does not(?) have native support for cross references to labels in specific documents (doc, ref but not both at the same time), also allowing duplicate labels. We have web and manual pages and to share the same text there are common files included from each, defining labels. This leads to warnings and clicking the links ends up in the included document (like ch-volume-management-intro.rst) instead of the parent. As a last resort, implement own role that does the doc and ref in one go. A special directive is used to define a label that is expected to be duplicate (but otherwise it's an ordinary label) and this gets rid of the warning too. Signed-off-by: David Sterba <dsterba@suse.com>
-rw-r--r--Documentation/conf.py78
1 files changed, 78 insertions, 0 deletions
diff --git a/Documentation/conf.py b/Documentation/conf.py
index 0bff1bd0..014352ac 100644
--- a/Documentation/conf.py
+++ b/Documentation/conf.py
@@ -15,6 +15,10 @@
# sys.path.insert(0, os.path.abspath('.'))
import pathlib
+from docutils import nodes
+from docutils.utils import unescape
+from docutils.parsers.rst import Directive, directives
+from sphinx.util.nodes import split_explicit_title, set_source_info
# -- Project information -----------------------------------------------------
project = 'BTRFS'
@@ -73,3 +77,77 @@ man_pages = [
]
extensions = [ 'sphinx_rtd_theme' ]
+
+# Cross reference with document and label
+# Syntax: :docref`Title <rawdocname:label>`
+# Backends: html, man, others
+# - title is mandatory, for manual page backend will append "in rawdocname" if it's in another document
+# - label is not yet validated, can be duplicate in more documents
+# - rawdocname is without extension
+def role_docref(name, rawtext, text, lineno, inliner, options={}, content=[]):
+ env = inliner.document.settings.env
+ text = unescape(text)
+ has_explicit_title, title, target = split_explicit_title(text)
+ if not has_explicit_title:
+ msg = inliner.reporter.error(f"docref requires title: {rawtext}", line=lineno)
+ prb = inliner.problematic(rawtext, rawtext, msg)
+ return [prb], [msg]
+
+ try:
+ docname, label = target.split(':', 1)
+ except ValueError:
+ msg = inliner.reporter.error(f"invalid docref syntax {target}", line=lineno)
+ prb = inliner.problematic(rawtext, rawtext, msg)
+ return [prb], [msg]
+
+ # inliner.reporter.warning(f"DBG: docname={docname} label={label} env.docname={env.docname} title={title}")
+
+ # Validate doc
+ if docname not in env.found_docs:
+ docs = list(env.found_docs)
+ msg = inliner.reporter.error(f"document not found {docname} (%s" % (docs), line=lineno)
+ prb = inliner.problematic(rawtext, rawtext, msg)
+ return [prb], [msg]
+
+ # TODO: validate label
+
+ suffix = ''
+ if env.app.builder.name == 'html':
+ suffix = '.html'
+ elif env.app.builder.name == 'man':
+ suffix = '//'
+
+ titlesuffix = ''
+ if docname != env.docname:
+ titlesuffix = f" (in {docname})"
+
+ try:
+ ref_node = nodes.reference(rawtext, title + titlesuffix,
+ refuri=f"{docname}{suffix}#{label}", **options)
+ except ValueError:
+ msg = inliner.reporter.error('invalid cross reference %r' % text, line=lineno)
+ prb = inliner.problematic(rawtext, rawtext, msg)
+ return [prb], [msg]
+ return [ref_node], []
+
+# Directive to define a label that can appear multiple time (e.g. from an included
+# document), no warnings
+# Must be used in connection with :docref: to link to the containing rather than
+# included document
+# Syntax: .. duplabel:: label-name
+# Backends: all
+class DupLabelDirective(Directive):
+ required_arguments = 1
+
+ def run(self):
+ label = self.arguments[0]
+ target_node = nodes.target('', '', ids=[label])
+ env = self.state.document.settings.env
+ line_number = self.state.document.current_line
+ env.domaindata['std']['labels'][label] = (env.docname, label, line_number)
+ set_source_info(self, target_node)
+ return [target_node]
+
+def setup(app):
+ app.add_role('docref', role_docref)
+ app.add_directive('duplabel', DupLabelDirective)