OLD | NEW |
| (Empty) |
1 """ | |
2 Fenced Code Extension for Python Markdown | |
3 ========================================= | |
4 | |
5 This extension adds Fenced Code Blocks to Python-Markdown. | |
6 | |
7 See <https://pythonhosted.org/Markdown/extensions/fenced_code_blocks.html> | |
8 for documentation. | |
9 | |
10 Original code Copyright 2007-2008 [Waylan Limberg](http://achinghead.com/). | |
11 | |
12 | |
13 All changes Copyright 2008-2014 The Python Markdown Project | |
14 | |
15 License: [BSD](http://www.opensource.org/licenses/bsd-license.php) | |
16 """ | |
17 | |
18 from __future__ import absolute_import | |
19 from __future__ import unicode_literals | |
20 from . import Extension | |
21 from ..preprocessors import Preprocessor | |
22 from .codehilite import CodeHilite, CodeHiliteExtension, parse_hl_lines | |
23 import re | |
24 | |
25 | |
26 class FencedCodeExtension(Extension): | |
27 | |
28 def extendMarkdown(self, md, md_globals): | |
29 """ Add FencedBlockPreprocessor to the Markdown instance. """ | |
30 md.registerExtension(self) | |
31 | |
32 md.preprocessors.add('fenced_code_block', | |
33 FencedBlockPreprocessor(md), | |
34 ">normalize_whitespace") | |
35 | |
36 | |
37 class FencedBlockPreprocessor(Preprocessor): | |
38 FENCED_BLOCK_RE = re.compile(r''' | |
39 (?P<fence>^(?:~{3,}|`{3,}))[ ]* # Opening ``` or ~~~ | |
40 (\{?\.?(?P<lang>[a-zA-Z0-9_+-]*))?[ ]* # Optional {, and lang | |
41 # Optional highlight lines, single- or double-quote-delimited | |
42 (hl_lines=(?P<quot>"|')(?P<hl_lines>.*?)(?P=quot))?[ ]* | |
43 }?[ ]*\n # Optional closing } | |
44 (?P<code>.*?)(?<=\n) | |
45 (?P=fence)[ ]*$''', re.MULTILINE | re.DOTALL | re.VERBOSE) | |
46 CODE_WRAP = '<pre><code%s>%s</code></pre>' | |
47 LANG_TAG = ' class="%s"' | |
48 | |
49 def __init__(self, md): | |
50 super(FencedBlockPreprocessor, self).__init__(md) | |
51 | |
52 self.checked_for_codehilite = False | |
53 self.codehilite_conf = {} | |
54 | |
55 def run(self, lines): | |
56 """ Match and store Fenced Code Blocks in the HtmlStash. """ | |
57 | |
58 # Check for code hilite extension | |
59 if not self.checked_for_codehilite: | |
60 for ext in self.markdown.registeredExtensions: | |
61 if isinstance(ext, CodeHiliteExtension): | |
62 self.codehilite_conf = ext.config | |
63 break | |
64 | |
65 self.checked_for_codehilite = True | |
66 | |
67 text = "\n".join(lines) | |
68 while 1: | |
69 m = self.FENCED_BLOCK_RE.search(text) | |
70 if m: | |
71 lang = '' | |
72 if m.group('lang'): | |
73 lang = self.LANG_TAG % m.group('lang') | |
74 | |
75 # If config is not empty, then the codehighlite extension | |
76 # is enabled, so we call it to highlight the code | |
77 if self.codehilite_conf: | |
78 highliter = CodeHilite( | |
79 m.group('code'), | |
80 linenums=self.codehilite_conf['linenums'][0], | |
81 guess_lang=self.codehilite_conf['guess_lang'][0], | |
82 css_class=self.codehilite_conf['css_class'][0], | |
83 style=self.codehilite_conf['pygments_style'][0], | |
84 lang=(m.group('lang') or None), | |
85 noclasses=self.codehilite_conf['noclasses'][0], | |
86 hl_lines=parse_hl_lines(m.group('hl_lines')) | |
87 ) | |
88 | |
89 code = highliter.hilite() | |
90 else: | |
91 code = self.CODE_WRAP % (lang, | |
92 self._escape(m.group('code'))) | |
93 | |
94 placeholder = self.markdown.htmlStash.store(code, safe=True) | |
95 text = '%s\n%s\n%s' % (text[:m.start()], | |
96 placeholder, | |
97 text[m.end():]) | |
98 else: | |
99 break | |
100 return text.split("\n") | |
101 | |
102 def _escape(self, txt): | |
103 """ basic html escaping """ | |
104 txt = txt.replace('&', '&') | |
105 txt = txt.replace('<', '<') | |
106 txt = txt.replace('>', '>') | |
107 txt = txt.replace('"', '"') | |
108 return txt | |
109 | |
110 | |
111 def makeExtension(*args, **kwargs): | |
112 return FencedCodeExtension(*args, **kwargs) | |
OLD | NEW |