Source code for pyTooling.Sphinx.Roles

# ==================================================================================================================== #
#             _____           _ _               ____        _     _                                                    #
#  _ __  _   |_   _|__   ___ | (_)_ __   __ _  / ___| _ __ | |__ (_)_ __ __  __                                        #
# | '_ \| | | || |/ _ \ / _ \| | | '_ \ / _` | \___ \| '_ \| '_ \| | '_ \\ \/ /                                        #
# | |_) | |_| || | (_) | (_) | | | | | | (_| |_ ___) | |_) | | | | | | | |>  <                                         #
# | .__/ \__, ||_|\___/ \___/|_|_|_| |_|\__, (_)____/| .__/|_| |_|_|_| |_/_/\_\                                        #
# |_|    |___/                          |___/        |_|                                                               #
# ==================================================================================================================== #
# Authors:                                                                                                             #
#   Patrick Lehmann                                                                                                    #
#                                                                                                                      #
# License:                                                                                                             #
# ==================================================================================================================== #
# Copyright 2026-2026 Patrick Lehmann - Bötzingen, Germany                                                             #
#                                                                                                                      #
# Licensed under the Apache License, Version 2.0 (the "License");                                                      #
# you may not use this file except in compliance with the License.                                                     #
# You may obtain a copy of the License at                                                                              #
#                                                                                                                      #
#   http://www.apache.org/licenses/LICENSE-2.0                                                                         #
#                                                                                                                      #
# Unless required by applicable law or agreed to in writing, software                                                  #
# distributed under the License is distributed on an "AS IS" BASIS,                                                    #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.                                             #
# See the License for the specific language governing permissions and                                                  #
# limitations under the License.                                                                                       #
#                                                                                                                      #
# SPDX-License-Identifier: Apache-2.0                                                                                  #
# ==================================================================================================================== #
#
"""
The ReST roles shared by pyTooling's documentation and its sibling projects.

Every project used to declare these in its own :file:`doc/prolog.inc`, as ReST source re-parsed into every document
of every project - and eighteen hand-copied files had already drifted apart. Registering them from an extension
makes one declaration serve all of them, and puts the styling in a stylesheet instead of a ``raw:: html`` block.

.. seealso::

   :mod:`pyTooling.Sphinx`
      |rarr| The extension registering these, and what else it brings.
"""
from typing                      import Any, Optional as Nullable

from docutils                    import nodes
from docutils.parsers.rst.roles  import code_role
from docutils.parsers.rst.states import Inliner
from docutils.utils              import unescape

from pyTooling.Decorators        import export


__all__ = ["STYLE_ROLES", "PYTHON_CODE_ROLE", "BREAK_ROLES"]

#: The style roles, mapping a role's name to the CSS classes it applies.
STYLE_ROLES = {
	"bolditalic": ("bolditalic", ),
	"underline":  ("underline", ),
	"strike":     ("strike", ),
	"xlarge":     ("xlarge", ),
	"red":        ("colorred", ),
	"green":      ("colorgreen", ),
	"blue":       ("colorblue", ),
	"purple":     ("colorpurple", ),
	"deletion":   ("colorred", "strike"),
	"addition":   ("colorgreen", ),
}

#: Name of the role rendering inline Python code.
PYTHON_CODE_ROLE = "pycode"

#: The break roles, mapping a role's name to what each output format writes for it.
#:
#: A :class:`~docutils.nodes.raw` node carries the format it is meant for and a writer ignores every other one, so
#: emitting all of them lets each builder pick its own. That is what makes ``|br|`` work outside HTML.
BREAK_ROLES = {
	"br": {"html": "<br />",  "latex": r"\\"},
	"hr": {"html": "<hr />",  "latex": r"\par\noindent\rule{\textwidth}{0.4pt}\par"},
}


[docs] @export def styleRole( name: str, rawText: str, text: str, lineNumber: int, inliner: Inliner, options: Nullable[dict[str, Any]] = None, content: Nullable[list[str]] = None ) -> tuple[list[nodes.Node], list[nodes.system_message]]: """ Render inline text with the CSS classes registered for the role's name. This is what ``.. role:: red`` with ``:class: colorred`` does in a prolog, without the prolog: the role's name is looked up in :data:`STYLE_ROLES` and the classes it maps to are put on an :class:`~docutils.nodes.inline` node. :param name: Name the role was invoked by, which is what selects the classes. :param rawText: The role's text including its markup. :param text: The role's text with the markup removed. :param lineNumber: Line the role was used on. :param inliner: The inliner that called this role. :param options: Options given to the role; unused, because the classes come from the role's name. :param content: Content given to the role; unused. :returns: Tuple of the produced nodes and the system messages, as a docutils role returns. """ classes = STYLE_ROLES.get(name, (name, )) return [nodes.inline(rawText, unescape(text), classes=list(classes))], []
[docs] @export def pythonCodeRole( name: str, rawText: str, text: str, lineNumber: int, inliner: Inliner, options: Nullable[dict[str, Any]] = None, content: Nullable[list[str]] = None ) -> tuple[list[nodes.Node], list[nodes.system_message]]: r""" Render inline Python code, syntax-highlighted. The docutils ``code`` role does the work; this fixes its language to Python and adds the ``highlight`` class, so a page writes ``:pycode:`isinstance(x, int)``` instead of repeating the options at every use. :param name: Name the role was invoked by; unused, because the language is fixed. :param rawText: The role's text including its markup. :param text: The role's text with the markup removed. :param lineNumber: Line the role was used on. :param inliner: The inliner that called this role. :param options: Options given to the role; replaced by the fixed ones. :param content: Content given to the role, handed to the ``code`` role unchanged. :returns: Tuple of the produced nodes and the system messages, as a docutils role returns. """ options = {"language": "python", "classes": ["highlight"]} return code_role(name, rawText, text, lineNumber, inliner, options, [] if content is None else content)
[docs] @export def breakRole( name: str, rawText: str, text: str, lineNumber: int, inliner: Inliner, options: Nullable[dict[str, Any]] = None, content: Nullable[list[str]] = None ) -> tuple[list[nodes.Node], list[nodes.system_message]]: """ Emit a line or a horizontal break in every output format that has one. A :class:`~docutils.nodes.raw` node names the format it is for, and a writer skips the ones that aren't its own - so one node per format is emitted and each builder takes what it understands. Written as a ``raw:: html`` block, which is what these were before, a break reached HTML and nothing else. :param name: Name the role was invoked by, which selects what is written. :param rawText: The role's text including its markup. :param text: The role's text with the markup removed; unused, a break has no content. :param lineNumber: Line the role was used on. :param inliner: The inliner that called this role. :param options: Options given to the role; unused. :param content: Content given to the role; unused. :returns: Tuple of the produced nodes and the system messages, as a docutils role returns. """ return [nodes.raw("", written, format=outputFormat) for outputFormat, written in BREAK_ROLES[name].items()], []