-
Notifications
You must be signed in to change notification settings - Fork 562
/
codehilite.py
124 lines (104 loc) · 4.39 KB
/
codehilite.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import logging
import re
from markdown.extensions.codehilite import CodeHilite, CodeHiliteExtension
from markdown.preprocessors import Preprocessor
from markdown.treeprocessors import Treeprocessor
logger = logging.getLogger(__name__)
def highlight(code, config, tab_length, lang=None):
code = CodeHilite(
code,
linenums=config['linenums'],
guess_lang=config['guess_lang'],
css_class=config['css_class'],
style=config['pygments_style'],
noclasses=config['noclasses'],
tab_length=tab_length,
use_pygments=config['use_pygments'],
lang=lang,
)
html = code.hilite()
html = """<div class="codehilite-wrap">{}</div>""".format(html)
return html
class WikiFencedBlockPreprocessor(Preprocessor):
"""
This is a replacement of markdown.extensions.fenced_code which will
directly and without configuration options invoke the vanilla CodeHilite
extension.
"""
FENCED_BLOCK_RE = re.compile(r'''
(?P<fence>^(?:~{3,}|`{3,}))[ ]* # Opening ``` or ~~~
(\{?\.?(?P<lang>[a-zA-Z0-9_+-]*))?[ ]* # Optional {, and lang
# Optional highlight lines, single- or double-quote-delimited
(hl_lines=(?P<quot>"|')(?P<hl_lines>.*?)(?P=quot))?[ ]*
}?[ ]*\n # Optional closing }
(?P<code>.*?)(?<=\n)
(?P=fence)[ ]*$''', re.MULTILINE | re.DOTALL | re.VERBOSE)
CODE_WRAP = '<pre>%s</pre>'
def __init__(self, md):
super().__init__(md)
self.checked_for_codehilite = False
self.codehilite_conf = {}
def run(self, lines):
""" Match and store Fenced Code Blocks in the HtmlStash. """
text = "\n".join(lines)
while 1:
m = self.FENCED_BLOCK_RE.search(text)
if m:
lang = ''
if m.group('lang'):
lang = m.group('lang')
html = highlight(m.group('code'), self.config, self.markdown.tab_length, lang=lang)
placeholder = self.markdown.htmlStash.store(html, safe=True)
text = '%s\n%s\n%s' % (text[:m.start()],
placeholder,
text[m.end():])
else:
break
return text.split("\n")
class HiliteTreeprocessor(Treeprocessor):
""" Hilight source code in code blocks. """
def run(self, root):
""" Find code blocks and store in htmlStash. """
blocks = root.iter('pre')
for block in blocks:
if len(block) == 1 and block[0].tag == 'code':
html = highlight(block[0].text, self.config, self.markdown.tab_length)
placeholder = self.markdown.htmlStash.store(html, safe=True)
# Clear codeblock in etree instance
block.clear()
# Change to p element which will later
# be removed when inserting raw html
block.tag = 'p'
block.text = placeholder
class WikiCodeHiliteExtension(CodeHiliteExtension):
"""
markdown.extensions.codehilite cannot configure container tags but forces
code to be in <table></table>, so we had to overwrite some of the code
because it's hard to extend...
"""
def extendMarkdown(self, md, md_globals):
""" Add HilitePostprocessor to Markdown instance. """
hiliter = HiliteTreeprocessor(md)
hiliter.config = self.getConfigs()
if "hilite" in md.treeprocessors:
logger.warning(
"Replacing existing 'hilite' extension - please remove "
"'codehilite' from WIKI_MARKDOWN_KWARGS"
)
del md.treeprocessors["hilite"]
md.treeprocessors.add("hilite", hiliter, "<inline")
if "fenced_code_block" in md.preprocessors:
logger.warning(
"Replacing existing 'fenced_code_block' extension - please remove "
"'fenced_code_block' or 'extras' from WIKI_MARKDOWN_KWARGS"
)
del md.preprocessors["fenced_code_block"]
hiliter = WikiFencedBlockPreprocessor(md)
hiliter.config = self.getConfigs()
md.preprocessors.add('fenced_code_block',
hiliter,
">normalize_whitespace")
md.registerExtension(self)
def makeExtension(*args, **kwargs):
"""Return an instance of the extension."""
return WikiCodeHiliteExtension(*args, **kwargs)