| from sphinx.directives.patches import MathDirective | |
| from sphinx.util.texescape import tex_replace_map | |
| from sphinx.writers.html5 import HTML5Translator | |
| from sphinx.writers.latex import LaTeXTranslator | |
| from docutils import nodes | |
| from docutils.nodes import math | |
| from docutils.parsers.rst.directives.misc import Replace | |
| from six import text_type | |
| import re | |
| # Transform \xref in math nodes | |
| xref_re = re.compile('\\\\xref\{([^}]*)\}\{([^}]*)\}', re.M) | |
| def html_hyperlink(file, id): | |
| if "/" in file: | |
| return '\\href{../../core/%s.html#%s}' % (file, id.replace('_', '-')) | |
| else: | |
| return '\\href{%s.html#%s}' % (file, id.replace('_', '-')) | |
| def html_transform_math_xref(node): | |
| new_text = xref_re.sub(lambda m: html_hyperlink(m.group(1), m.group(2)), node.astext()) | |
| node.children[0] = nodes.Text(new_text) | |
| # Mirrors sphinx/writers/latex | |
| def latex_hyperlink(file, id): | |
| id = text_type(id).translate(tex_replace_map).\ | |
| encode('ascii', 'backslashreplace').decode('ascii').\ | |
| replace('_', '-').replace('\\', '_') | |
| if "/" in file: | |
| return '\\hyperref[Core-%s:%s]' % (file, id) | |
| else: | |
| return '\\hyperref[%s:%s]' % (file, id) | |
| def latex_transform_math_xref(node): | |
| new_text = xref_re.sub(lambda m: latex_hyperlink(m.group(1), m.group(2)), node.astext()) | |
| node.children[0] = nodes.Text(new_text) | |
| # Expand mathdef names in math roles and directives | |
| def_re = re.compile('\\\\[A-Za-z][0-9A-Za-z]*', re.M) | |
| auxcounter = 0 | |
| def lookup_mathdef(defs, name): | |
| if name in defs: | |
| [arity, s] = defs[name] | |
| if arity > 0: | |
| global auxcounter | |
| auxcounter = auxcounter + 1 | |
| name = "\\mathdef%d" % auxcounter | |
| s = "\\def%s#%d{%s}%s" % (name, arity, s, name) | |
| return s | |
| return name | |
| def replace_mathdefs(doc, s): | |
| if not hasattr(doc, 'mathdefs'): | |
| return s | |
| return def_re.sub(lambda m: lookup_mathdef(doc.mathdefs, m.group(0)), s) | |
| def ext_math_role(role, raw, text, line, inliner, options = {}, content = []): | |
| text = replace_mathdefs(inliner.document, raw.split('`')[1]) | |
| return [math(raw, text)], [] | |
| class ExtMathDirective(MathDirective): | |
| def run(self): | |
| doc = self.state.document | |
| for i, s in enumerate(self.content): | |
| self.content[i] = replace_mathdefs(doc, s) | |
| for i, s in enumerate(self.arguments): | |
| self.arguments[i] = replace_mathdefs(doc, s) | |
| return super().run() | |
| class MathdefDirective(Replace): | |
| def run(self): | |
| name = '\\' + self.state.parent.rawsource.split('|')[1] | |
| name = name.split('#') | |
| if len(name) > 1: | |
| arity = int(name[1]) | |
| else: | |
| arity = 0 | |
| name = name[0] | |
| doc = self.state.document | |
| if not hasattr(doc, 'mathdefs'): | |
| doc.mathdefs = {} | |
| # TODO: we don't ever hit the case where len(self.content) > 1 | |
| for i, s in enumerate(self.content): | |
| self.content[i] = replace_mathdefs(doc, s) | |
| doc.mathdefs[name] = [arity, ''.join(self.content)] | |
| self.content[0] = ':math:`' + self.content[0] | |
| self.content[-1] = self.content[-1] + '`' | |
| return super().run() | |
| class WebAssemblyHTML5Translator(HTML5Translator): | |
| """ | |
| Customize HTML5Translator. | |
| Convert xref in math and math block nodes to hrefs. | |
| """ | |
| def visit_math(self, node, math_env = ''): | |
| html_transform_math_xref(node) | |
| super().visit_math(node, math_env) | |
| def visit_math_block(self, node, math_env = ''): | |
| html_transform_math_xref(node) | |
| super().visit_math_block(node, math_env) | |
| class WebAssemblyLaTeXTranslator(LaTeXTranslator): | |
| """ | |
| Customize LaTeXTranslator. | |
| Convert xref in math and math block nodes to hyperrefs. | |
| """ | |
| def visit_math(self, node): | |
| latex_transform_math_xref(node) | |
| super().visit_math(node) | |
| def visit_math_block(self, node): | |
| latex_transform_math_xref(node) | |
| super().visit_math_block(node) | |
| # Setup | |
| def setup(app): | |
| app.set_translator('html', WebAssemblyHTML5Translator) | |
| app.set_translator('latex', WebAssemblyLaTeXTranslator) | |
| app.add_role('math', ext_math_role) | |
| app.add_directive('math', ExtMathDirective, override = True) | |
| app.add_directive('mathdef', MathdefDirective) |