diff options
author | Takeshi KOMIYA <i.tkomiya@gmail.com> | 2016-08-16 23:36:31 +0900 |
---|---|---|
committer | Takeshi KOMIYA <i.tkomiya@gmail.com> | 2016-08-22 19:58:46 +0900 |
commit | bd22d09f48009a8fa1a2fb6c3a485f1d0103fcdf (patch) | |
tree | 0ed8f9b5d98688a72f9898713178c9759c6665f0 /sphinx/ext/mathbase.py | |
parent | e0d2783c7baee052db7016c2998ef75694e8f5ff (diff) | |
download | sphinx-git-bd22d09f48009a8fa1a2fb6c3a485f1d0103fcdf.tar.gz |
Add domain support to support equation cross references (ref: #1210, #2851)
Diffstat (limited to 'sphinx/ext/mathbase.py')
-rw-r--r-- | sphinx/ext/mathbase.py | 185 |
1 files changed, 117 insertions, 68 deletions
diff --git a/sphinx/ext/mathbase.py b/sphinx/ext/mathbase.py index ddcec492c..20cc3d5c4 100644 --- a/sphinx/ext/mathbase.py +++ b/sphinx/ext/mathbase.py @@ -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) |