#
import re

from markdown.util import etree, AtomicString
from markdown.inlinepatterns import Pattern
from markdown.preprocessors import Preprocessor
from markdown.extensions import Extension
#

Markdown preprocessor that matches all TODO and FIXME strings occurring at the beginning of the line or after the inline comment delimiter and highlights them in the documentation.

    class Prep(Preprocessor):

        matched_strings = ["TODO", "FIXME", "WARNING", "CAUTION"]
        regex = re.compile("^\s*(" + "|".join(matched_strings) + ":?)(.*)", flags=re.I)
#

Intended markup for TODO strings. The type of the string is used as a class.

        def template(self, match):

            return "<span class={0:s}><strong>{1:s}</strong>{2:s}</span>"\
                .format(match.group(1).lower(), match.group(1), match.group(2))
#

String matching is case insensitive

        def run(self, lines):

            return [self.regex.sub(self.template, line) for line in lines]
#
    def extendMarkdown(self, md, md_globals):
        md.preprocessors.add('todo', Todo.Prep(md), '_end')
class LinesConnector(Extension):

    def __init__(self, regex=r"(\S)\s*\\\s*\n\s*(\S)", sub=r"\1 \2", *args, **kwargs):

        regex = re.compile(regex, flags=re.M)
#
        class Prep(Preprocessor):
#

Method ensures that there is exactly one space between 2 joined strings.

            def run(self, lines):

                return regex.sub(sub, "\n".join(lines)).split("\n")
#
        self.Prep = Prep()

        super(LinesConnector, self).__init__(*args, **kwargs)
#
    def extendMarkdown(self, md, md_globals):
        md.preprocessors.add('lines-connector', self.Prep, '_end')
class SaneDefList(Extension):
#

Markdown preprocessor that prepares natural-style definition lists for native Markdown extension def_list. It allows to write more compact and readable class field definitions.

    class Prep(Preprocessor):
#

Searches for a line starting with a literal followed by a colon and multiple spaces:

some arbitrary parameter name
:   and its definition separated by `:\s\s+`

`parameter name` might contain everything except a [colon](//en.wikipedia.org/wiki/Colon_(punctuation)) and be multiline
:   it still works

And turns it into:

some arbitrary parameter name
and its definition separated by :\s\s+
parameter name might contain everything except a colon and be multiline
it still works
        def run(self, lines):

            new_lines = []
            for line in lines:
                match = re.match(r'^(\s*)([^:]+):\s{2,}(.+)', line)
                if match:
                    new_lines.append(match.group(1) + match.group(2))
                    new_lines.append(match.group(1) + ':   ' + match.group(3))
                    new_lines.append('')
                else:
                    new_lines.append(line)

            return new_lines
#
    def extendMarkdown(self, md, md_globals):
        md.preprocessors.add('sane-def-list', SaneDefList.Prep(md), '_end')
class Pydoc(Extension):
#

Preprocessor used to parse PyDoc-style comments like :param name: and format them.

    class Prep(Preprocessor):
#

param lines Documentation lines return Lines of text with parsed PyDoc comments

        def run(self, lines):

            new_lines = []
            for text in lines:
#

Regex that matches @param name

                text = re.compile(
                    r'^(\s?)@(\w+)\s+(["\'\`].+["\'\`]|\S+)\s*', re.M
                ).sub(
                    r'\1<span class="pydoc pydoc-\2"><span>\2</span> <code>\3</code></span> ',
                    text)
#

Regex that matches @var

                text = re.compile(
                    r'^(\s?)@(\w+)', re.M
                ).sub(
                    r'\1<span class="pydoc pydoc-\2"><span>\2</span></span>',
                    text)
#

Regex that matches :param name:

                text = re.compile(
                    r'^(\s?):([^: ]+) +([^:]+):', re.M
                ).sub(
                    r'\1<span class="pydoc pydoc-\2"><span>\2</span> <code>\3</code></span>',
                    text)
#

Regex that matches single-word comments: :return:

                text = re.compile(
                    r'^(\s?):([^: ]+):', re.M
                ).sub(
                    r'\1<span class="pydoc pydoc-\2"><span>\2</span></span>',
                    text)

                new_lines.append(text)
#
            return new_lines
#
    def extendMarkdown(self, md, md_globals):
        md.preprocessors.add('pydoc', Pydoc.Prep(md), '_end')
#

Autolink extension

There's already an inline pattern called autolink which handles http://www.google.com type links.

class AutoLinkExtension(Extension):

    EXTRA_AUTOLINK_RE = r'(?<!"|>)((https?://|www)[-\w./#?%=&]+)'
#
    class pattern(Pattern):

        def handleMatch(self, m):
            el = etree.Element('a')
            if m.group(2).startswith('http'):
                href = m.group(2)
            else:
                href = 'http://%s' % m.group(2)
            el.set('href', href)
            el.text = m.group(2)
            return el
#
    def extendMarkdown(self, md, md_globals):
        md.inlinePatterns.add(
            'extra_autolink',
            AutoLinkExtension.pattern(self.EXTRA_AUTOLINK_RE, self),
            '>autolink'
        )
#

Math extension for Python-Markdown

Adds support for displaying math formulas using MathJax.

Author: 2015, Dmitry Shachnev mitya57@gmail.com. Slightly customized by cryptonomicon314

class MathExtension(Extension):

    def __init__(self, *args, **kwargs):
        super(MathExtension, self).__init__(*args, **kwargs)
#
    def extendMarkdown(self, md, md_globals):

        def handle_match_inline(m):
            node = etree.Element('script')
            node.set('type', 'math/tex')
            node.text = AtomicString(m.group(3))
            return node
#
        def handle_match(m):
            node = etree.Element('script')
            node.set('type', 'math/tex; mode=display')
            if '\\begin' in m.group(2):
                node.text = AtomicString(m.group(2) + m.group(4) + m.group(5))
            else:
                node.text = AtomicString(m.group(3))
            return node

        inlinemathpatterns = (
#

Inline math with $...$

            Pattern(r'(?<!\\|\$)(\$)([^\$]+)(\$)'),
#

Inline math with \(...\)

            Pattern(r'(?<!\\)(\\\()(.+?)(\\\))')
#
        )
        mathpatterns = (
#

Display style math with $$...$$

            Pattern(r'(?<!\\)(\$\$)([^\$]+)(\$\$)'),
#

Display style math with \[...\]

            Pattern(r'(?<!\\)(\\\[)(.+?)(\\\])'),
            Pattern(r'(?<!\\)(\\begin{([a-z]+?\*?)})(.+?)(\\end{\3})')
#
        )
        for i, pattern in enumerate(inlinemathpatterns):
            pattern.handleMatch = handle_match_inline
            md.inlinePatterns.add('math-inline-%d' % i, pattern, '<escape')
        for i, pattern in enumerate(mathpatterns):
            pattern.handleMatch = handle_match
            md.inlinePatterns.add('math-%d' % i, pattern, '<escape')