##// END OF EJS Templates
Rename mistune renderer class
Thomas Kluyver -
Show More
@@ -1,206 +1,206 b''
1 1 """Markdown filters
2 2 This file contains a collection of utility filters for dealing with
3 3 markdown within Jinja templates.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from __future__ import print_function
17 17
18 18 # Stdlib imports
19 19 import os
20 20 import subprocess
21 21 from io import TextIOWrapper, BytesIO
22 22 import re
23 23
24 24 import mistune
25 25 from pygments import highlight
26 26 from pygments.lexers import get_lexer_by_name
27 27 from pygments.formatters import HtmlFormatter
28 28
29 29 # IPython imports
30 30 from IPython.nbconvert.utils.pandoc import pandoc
31 31 from IPython.nbconvert.utils.exceptions import ConversionException
32 32 from IPython.utils.process import get_output_error_code
33 33 from IPython.utils.py3compat import cast_bytes
34 34 from IPython.utils.version import check_version
35 35
36 36 #-----------------------------------------------------------------------------
37 37 # Functions
38 38 #-----------------------------------------------------------------------------
39 39 marked = os.path.join(os.path.dirname(__file__), "marked.js")
40 40 _node = None
41 41
42 42 __all__ = [
43 43 'markdown2html',
44 44 'markdown2html_pandoc',
45 45 'markdown2html_marked',
46 46 'markdown2html_mistune',
47 47 'markdown2latex',
48 48 'markdown2rst',
49 49 ]
50 50
51 51 class NodeJSMissing(ConversionException):
52 52 """Exception raised when node.js is missing."""
53 53 pass
54 54
55 55 def markdown2latex(source):
56 56 """Convert a markdown string to LaTeX via pandoc.
57 57
58 58 This function will raise an error if pandoc is not installed.
59 59 Any error messages generated by pandoc are printed to stderr.
60 60
61 61 Parameters
62 62 ----------
63 63 source : string
64 64 Input string, assumed to be valid markdown.
65 65
66 66 Returns
67 67 -------
68 68 out : string
69 69 Output as returned by pandoc.
70 70 """
71 71 return pandoc(source, 'markdown', 'latex')
72 72
73 73 class MathBlockGrammar(mistune.BlockGrammar):
74 74 block_math = re.compile("^\$\$(.*?)\$\$")
75 75 block_math2 = re.compile(r"^\\begin(.*?)\\end")
76 76
77 77 class MathBlockLexer(mistune.BlockLexer):
78 78 default_features = ['block_math', 'block_math2'] + mistune.BlockLexer.default_features
79 79
80 80 def __init__(self, rules=None, **kwargs):
81 81 if rules is None:
82 82 rules = MathBlockGrammar()
83 83 super(MathBlockLexer, self).__init__(rules, **kwargs)
84 84
85 85 def parse_block_math(self, m):
86 86 """Parse a $$math$$ block"""
87 87 self.tokens.append({
88 88 'type': 'block_math',
89 89 'text': m.group(1)
90 90 })
91 91
92 92 parse_block_math2 = parse_block_math
93 93
94 94 class MathInlineGrammar(mistune.InlineGrammar):
95 95 math = re.compile("^\$(.+?)\$")
96 96
97 97 class MathInlineLexer(mistune.InlineLexer):
98 98 default_features = ['math'] + mistune.InlineLexer.default_features
99 99
100 100 def __init__(self, renderer, rules=None, **kwargs):
101 101 if rules is None:
102 102 rules = MathInlineGrammar()
103 103 super(MathInlineLexer, self).__init__(renderer, rules, **kwargs)
104 104
105 105 def output_math(self, m):
106 106 self.renderer.inline_math(m.group(1))
107 107
108 108 class MarkdownWithMath(mistune.Markdown):
109 109 def __init__(self, renderer, **kwargs):
110 110 if 'inline' not in kwargs:
111 111 kwargs['inline'] = MathInlineLexer(renderer, **kwargs)
112 112 if 'block' not in kwargs:
113 113 kwargs['block'] = MathBlockLexer(**kwargs)
114 114 super(MarkdownWithMath, self).__init__(renderer, **kwargs)
115 115
116 116 def parse_block_math(self):
117 117 return self.renderer.block_math(self.token['text'])
118 118
119 class MyRenderer(mistune.Renderer):
119 class IPythonRenderer(mistune.Renderer):
120 120 def block_code(self, code, lang):
121 121 if not lang:
122 122 return '\n<pre><code>%s</code></pre>\n' % \
123 123 mistune.escape(code)
124 124 lexer = get_lexer_by_name(lang, stripall=True)
125 125 formatter = HtmlFormatter()
126 126 return highlight(code, lexer, formatter)
127 127
128 128 # Pass math through unaltered - mathjax does the rendering in the browser
129 129 def block_math(self, text):
130 130 return '$$%s$$' % text
131 131
132 132 def inline_math(self, text):
133 133 return '$%s$' % text
134 134
135 135 def markdown2html_mistune(source):
136 136 """Convert a markdown string to HTML using mistune"""
137 return MarkdownWithMath(renderer=MyRenderer()).render(source)
137 return MarkdownWithMath(renderer=IPythonRenderer()).render(source)
138 138
139 139 def markdown2html_pandoc(source):
140 140 """Convert a markdown string to HTML via pandoc"""
141 141 return pandoc(source, 'markdown', 'html', extra_args=['--mathjax'])
142 142
143 143 def _find_nodejs():
144 144 global _node
145 145 if _node is None:
146 146 # prefer md2html via marked if node.js >= 0.9.12 is available
147 147 # node is called nodejs on debian, so try that first
148 148 _node = 'nodejs'
149 149 if not _verify_node(_node):
150 150 _node = 'node'
151 151 return _node
152 152
153 153 def markdown2html_marked(source, encoding='utf-8'):
154 154 """Convert a markdown string to HTML via marked"""
155 155 command = [_find_nodejs(), marked]
156 156 try:
157 157 p = subprocess.Popen(command,
158 158 stdin=subprocess.PIPE, stdout=subprocess.PIPE
159 159 )
160 160 except OSError as e:
161 161 raise NodeJSMissing(
162 162 "The command '%s' returned an error: %s.\n" % (" ".join(command), e) +
163 163 "Please check that Node.js is installed."
164 164 )
165 165 out, _ = p.communicate(cast_bytes(source, encoding))
166 166 out = TextIOWrapper(BytesIO(out), encoding, 'replace').read()
167 167 return out.rstrip('\n')
168 168
169 169 # The mistune renderer is the default, because it's simple to depend on it
170 170 markdown2html = markdown2html_marked
171 171
172 172 def markdown2rst(source):
173 173 """Convert a markdown string to ReST via pandoc.
174 174
175 175 This function will raise an error if pandoc is not installed.
176 176 Any error messages generated by pandoc are printed to stderr.
177 177
178 178 Parameters
179 179 ----------
180 180 source : string
181 181 Input string, assumed to be valid markdown.
182 182
183 183 Returns
184 184 -------
185 185 out : string
186 186 Output as returned by pandoc.
187 187 """
188 188 return pandoc(source, 'markdown', 'rst')
189 189
190 190 def _verify_node(cmd):
191 191 """Verify that the node command exists and is at least the minimum supported
192 192 version of node.
193 193
194 194 Parameters
195 195 ----------
196 196 cmd : string
197 197 Node command to verify (i.e 'node')."""
198 198 try:
199 199 out, err, return_code = get_output_error_code([cmd, '--version'])
200 200 except OSError:
201 201 # Command not found
202 202 return False
203 203 if return_code:
204 204 # Command error
205 205 return False
206 206 return check_version(out.lstrip('v'), '0.9.12')
General Comments 0
You need to be logged in to leave comments. Login now