forked from sphinx-doc/sphinx
-
Notifications
You must be signed in to change notification settings - Fork 1
/
math.py
153 lines (127 loc) · 5.52 KB
/
math.py
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# -*- coding: utf-8 -*-
"""
sphinx.domains.math
~~~~~~~~~~~~~~~~~~~
The math domain.
:copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from docutils import nodes
from docutils.nodes import make_id
from sphinx.addnodes import math_block as displaymath
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 False:
# For type annotation
from typing import Any, Callable, Dict, Iterable, List, Tuple, Union # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.util.typing import RoleFunction # NOQA
logger = logging.getLogger(__name__)
class MathReferenceRole(XRefRole):
def result_nodes(self, document, env, node, is_ref):
# type: (nodes.Node, BuildEnvironment, nodes.Node, bool) -> Tuple[List[nodes.Node], List[nodes.Node]] # NOQA
node['refdomain'] = 'math'
return [node], []
class MathDomain(Domain):
"""Mathematics domain."""
name = 'math'
label = 'mathematics'
initial_data = {
'objects': {}, # labelid -> (docname, eqno)
'has_equations': {}, # docname -> bool
} # type: Dict[unicode, Dict[unicode, Tuple[unicode, int]]]
dangling_warnings = {
'eq': 'equation not found: %(target)s',
}
enumerable_nodes = { # node_class -> (figtype, title_getter)
displaymath: ('displaymath', None),
nodes.math_block: ('displaymath', None),
} # type: Dict[nodes.Node, Tuple[unicode, Callable]]
roles = {
'numref': MathReferenceRole(),
}
def process_doc(self, env, docname, document):
# type: (BuildEnvironment, unicode, nodes.Node) -> None
def math_node(node):
return isinstance(node, (nodes.math, nodes.math_block))
self.data['has_equations'][docname] = any(document.traverse(math_node))
def clear_doc(self, docname):
# type: (unicode) -> None
for equation_id, (doc, eqno) in list(self.data['objects'].items()):
if doc == docname:
del self.data['objects'][equation_id]
self.data['has_equations'].pop(docname, None)
def merge_domaindata(self, docnames, otherdata):
# type: (Iterable[unicode], Dict) -> None
for labelid, (doc, eqno) in otherdata['objects'].items():
if doc in docnames:
self.data['objects'][labelid] = (doc, eqno)
for docname in docnames:
self.data['has_equations'][docname] = otherdata['has_equations'][docname]
def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
# type: (BuildEnvironment, unicode, Builder, unicode, unicode, nodes.Node, nodes.Node) -> nodes.Node # NOQA
assert typ in ('eq', 'numref')
docname, number = self.data['objects'].get(target, (None, None))
if docname:
# 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:
number = env.toc_fignumbers[docname]['displaymath'].get(node_id, ())
number = '.'.join(map(str, number))
else:
number = ''
try:
eqref_format = env.config.math_eqref_format or "({number})"
title = nodes.Text(eqref_format.format(number=number))
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, fromdocname, builder, target, node, contnode):
# type: (BuildEnvironment, unicode, Builder, unicode, nodes.Node, nodes.Node) -> List[nodes.Node] # NOQA
refnode = self.resolve_xref(env, fromdocname, builder, 'eq', target, node, contnode)
if refnode is None:
return []
else:
return [refnode]
def get_objects(self):
# type: () -> List
return []
def add_equation(self, env, docname, labelid):
# type: (BuildEnvironment, unicode, unicode) -> int
equations = self.data['objects']
if labelid in equations:
path = env.doc2path(equations[labelid][0])
msg = __('duplicate label of equation %s, other instance in %s') % (labelid, path)
raise UserWarning(msg)
else:
eqno = self.get_next_equation_number(docname)
equations[labelid] = (docname, eqno)
return eqno
def get_next_equation_number(self, docname):
# type: (unicode) -> int
targets = [eq for eq in self.data['objects'].values() if eq[0] == docname]
return len(targets) + 1
def has_equations(self):
# type: () -> bool
return any(self.data['has_equations'].values())
def setup(app):
# type: (Sphinx) -> Dict[unicode, 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,
}