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