1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
|
# SPDX-License-Identifier: MIT
#
# Copyright (c) 2020 Philippe Proulx <pproulx@efficios.com>
#
# This file is a Sphinx extension which adds the following roles:
#
# `bt2man`:
# A typical manual page reference, like `grep(1)`.
#
# Example:
#
# :bt2man:`grep(1)`
#
# This role creates a simple inline literal node with the role's
# text if it's not a Babeltrace 2 manual page reference, or an
# external link to the corresponding online manual page (on
# `babeltrace.org`) with the appropriate project's version
# (`version` configuration entry) otherwise.
#
# `bt2link`:
# An external link with an URL in which a specific placeholder is
# replaced with the project's version.
#
# The role's text follows the typical external link format, for
# example:
#
# Link text <https://example.com/>
#
# Any `@ver@` in the URL is replaced with the project's version
# (`version` configuration entry).
#
# Example:
#
# :bt2link:`libbabeltrace2 <https://babeltrace.org/docs/v@ver@/libbabeltrace2/>`
import re
import functools
import docutils
import docutils.nodes
import docutils.utils
def _bt2man_role(
bt2_version, name, rawtext, text, lineno, inliner, options=None, content=None
):
# match a manual page reference
m = re.match(r"^([a-zA-Z0-9_.:-]+)\(([a-zA-Z0-9]+)\)$", text)
if not m:
msg = "Cannot parse manual page reference `{}`".format(text)
inliner.reporter.severe(msg, line=lineno)
return [inliner.problematic(rawtext, rawtext, msg)], [msg]
# matched manual page and volume
page = m.group(1)
vol = m.group(2)
# create nodes: `ret_node` is the node to return
page_node = docutils.nodes.strong(rawtext, page)
vol_node = docutils.nodes.inline(rawtext, "({})".format(vol))
man_node = docutils.nodes.inline(rawtext, "", page_node, vol_node)
ret_node = docutils.nodes.literal(rawtext, "", man_node)
if page.startswith("babeltrace2"):
# Babeltrace 2 manual page: wrap `ret_node` with an external
# link node
url_tmpl = "https://babeltrace.org/docs/v{ver}/man{vol}/{page}.{vol}/"
url = url_tmpl.format(ver=bt2_version, vol=vol, page=page)
ret_node = docutils.nodes.reference(
rawtext, "", ret_node, internal=False, refuri=url
)
return [ret_node], []
def _bt2link_role(
bt2_version, name, rawtext, text, lineno, inliner, options=None, content=None
):
# match link text and URL
m = re.match(r"^([^<]+) <([^>]+)>$", text)
if not m:
msg = "Cannot parse link template `{}`".format(text)
inliner.reporter.severe(msg, line=lineno)
return [inliner.problematic(rawtext, rawtext, msg)], [msg]
link_text = m.group(1)
# replace `@ver@` with the project's version
url = m.group(2).replace("@ver@", bt2_version)
# create and return an external link node
node = docutils.nodes.reference(rawtext, link_text, internal=False, refuri=url)
return [node], []
def _add_roles(app):
# add the extension's roles; the role functions above expect the
# project's version as their first parameter
app.add_role("bt2man", functools.partial(_bt2man_role, app.config.version))
app.add_role("bt2link", functools.partial(_bt2link_role, app.config.version))
def setup(app):
app.connect("builder-inited", _add_roles)
return {
"version": app.config.version,
"parallel_read_safe": True,
}
|