diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 17:25:40 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 17:25:40 +0000 |
commit | cf7da1843c45a4c2df7a749f7886a2d2ba0ee92a (patch) | |
tree | 18dcde1a8d1f5570a77cd0c361de3b490d02c789 /sphinx/domains/math.py | |
parent | Initial commit. (diff) | |
download | sphinx-upstream/7.2.6.tar.xz sphinx-upstream/7.2.6.zip |
Adding upstream version 7.2.6.upstream/7.2.6
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sphinx/domains/math.py')
-rw-r--r-- | sphinx/domains/math.py | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/sphinx/domains/math.py b/sphinx/domains/math.py new file mode 100644 index 0000000..d283d3f --- /dev/null +++ b/sphinx/domains/math.py @@ -0,0 +1,152 @@ +"""The math domain.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +from docutils import nodes +from docutils.nodes import Element, Node, make_id, system_message + +from sphinx.domains import Domain +from sphinx.locale import __ +from sphinx.roles import XRefRole +from sphinx.util import logging +from sphinx.util.nodes import make_refnode + +if TYPE_CHECKING: + from collections.abc import Iterable + + from sphinx.addnodes import pending_xref + from sphinx.application import Sphinx + from sphinx.builders import Builder + from sphinx.environment import BuildEnvironment + + +logger = logging.getLogger(__name__) + + +class MathReferenceRole(XRefRole): + def result_nodes(self, document: nodes.document, env: BuildEnvironment, node: Element, + is_ref: bool) -> tuple[list[Node], list[system_message]]: + node['refdomain'] = 'math' + return [node], [] + + +class MathDomain(Domain): + """Mathematics domain.""" + name = 'math' + label = 'mathematics' + + initial_data: dict[str, Any] = { + 'objects': {}, # labelid -> (docname, eqno) + 'has_equations': {}, # docname -> bool + } + dangling_warnings = { + 'eq': 'equation not found: %(target)s', + } + enumerable_nodes = { # node_class -> (figtype, title_getter) + nodes.math_block: ('displaymath', None), + } + roles = { + 'numref': MathReferenceRole(), + } + + @property + def equations(self) -> dict[str, tuple[str, int]]: + return self.data.setdefault('objects', {}) # labelid -> (docname, eqno) + + def note_equation(self, docname: str, labelid: str, location: Any = None) -> None: + if labelid in self.equations: + other = self.equations[labelid][0] + logger.warning(__('duplicate label of equation %s, other instance in %s') % + (labelid, other), location=location) + + self.equations[labelid] = (docname, self.env.new_serialno('eqno') + 1) + + def get_equation_number_for(self, labelid: str) -> int | None: + if labelid in self.equations: + return self.equations[labelid][1] + else: + return None + + def process_doc(self, env: BuildEnvironment, docname: str, + document: nodes.document) -> None: + def math_node(node: Node) -> bool: + return isinstance(node, (nodes.math, nodes.math_block)) + + self.data['has_equations'][docname] = any(document.findall(math_node)) + + def clear_doc(self, docname: str) -> None: + for equation_id, (doc, _eqno) in list(self.equations.items()): + if doc == docname: + del self.equations[equation_id] + + self.data['has_equations'].pop(docname, None) + + def merge_domaindata(self, docnames: Iterable[str], otherdata: dict[str, Any]) -> None: + for labelid, (doc, eqno) in otherdata['objects'].items(): + if doc in docnames: + self.equations[labelid] = (doc, eqno) + + for docname in docnames: + self.data['has_equations'][docname] = otherdata['has_equations'][docname] + + def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder, + typ: str, target: str, node: pending_xref, contnode: Element, + ) -> Element | None: + assert typ in ('eq', 'numref') + result = self.equations.get(target) + if result: + docname, number = result + # TODO: perhaps use rather a sphinx-core provided prefix here? + node_id = make_id('equation-%s' % target) + if env.config.math_numfig and env.config.numfig: + if docname in env.toc_fignumbers: + numbers = env.toc_fignumbers[docname]['displaymath'].get(node_id, ()) + eqno = '.'.join(map(str, numbers)) + else: + eqno = '' + else: + eqno = str(number) + + try: + eqref_format = env.config.math_eqref_format or "({number})" + title = nodes.Text(eqref_format.format(number=eqno)) + except KeyError as exc: + logger.warning(__('Invalid math_eqref_format: %r'), exc, + location=node) + title = nodes.Text("(%d)" % number) + title = nodes.Text("(%d)" % number) + return make_refnode(builder, fromdocname, docname, node_id, title) + else: + return None + + def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder, + target: str, node: pending_xref, contnode: Element, + ) -> list[tuple[str, Element]]: + refnode = self.resolve_xref(env, fromdocname, builder, 'eq', target, node, contnode) + if refnode is None: + return [] + else: + return [('eq', refnode)] + + def get_objects(self) -> Iterable[tuple[str, str, str, str, str, int]]: + return [] + + def has_equations(self, docname: str | None = None) -> bool: + if docname: + return self.data['has_equations'].get(docname, False) + else: + return any(self.data['has_equations'].values()) + + +def setup(app: Sphinx) -> dict[str, Any]: + app.add_domain(MathDomain) + app.add_role('eq', MathReferenceRole(warn_dangling=True)) + + return { + 'version': 'builtin', + 'env_version': 2, + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } |