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>
This commit is contained in:
David Sterba 2024-02-13 17:50:11 +01:00
parent d80dc96460
commit 2700ab22d4
1 changed files with 78 additions and 0 deletions

View File

@ -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)