Skip to content

Commit

Permalink
Add Markdown block lexer/parser for LaTeX blocks
Browse files Browse the repository at this point in the history
The block lexer/parser was splitting equations like this

$$
x
=
2
$$

So the inline lexer/parser was never seeing the whole equation, and
it wasn't getting properly rendered. This fixes such breaking by
adding a block-level lexer/parser to the LaTeX equations written as
either $$...$$ or \\[...\\]

The inline "block math" parsing code was kept as is, since the above
equation could have been part of a paragraph like "$$x = 2$$" to keep
the compatibility with Jupyter Notebook rendering engine (and because
there's a test enforcing that behavior)
  • Loading branch information
danilobellini committed Dec 4, 2017
1 parent 3e203ce commit 1db9666
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 0 deletions.
40 changes: 40 additions & 0 deletions nbconvert/filters/markdown_mistune.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
from nbconvert.filters.strings import add_anchor


class MathBlockGrammar(mistune.BlockGrammar):
block_math = re.compile(r"^\$\$(.*?)\$\$|^\\\\\[(.*?)\\\\\]", re.DOTALL)
latex_environment = re.compile(r"^\\begin\{([a-z]*\*?)\}(.*?)\\end\{\1\}",
re.DOTALL)


class MathInlineGrammar(mistune.InlineGrammar):
inline_math = re.compile(r"^\$(.+?)\$|^\\\\\((.+?)\\\\\)", re.DOTALL)
block_math = re.compile(r"^\$\$(.*?)\$\$|^\\\\\[(.*?)\\\\\]", re.DOTALL)
Expand All @@ -29,6 +35,31 @@ class MathInlineGrammar(mistune.InlineGrammar):
text = re.compile(r'^[\s\S]+?(?=[\\<!\[_*`~$]|https?://| {2,}\n|$)')


class MathBlockLexer(mistune.BlockLexer):
default_rules = (['block_math', 'latex_environment']
+ mistune.BlockLexer.default_rules)

def __init__(self, rules=None, **kwargs):
if rules is None:
rules = MathBlockGrammar()
super(MathBlockLexer, self).__init__(rules, **kwargs)

def parse_block_math(self, m):
"""Add token for a $$math$$ block"""
self.tokens.append({
'type': 'block_math',
'text': m.group(1) or m.group(2)
})

def parse_latex_environment(self, m):
"""Add token for a \begin{.} ...LaTeX stuff... \end{.} block"""
self.tokens.append({
'type': 'latex_environment',
'name': m.group(1),
'text': m.group(2)
})


class MathInlineLexer(mistune.InlineLexer):
default_rules = (['block_math', 'inline_math', 'latex_environment']
+ mistune.InlineLexer.default_rules)
Expand All @@ -53,8 +84,17 @@ 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_block_math(self):
return self.renderer.block_math(self.token["text"])

def output_latex_environment(self):
return self.renderer.latex_environment(self.token["name"],
self.token["text"])


class IPythonRenderer(mistune.Renderer):
def block_code(self, code, lang):
Expand Down
12 changes: 12 additions & 0 deletions nbconvert/filters/tests/test_markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,18 @@ def test_markdown2html_math(self):
"$$a<b&b<lt$$",
"$$a<b&lt;b>a;a-b<0$$",
"$$<k'>$$",
"""$$x
=
2$$""",
r"""$$
b_{l_1,l_2,l_3}^{\phi\phi\omega} = -8\, l_1\times l_2 \,l_1\cdot l_2 \int_0^{\chi_*} d\chi \frac{W(\chi,\chi_*)^2}{\chi^2} \int_0^{\chi}d\chi' \frac{W(\chi',\chi)W(\chi',\chi_*)}{{\chi'}^2} \left[
P_\psi\left(\frac{l_1}{\chi},z(\chi)\right) P_\Psi\left(\frac{l_2}{\chi'},z(\chi')\right)
- (l_1\leftrightarrow l_2)
\right]
$$""",
r"""\begin{equation*}
x = 2 *55* 7
\end{equation*}""",
"""$
\\begin{tabular}{ l c r }
1 & 2 & 3 \\
Expand Down

0 comments on commit 1db9666

Please sign in to comment.