Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
152 lines (114 sloc) 5.15 KB
# -*- coding: utf-8 -*-
"""Markdown filters with mistune
Used from markdown.py
"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
from __future__ import print_function
import re
import cgi
import mistune
from pygments import highlight
from pygments.lexers import get_lexer_by_name
from pygments.formatters import HtmlFormatter
from pygments.util import ClassNotFound
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\}",
re.DOTALL)
text = re.compile(r'^[\s\S]+?(?=[\\<!\[_*`~$]|https?://| {2,}\n|$)')
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)
def __init__(self, renderer, rules=None, **kwargs):
if rules is None:
rules = MathInlineGrammar()
super(MathInlineLexer, self).__init__(renderer, rules, **kwargs)
def output_inline_math(self, m):
return self.renderer.inline_math(m.group(1) or m.group(2))
def output_block_math(self, m):
return self.renderer.block_math(m.group(1) or m.group(2) or "")
def output_latex_environment(self, m):
return self.renderer.latex_environment(m.group(1),
m.group(2))
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):
if lang:
try:
lexer = get_lexer_by_name(lang, stripall=True)
except ClassNotFound:
code = lang + '\n' + code
lang = None
if not lang:
return '\n<pre><code>%s</code></pre>\n' % \
mistune.escape(code)
formatter = HtmlFormatter()
return highlight(code, lexer, formatter)
def header(self, text, level, raw=None):
html = super(IPythonRenderer, self).header(text, level, raw=raw)
anchor_link_text = self.options.get('anchor_link_text', u'')
return add_anchor(html, anchor_link_text=anchor_link_text)
# We must be careful here for compatibility
# html.escape() is not availale on python 2.7
# For more details, see:
# https://wiki.python.org/moin/EscapingHtml
def escape_html(self, text):
return cgi.escape(text)
def block_math(self, text):
return '$$%s$$' % self.escape_html(text)
def latex_environment(self, name, text):
name = self.escape_html(name)
text = self.escape_html(text)
return r'\begin{%s}%s\end{%s}' % (name, text, name)
def inline_math(self, text):
return '$%s$' % self.escape_html(text)
def markdown2html_mistune(source):
"""Convert a markdown string to HTML using mistune"""
return MarkdownWithMath(renderer=IPythonRenderer(
escape=False)).render(source)