diff --git a/nbconvert/filters/markdown_mistune.py b/nbconvert/filters/markdown_mistune.py index 0b6a9eeb0..280ae86ef 100644 --- a/nbconvert/filters/markdown_mistune.py +++ b/nbconvert/filters/markdown_mistune.py @@ -21,7 +21,42 @@ from nbconvert.filters.strings import add_anchor +class MathBlockGrammar(mistune.BlockGrammar): + """This defines a single regex comprised of the different patterns that + identify math content spanning multiple lines. These are used by the + MathBlockLexer. + """ + multi_math_str = "|".join([r"^\$\$.*?\$\$", + r"^\\\\\[.*?\\\\\]", + r"^\\begin\{([a-z]*\*?)\}(.*?)\\end\{\1\}"]) + multiline_math = re.compile(multi_math_str, re.DOTALL) + + +class MathBlockLexer(mistune.BlockLexer): + """ This acts as a pass-through to the MathInlineLexer. It is needed in + order to avoid other block level rules splitting math sections apart. + """ + + default_rules = (['multiline_math'] + + mistune.BlockLexer.default_rules) + + def __init__(self, rules=None, **kwargs): + if rules is None: + rules = MathBlockGrammar() + super(MathBlockLexer, self).__init__(rules, **kwargs) + + def parse_multiline_math(self, m): + """Add token to pass through mutiline math.""" + self.tokens.append({ + "type": "multiline_math", + "text": m.group(0) + }) + + class MathInlineGrammar(mistune.InlineGrammar): + """This defines different ways of declaring math objects that should be + passed through to mathjax unaffected. These are used by the MathInlineLexer. + """ inline_math = re.compile(r"^\$(.+?)\$|^\\\\\((.+?)\\\\\)", re.DOTALL) block_math = re.compile(r"^\$\$(.*?)\$\$|^\\\\\[(.*?)\\\\\]", re.DOTALL) latex_environment = re.compile(r"^\\begin\{([a-z]*\*?)\}(.*?)\\end\{\1\}", @@ -30,6 +65,14 @@ class MathInlineGrammar(mistune.InlineGrammar): class MathInlineLexer(mistune.InlineLexer): + """This interprets the content of LaTeX style math objects using the rules + defined by the MathInlineGrammar. + + In particular this grabs ``$$...$$``, ``\\[...\\]``, ``\\(...\\)``, ``$...$``, + and ``\begin{foo}...\end{foo}`` styles for declaring mathematics. It strips + delimiters from all these varieties, and extracts the type of environment + in the last case (``foo`` in this example). + """ default_rules = (['block_math', 'inline_math', 'latex_environment'] + mistune.InlineLexer.default_rules) @@ -53,8 +96,14 @@ class MarkdownWithMath(mistune.Markdown): def __init__(self, renderer, **kwargs): if 'inline' not in kwargs: kwargs['inline'] = MathInlineLexer + if 'block' not in kwargs: + kwargs['block'] = MathBlockLexer super(MarkdownWithMath, self).__init__(renderer, **kwargs) + + def output_multiline_math(self): + return self.inline(self.token["text"]) + class IPythonRenderer(mistune.Renderer): def block_code(self, code, lang): diff --git a/nbconvert/filters/tests/test_markdown.py b/nbconvert/filters/tests/test_markdown.py index 943f9b894..d25f524e8 100644 --- a/nbconvert/filters/tests/test_markdown.py +++ b/nbconvert/filters/tests/test_markdown.py @@ -137,6 +137,18 @@ def test_markdown2html_math(self): "$$aa;a-b<0$$", "$$$$", + ("$$x\n" + "=\n" + "2$$"), + ("$$\n" + "b = \\left[\n" + "P\\left(\\right)\n" + "- (l_1\\leftrightarrow l_2\n)" + "\\right]\n" + "$$"), + ("\\begin{equation*}\n" + "x = 2 *55* 7\n" + "\\end{equation*}"), """$ \\begin{tabular}{ l c r } 1 & 2 & 3 \\