#!/usr/bin/env python """ Fenced Code Extension for Python Markdown ========================================= This extension adds Fenced Code Blocks to Python-Markdown. >>> import markdown >>> text = ''' ... A paragraph before a fenced code block: ... ... ~~~ ... Fenced code block ... ~~~ ... ''' >>> html = markdown.markdown(text, extensions=['fenced_code']) >>> html u'
A paragraph before a fenced code block:\\n
\\nFenced code block\\n'
Works with safe_mode also (we check this because we are using the HtmlStash):
>>> markdown.markdown(text, extensions=['fenced_code'], safe_mode='replace')
u'A paragraph before a fenced code block:\\n
\\nFenced code block\\n'
Include tilde's in a code block and wrap with blank lines:
>>> text = '''
... ~~~~~~~~
...
... ~~~~
...
... ~~~~~~~~'''
>>> markdown.markdown(text, extensions=['fenced_code'])
u'\\n~~~~\\n\\n'
Multiple blocks and language tags:
>>> text = '''
... ~~~~
... block one
... ~~~~{.python}
...
... ~~~~
... block two
... ~~~~{.html}'''
>>> markdown.markdown(text, extensions=['fenced_code'])
u'block one\\n\\n\\n<p>block two</p>\\n'
"""
import markdown, re
# Global vars
FENCED_BLOCK_RE = re.compile( \
r'(?P^~{3,})[ ]*\n(?P.*?)(?P=fence)[ ]*(\{\.(?P[a-zA-Z0-9_-]*)\})?[ ]*$',
re.MULTILINE|re.DOTALL
)
CODE_WRAP = '%s'
LANG_TAG = ' class="%s"'
class FencedCodeExtension(markdown.Extension):
def extendMarkdown(self, md, md_globals):
""" Add FencedBlockPreprocessor to the Markdown instance. """
FENCED_BLOCK_PREPROCESSOR = FencedBlockPreprocessor()
FENCED_BLOCK_PREPROCESSOR.md = md
md.textPreprocessors.insert(0, FENCED_BLOCK_PREPROCESSOR)
class FencedBlockPreprocessor(markdown.TextPreprocessor):
def run(self, text):
""" Match and store Fenced Code Blocks in the HtmlStash. """
while 1:
m = FENCED_BLOCK_RE.search(text)
if m:
lang = ''
if m.group('lang'):
lang = LANG_TAG % m.group('lang')
code = CODE_WRAP % (lang, self._escape(m.group('code')))
placeholder = self.md.htmlStash.store(code, safe=True)
text = '%s\n%s\n%s'% (text[:m.start()], placeholder, text[m.end():])
else:
break
return text
def _escape(self, txt):
""" basic html escaping """
txt = txt.replace('&', '&')
txt = txt.replace('<', '<')
txt = txt.replace('>', '>')
txt = txt.replace('"', '"')
return txt
def makeExtension(configs=None):
return FencedCodeExtension()
if __name__ == "__main__":
import doctest
doctest.testmod()