""" 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']) >>> print html
A paragraph before a fenced code block:
Fenced code block
    A paragraph before a fenced code block:
Fenced code block
    
    ~~~~
    # Some python code
    # Arbitrary code
    ~~~~~ # these tildes will not close the block
    .*?)(?<=\n)(?P=fence)[ ]*$',
    re.MULTILINE|re.DOTALL
    )
CODE_WRAP = '%s
'
LANG_TAG = ' class="%s"'
class FencedCodeExtension(Extension):
    def extendMarkdown(self, md, md_globals):
        """ Add FencedBlockPreprocessor to the Markdown instance. """
        md.registerExtension(self)
        md.preprocessors.add('fenced_code_block',
                                 FencedBlockPreprocessor(md),
                                 ">normalize_whitespace")
class FencedBlockPreprocessor(Preprocessor):
    def __init__(self, md):
        super(FencedBlockPreprocessor, self).__init__(md)
        self.checked_for_codehilite = False
        self.codehilite_conf = {}
    def run(self, lines):
        """ Match and store Fenced Code Blocks in the HtmlStash. """
        # Check for code hilite extension
        if not self.checked_for_codehilite:
            for ext in self.markdown.registeredExtensions:
                if isinstance(ext, CodeHiliteExtension):
                    self.codehilite_conf = ext.config
                    break
            self.checked_for_codehilite = True
        text = "\n".join(lines)
        while 1:
            m = FENCED_BLOCK_RE.search(text)
            if m:
                lang = ''
                if m.group('lang'):
                    lang = LANG_TAG % m.group('lang')
                # If config is not empty, then the codehighlite extension
                # is enabled, so we call it to highlite the code
                if self.codehilite_conf:
                    highliter = CodeHilite(m.group('code'),
                            linenums=self.codehilite_conf['linenums'][0],
                            guess_lang=self.codehilite_conf['guess_lang'][0],
                            css_class=self.codehilite_conf['css_class'][0],
                            style=self.codehilite_conf['pygments_style'][0],
                            lang=(m.group('lang') or None),
                            noclasses=self.codehilite_conf['noclasses'][0])
                    code = highliter.hilite()
                else:
                    code = CODE_WRAP % (lang, self._escape(m.group('code')))
                placeholder = self.markdown.htmlStash.store(code, safe=True)
                text = '%s\n%s\n%s'% (text[:m.start()], placeholder, text[m.end():])
            else:
                break
        return text.split("\n")
    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(configs=configs)