mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
This commit is contained in:
parent
e0d2783c7b
commit
bd22d09f48
2
CHANGES
2
CHANGES
@ -101,6 +101,8 @@ Features added
|
||||
* apidoc now avoids invalidating cached files by not writing to files whose
|
||||
content doesn't change. This can lead to significant performance wins if
|
||||
apidoc is run frequently.
|
||||
* #2851: `sphinx.ext.math` emits missing-reference event if equation not found
|
||||
* #1210: ``eqref`` role now supports cross reference
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
|
@ -12,7 +12,10 @@
|
||||
from docutils import nodes, utils
|
||||
from docutils.parsers.rst import directives
|
||||
|
||||
from sphinx.util.nodes import set_source_info
|
||||
from sphinx.roles import XRefRole
|
||||
from sphinx.locale import _
|
||||
from sphinx.domains import Domain
|
||||
from sphinx.util.nodes import make_refnode, set_source_info
|
||||
from sphinx.util.compat import Directive
|
||||
|
||||
|
||||
@ -28,6 +31,76 @@ class eqref(nodes.Inline, nodes.TextElement):
|
||||
pass
|
||||
|
||||
|
||||
class EqXRefRole(XRefRole):
|
||||
def result_nodes(self, document, env, node, is_ref):
|
||||
node['refdomain'] = 'math'
|
||||
return [node], []
|
||||
|
||||
|
||||
class MathDomain(Domain):
|
||||
"""Mathematics domain."""
|
||||
name = 'math'
|
||||
label = 'mathematics'
|
||||
|
||||
initial_data = {
|
||||
'objects': {}, # labelid -> (docname, eqno)
|
||||
}
|
||||
dangling_warnings = {
|
||||
'eq': 'equation not found: %(target)s',
|
||||
}
|
||||
|
||||
def clear_doc(self, docname):
|
||||
for labelid, (doc, eqno) in list(self.data['objects'].items()):
|
||||
if doc == docname:
|
||||
del self.data['objects'][labelid]
|
||||
|
||||
def merge_domaindata(self, docnames, otherdata):
|
||||
for labelid, (doc, eqno) in otherdata['objects'].items():
|
||||
if doc in docnames:
|
||||
self.data['objects'][labelid] = doc
|
||||
|
||||
def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
|
||||
assert typ == 'eq'
|
||||
docname, number = self.data['objects'].get(target, (None, None))
|
||||
if docname:
|
||||
if builder.name == 'latex':
|
||||
newnode = eqref('', **node.attributes)
|
||||
newnode['docname'] = docname
|
||||
newnode['target'] = target
|
||||
return newnode
|
||||
else:
|
||||
title = nodes.Text("(%d)" % number)
|
||||
return make_refnode(builder, fromdocname, docname,
|
||||
"equation-" + target, title)
|
||||
else:
|
||||
return None
|
||||
|
||||
def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode):
|
||||
refnode = self.resolve_xref(env, fromdocname, builder, 'eq', target, node, contnode)
|
||||
if refnode is None:
|
||||
return []
|
||||
else:
|
||||
return [refnode]
|
||||
|
||||
def get_objects(self):
|
||||
return []
|
||||
|
||||
def add_equation(self, env, docname, labelid):
|
||||
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):
|
||||
targets = [eq for eq in self.data['objects'].values() if eq[0] == docname]
|
||||
return len(targets) + 1
|
||||
|
||||
|
||||
def wrap_displaymath(math, label, numbering):
|
||||
def is_equation(part):
|
||||
return part.strip()
|
||||
@ -68,13 +141,6 @@ def math_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
return [math(latex=latex)], []
|
||||
|
||||
|
||||
def eq_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
text = utils.unescape(text)
|
||||
node = eqref('(?)', '(?)', target=text)
|
||||
node['docname'] = inliner.document.settings.env.docname
|
||||
return [node], []
|
||||
|
||||
|
||||
def is_in_section_title(node):
|
||||
"""Determine whether the node is in a section title"""
|
||||
from sphinx.util.nodes import traverse_parent
|
||||
@ -104,21 +170,47 @@ class MathDirective(Directive):
|
||||
latex = self.arguments[0] + '\n\n' + latex
|
||||
node = displaymath()
|
||||
node['latex'] = latex
|
||||
node['label'] = self.options.get('name', None)
|
||||
if node['label'] is None:
|
||||
node['label'] = self.options.get('label', None)
|
||||
node['number'] = None
|
||||
node['label'] = None
|
||||
if 'name' in self.options:
|
||||
node['label'] = self.options['name']
|
||||
if 'label' in self.options:
|
||||
node['label'] = self.options['label']
|
||||
node['nowrap'] = 'nowrap' in self.options
|
||||
node['docname'] = self.state.document.settings.env.docname
|
||||
ret = [node]
|
||||
set_source_info(self, node)
|
||||
if hasattr(self, 'src'):
|
||||
node.source = self.src
|
||||
if node['label']:
|
||||
tnode = nodes.target('', '', ids=['equation-' + node['label']])
|
||||
self.state.document.note_explicit_target(tnode)
|
||||
ret.insert(0, tnode)
|
||||
self.add_target(ret)
|
||||
return ret
|
||||
|
||||
def add_target(self, ret):
|
||||
node = ret[0]
|
||||
env = self.state.document.settings.env
|
||||
|
||||
# assign label automatically if math_number_all enabled
|
||||
if node['label'] == '' or (env.config.math_number_all and not node['label']):
|
||||
seq = env.new_serialno('sphinx.ext.math#equations')
|
||||
node['label'] = "%s:%d" % (env.docname, seq)
|
||||
|
||||
# no targets and numbers are needed
|
||||
if not node['label']:
|
||||
return
|
||||
|
||||
# register label to domain
|
||||
domain = env.get_domain('math')
|
||||
try:
|
||||
eqno = domain.add_equation(env, env.docname, node['label'])
|
||||
node['number'] = eqno
|
||||
|
||||
# add target node
|
||||
target = nodes.target('', '', ids=['equation-' + node['label']])
|
||||
self.state.document.note_explicit_target(target)
|
||||
ret.insert(0, target)
|
||||
except UserWarning as exc:
|
||||
self.state_machine.reporter.warning(exc[0], line=self.lineno)
|
||||
|
||||
|
||||
def latex_visit_math(self, node):
|
||||
if is_in_section_title(node):
|
||||
@ -134,14 +226,18 @@ def latex_visit_displaymath(self, node):
|
||||
if node['nowrap']:
|
||||
self.body.append(node['latex'])
|
||||
else:
|
||||
label = node['label'] and node['docname'] + '-' + node['label'] or None
|
||||
if not node['label']:
|
||||
label = None
|
||||
else:
|
||||
label = "equation:%s:%s" % (node['docname'], node['label'])
|
||||
self.body.append(wrap_displaymath(node['latex'], label,
|
||||
self.builder.config.math_number_all))
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def latex_visit_eqref(self, node):
|
||||
self.body.append('\\eqref{%s-%s}' % (node['docname'], node['target']))
|
||||
label = "equation:%s:%s" % (node['docname'], node['target'])
|
||||
self.body.append('\\eqref{%s}' % label)
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
@ -157,11 +253,6 @@ def text_visit_displaymath(self, node):
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def text_visit_eqref(self, node):
|
||||
self.add_text(node['target'])
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def man_visit_math(self, node):
|
||||
self.body.append(node['latex'])
|
||||
raise nodes.SkipNode
|
||||
@ -175,11 +266,6 @@ def man_depart_displaymath(self, node):
|
||||
self.depart_centered(node)
|
||||
|
||||
|
||||
def man_visit_eqref(self, node):
|
||||
self.body.append(node['target'])
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def texinfo_visit_math(self, node):
|
||||
self.body.append('@math{' + self.escape_arg(node['latex']) + '}')
|
||||
raise nodes.SkipNode
|
||||
@ -196,40 +282,9 @@ def texinfo_depart_displaymath(self, node):
|
||||
pass
|
||||
|
||||
|
||||
def texinfo_visit_eqref(self, node):
|
||||
self.add_xref(node['docname'] + ':' + node['target'],
|
||||
node['target'], node)
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def html_visit_eqref(self, node):
|
||||
self.body.append('<a href="#equation-%s">' % node['target'])
|
||||
|
||||
|
||||
def html_depart_eqref(self, node):
|
||||
self.body.append('</a>')
|
||||
|
||||
|
||||
def number_equations(app, doctree, docname):
|
||||
num = 0
|
||||
numbers = {}
|
||||
for node in doctree.traverse(displaymath):
|
||||
if node['label'] is not None or app.config.math_number_all:
|
||||
num += 1
|
||||
node['number'] = num
|
||||
if node['label'] is not None:
|
||||
numbers[node['label']] = num
|
||||
else:
|
||||
node['number'] = None
|
||||
for node in doctree.traverse(eqref):
|
||||
if node['target'] not in numbers:
|
||||
continue
|
||||
num = '(%d)' % numbers[node['target']]
|
||||
node[0] = nodes.Text(num, num)
|
||||
|
||||
|
||||
def setup_math(app, htmlinlinevisitors, htmldisplayvisitors):
|
||||
app.add_config_value('math_number_all', False, 'html')
|
||||
app.add_config_value('math_number_all', False, 'env')
|
||||
app.add_domain(MathDomain)
|
||||
app.add_node(math, override=True,
|
||||
latex=(latex_visit_math, None),
|
||||
text=(text_visit_math, None),
|
||||
@ -242,13 +297,7 @@ def setup_math(app, htmlinlinevisitors, htmldisplayvisitors):
|
||||
man=(man_visit_displaymath, man_depart_displaymath),
|
||||
texinfo=(texinfo_visit_displaymath, texinfo_depart_displaymath),
|
||||
html=htmldisplayvisitors)
|
||||
app.add_node(eqref,
|
||||
latex=(latex_visit_eqref, None),
|
||||
text=(text_visit_eqref, None),
|
||||
man=(man_visit_eqref, None),
|
||||
texinfo=(texinfo_visit_eqref, None),
|
||||
html=(html_visit_eqref, html_depart_eqref))
|
||||
app.add_node(eqref, latex=(latex_visit_eqref, None))
|
||||
app.add_role('math', math_role)
|
||||
app.add_role('eq', eq_role)
|
||||
app.add_role('eq', EqXRefRole(warn_dangling=True))
|
||||
app.add_directive('math', MathDirective)
|
||||
app.connect('doctree-resolved', number_equations)
|
||||
|
@ -1,6 +1,10 @@
|
||||
Test Math
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
|
||||
math
|
||||
|
||||
.. math:: a^2+b^2=c^2
|
||||
|
||||
Inline :math:`E=mc^2`
|
||||
|
@ -25,8 +25,10 @@ def test_jsmath(app, status, warning):
|
||||
assert (u'<span class="eqno">(1)<a class="headerlink" href="#equation-foo" '
|
||||
u'title="Permalink to this equation">\xb6</a></span>'
|
||||
u'<div class="math" id="equation-foo">\ne^{i\\pi} = 1</div>' in content)
|
||||
assert ('<span class="eqno">(2)</span><div class="math">\n'
|
||||
'e^{ix} = \\cos x + i\\sin x</div>' in content)
|
||||
assert (u'<span class="eqno">(2)<a class="headerlink" href="#equation-math:0" '
|
||||
u'title="Permalink to this equation">\xb6</a></span>'
|
||||
u'<div class="math" id="equation-math:0">\n'
|
||||
u'e^{ix} = \\cos x + i\\sin x</div>' in content)
|
||||
assert '<div class="math">\nn \\in \\mathbb N</div>' in content
|
||||
assert '<div class="math">\na + 1 < b</div>' in content
|
||||
|
||||
@ -81,8 +83,8 @@ def test_math_number_all_mathjax(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
content = (app.outdir / 'index.html').text()
|
||||
html = (r'<div class="math">\s*'
|
||||
r'<span class="eqno">\(1\)</span>\\\[a\^2\+b\^2=c\^2\\\]</div>')
|
||||
html = (r'<div class="math" id="equation-index:0">\s*'
|
||||
r'<span class="eqno">\(1\)<a .*>\xb6</a></span>\\\[a\^2\+b\^2=c\^2\\\]</div>')
|
||||
assert re.search(html, content, re.S)
|
||||
|
||||
|
||||
@ -110,3 +112,6 @@ def test_math_number_all_latex(app, status, warning):
|
||||
r'V &= \\frac\{4}\{3} \\pi r\^3\\\\\s*'
|
||||
r'\\end{aligned}\\end{align\*}')
|
||||
assert re.search(macro, content, re.S)
|
||||
|
||||
macro = r'Referencing equation \\eqref{equation:math:foo}.'
|
||||
assert re.search(macro, content, re.S)
|
||||
|
Loading…
Reference in New Issue
Block a user