diff options
author | Waylan Limberg <waylan@gmail.com> | 2008-11-20 19:45:33 -0500 |
---|---|---|
committer | Waylan Limberg <waylan@gmail.com> | 2008-11-20 19:45:33 -0500 |
commit | 34666742ee4b5cb03f9a9f6297d5784f553d5346 (patch) | |
tree | c9e7ece5ba86bdc3adbf7150c25cd6f9b4ed0bdb /markdown_extensions | |
parent | 517d38e552e91ebbe527a0286d43dd1daa585bcc (diff) | |
download | markdown-34666742ee4b5cb03f9a9f6297d5784f553d5346.tar.gz markdown-34666742ee4b5cb03f9a9f6297d5784f553d5346.tar.bz2 markdown-34666742ee4b5cb03f9a9f6297d5784f553d5346.zip |
Moved markdown_extensions/ to markdown/extensions. Markdown is now one package instead of two.
Diffstat (limited to 'markdown_extensions')
-rw-r--r-- | markdown_extensions/__init__.py | 0 | ||||
-rw-r--r-- | markdown_extensions/abbr.py | 95 | ||||
-rw-r--r-- | markdown_extensions/codehilite.py | 224 | ||||
-rw-r--r-- | markdown_extensions/def_list.py | 206 | ||||
-rw-r--r-- | markdown_extensions/extra.py | 52 | ||||
-rw-r--r-- | markdown_extensions/fenced_code.py | 117 | ||||
-rw-r--r-- | markdown_extensions/footnotes.py | 293 | ||||
-rw-r--r-- | markdown_extensions/headerid.py | 172 | ||||
-rw-r--r-- | markdown_extensions/imagelinks.py | 119 | ||||
-rw-r--r-- | markdown_extensions/legacy.py | 468 | ||||
-rw-r--r-- | markdown_extensions/meta.py | 90 | ||||
-rw-r--r-- | markdown_extensions/rss.py | 118 | ||||
-rw-r--r-- | markdown_extensions/tables.py | 70 | ||||
-rw-r--r-- | markdown_extensions/toc.py | 137 | ||||
-rw-r--r-- | markdown_extensions/wikilinks.py | 138 |
15 files changed, 0 insertions, 2299 deletions
diff --git a/markdown_extensions/__init__.py b/markdown_extensions/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/markdown_extensions/__init__.py +++ /dev/null diff --git a/markdown_extensions/abbr.py b/markdown_extensions/abbr.py deleted file mode 100644 index 551e4f5..0000000 --- a/markdown_extensions/abbr.py +++ /dev/null @@ -1,95 +0,0 @@ -''' -Abbreviation Extension for Python-Markdown -========================================== - -This extension adds abbreviation handling to Python-Markdown. - -Simple Usage: - - >>> import markdown - >>> text = """ - ... Some text with an ABBR and a REF. Ignore REFERENCE and ref. - ... - ... *[ABBR]: Abbreviation - ... *[REF]: Abbreviation Reference - ... """ - >>> markdown.markdown(text, ['abbr']) - u'<p>Some text with an <abbr title="Abbreviation">ABBR</abbr> and a <abbr title="Abbreviation Reference">REF</abbr>. Ignore REFERENCE and ref.</p>' - -Copyright 2007-2008 -* [Waylan Limberg](http://achinghead.com/) -* [Seemant Kulleen](http://www.kulleen.org/) - - -''' - -import markdown, re -from markdown import etree - -# Global Vars -ABBR_REF_RE = re.compile(r'[*]\[(?P<abbr>[^\]]*)\][ ]?:\s*(?P<title>.*)') - -class AbbrExtension(markdown.Extension): - """ Abbreviation Extension for Python-Markdown. """ - - def extendMarkdown(self, md, md_globals): - """ Insert AbbrPreprocessor before ReferencePreprocessor. """ - md.preprocessors.add('abbr', AbbrPreprocessor(md), '<reference') - - -class AbbrPreprocessor(markdown.Preprocessor): - """ Abbreviation Preprocessor - parse text for abbr references. """ - - def run(self, lines): - ''' - Find and remove all Abbreviation references from the text. - Each reference is set as a new AbbrPattern in the markdown instance. - - ''' - new_text = [] - for line in lines: - m = ABBR_REF_RE.match(line) - if m: - abbr = m.group('abbr').strip() - title = m.group('title').strip() - self.markdown.inlinePatterns['abbr-%s'%abbr] = \ - AbbrPattern(self._generate_pattern(abbr), title) - else: - new_text.append(line) - return new_text - - def _generate_pattern(self, text): - ''' - Given a string, returns an regex pattern to match that string. - - 'HTML' -> r'(?P<abbr>[H][T][M][L])' - - Note: we force each char as a literal match (in brackets) as we don't - know what they will be beforehand. - - ''' - chars = list(text) - for i in range(len(chars)): - chars[i] = r'[%s]' % chars[i] - return r'(?P<abbr>\b%s\b)' % (r''.join(chars)) - - -class AbbrPattern(markdown.Pattern): - """ Abbreviation inline pattern. """ - - def __init__(self, pattern, title): - markdown.Pattern.__init__(self, pattern) - self.title = title - - def handleMatch(self, m): - abbr = etree.Element('abbr') - abbr.text = m.group('abbr') - abbr.set('title', self.title) - return abbr - -def makeExtension(configs=None): - return AbbrExtension(configs=configs) - -if __name__ == "__main__": - import doctest - doctest.testmod() diff --git a/markdown_extensions/codehilite.py b/markdown_extensions/codehilite.py deleted file mode 100644 index bc75da2..0000000 --- a/markdown_extensions/codehilite.py +++ /dev/null @@ -1,224 +0,0 @@ -#!/usr/bin/python - -""" -CodeHilite Extension for Python-Markdown -======================================== - -Adds code/syntax highlighting to standard Python-Markdown code blocks. - -Copyright 2006-2008 [Waylan Limberg](http://achinghead.com/). - -Project website: <http://www.freewisdom.org/project/python-markdown/CodeHilite> -Contact: markdown@freewisdom.org - -License: BSD (see ../docs/LICENSE for details) - -Dependencies: -* [Python 2.3+](http://python.org/) -* [Markdown 2.0+](http://www.freewisdom.org/projects/python-markdown/) -* [Pygments](http://pygments.org/) - -""" - -import markdown - -# --------------- CONSTANTS YOU MIGHT WANT TO MODIFY ----------------- - -try: - TAB_LENGTH = markdown.TAB_LENGTH -except AttributeError: - TAB_LENGTH = 4 - - -# ------------------ The Main CodeHilite Class ---------------------- -class CodeHilite: - """ - Determine language of source code, and pass it into the pygments hilighter. - - Basic Usage: - >>> code = CodeHilite(src = 'some text') - >>> html = code.hilite() - - * src: Source string or any object with a .readline attribute. - - * linenos: (Boolen) Turn line numbering 'on' or 'off' (off by default). - - * css_class: Set class name of wrapper div ('codehilite' by default). - - Low Level Usage: - >>> code = CodeHilite() - >>> code.src = 'some text' # String or anything with a .readline attr. - >>> code.linenos = True # True or False; Turns line numbering on or of. - >>> html = code.hilite() - - """ - - def __init__(self, src=None, linenos=False, css_class="codehilite"): - self.src = src - self.lang = None - self.linenos = linenos - self.css_class = css_class - - def hilite(self): - """ - Pass code to the [Pygments](http://pygments.pocoo.org/) highliter with - optional line numbers. The output should then be styled with css to - your liking. No styles are applied by default - only styling hooks - (i.e.: <span class="k">). - - returns : A string of html. - - """ - - self.src = self.src.strip('\n') - - self._getLang() - - try: - from pygments import highlight - from pygments.lexers import get_lexer_by_name, guess_lexer, \ - TextLexer - from pygments.formatters import HtmlFormatter - except ImportError: - # just escape and pass through - txt = self._escape(self.src) - if self.linenos: - txt = self._number(txt) - else : - txt = '<div class="%s"><pre>%s</pre></div>\n'% \ - (self.css_class, txt) - return txt - else: - try: - lexer = get_lexer_by_name(self.lang) - except ValueError: - try: - lexer = guess_lexer(self.src) - except ValueError: - lexer = TextLexer() - formatter = HtmlFormatter(linenos=self.linenos, - cssclass=self.css_class) - return highlight(self.src, lexer, formatter) - - def _escape(self, txt): - """ basic html escaping """ - txt = txt.replace('&', '&') - txt = txt.replace('<', '<') - txt = txt.replace('>', '>') - txt = txt.replace('"', '"') - return txt - - def _number(self, txt): - """ Use <ol> for line numbering """ - # Fix Whitespace - txt = txt.replace('\t', ' '*TAB_LENGTH) - txt = txt.replace(" "*4, " ") - txt = txt.replace(" "*3, " ") - txt = txt.replace(" "*2, " ") - - # Add line numbers - lines = txt.splitlines() - txt = '<div class="codehilite"><pre><ol>\n' - for line in lines: - txt += '\t<li>%s</li>\n'% line - txt += '</ol></pre></div>\n' - return txt - - - def _getLang(self): - """ - Determines language of a code block from shebang lines and whether said - line should be removed or left in place. If the sheband line contains a - path (even a single /) then it is assumed to be a real shebang lines and - left alone. However, if no path is given (e.i.: #!python or :::python) - then it is assumed to be a mock shebang for language identifitation of a - code fragment and removed from the code block prior to processing for - code highlighting. When a mock shebang (e.i: #!python) is found, line - numbering is turned on. When colons are found in place of a shebang - (e.i.: :::python), line numbering is left in the current state - off - by default. - - """ - - import re - - #split text into lines - lines = self.src.split("\n") - #pull first line to examine - fl = lines.pop(0) - - c = re.compile(r''' - (?:(?:::+)|(?P<shebang>[#]!)) # Shebang or 2 or more colons. - (?P<path>(?:/\w+)*[/ ])? # Zero or 1 path - (?P<lang>[\w+-]*) # The language - ''', re.VERBOSE) - # search first line for shebang - m = c.search(fl) - if m: - # we have a match - try: - self.lang = m.group('lang').lower() - except IndexError: - self.lang = None - if m.group('path'): - # path exists - restore first line - lines.insert(0, fl) - if m.group('shebang'): - # shebang exists - use line numbers - self.linenos = True - else: - # No match - lines.insert(0, fl) - - self.src = "\n".join(lines).strip("\n") - - - -# ------------------ The Markdown Extension ------------------------------- -class HiliteTreeprocessor(markdown.Treeprocessor): - """ Hilight source code in code blocks. """ - - def run(self, root): - """ Find code blocks and store in htmlStash. """ - blocks = root.getiterator('pre') - for block in blocks: - children = block.getchildren() - if len(children) == 1 and children[0].tag == 'code': - code = CodeHilite(children[0].text, - linenos=self.config['force_linenos'][0], - css_class=self.config['css_class'][0]) - placeholder = self.markdown.htmlStash.store(code.hilite(), - 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 CodeHiliteExtension(markdown.Extension): - """ Add source code hilighting to markdown codeblocks. """ - - def __init__(self, configs): - # define default configs - self.config = { - 'force_linenos' : [False, "Force line numbers - Default: False"], - 'css_class' : ["codehilite", - "Set class name for wrapper <div> - Default: codehilite"], - } - - # Override defaults with user settings - for key, value in configs: - self.setConfig(key, value) - - def extendMarkdown(self, md, md_globals): - """ Add HilitePostprocessor to Markdown instance. """ - hiliter = HiliteTreeprocessor(md) - hiliter.config = self.config - md.treeprocessors.add("hilite", hiliter, "_begin") - - -def makeExtension(configs={}): - return CodeHiliteExtension(configs=configs) - diff --git a/markdown_extensions/def_list.py b/markdown_extensions/def_list.py deleted file mode 100644 index 831e840..0000000 --- a/markdown_extensions/def_list.py +++ /dev/null @@ -1,206 +0,0 @@ -#!/usr/bin/env Python -""" -Definition List Extension for Python-Markdown -============================================= - -Added parsing of Definition Lists to Python-Markdown. - -A simple example: - - Apple - : Pomaceous fruit of plants of the genus Malus in - the family Rosaceae. - : An american computer company. - - Orange - : The fruit of an evergreen tree of the genus Citrus. - -Copyright 2008 - [Waylan Limberg](http://achinghead.com) - -""" - -import markdown, re -from markdown import etree, CORE_RE - -DEF_RE = re.compile(r'^[ ]{0,3}:[ ]{1,3}(.*)$') - -class DefListParser(markdown.MarkdownParser): - """ Subclass of MarkdownParser which adds definition list parsing. """ - - def parseChunk(self, parent_elem, lines, inList=0, looseList=0): - """Process a chunk of markdown-formatted text and attach the parse to - an ElementTree node. - - Process a section of a source document, looking for high - level structural elements like lists, block quotes, code - segments, html blocks, etc. Some those then get stripped - of their high level markup (e.g. get unindented) and the - lower-level markup is processed recursively. - - Keyword arguments: - - * parent_elem: The ElementTree element to which the content will be - added. - * lines: a list of lines - * inList: a level - - Returns: None - - """ - # Loop through lines until none left. - while lines: - # Skipping empty line - if not lines[0]: - lines = lines[1:] - continue - - # Check if this section starts with a list, a blockquote or - # a code block. If so, process them. - processFn = { 'ul': self._MarkdownParser__processUList, - 'ol': self._MarkdownParser__processOList, - 'quoted': self._MarkdownParser__processQuote, - 'tabbed': self._MarkdownParser__processCodeBlock} - for regexp in ['ul', 'ol', 'quoted', 'tabbed']: - m = CORE_RE[regexp].match(lines[0]) - if m: - processFn[regexp](parent_elem, lines, inList) - return - - # We are NOT looking at one of the high-level structures like - # lists or blockquotes. So, it's just a regular paragraph - # (though perhaps nested inside a list or something else). If - # we are NOT inside a list, we just need to look for a blank - # line to find the end of the block. If we ARE inside a - # list, however, we need to consider that a sublist does not - # need to be separated by a blank line. Rather, the following - # markup is legal: - # - # * The top level list item - # - # Another paragraph of the list. This is where we are now. - # * Underneath we might have a sublist. - # - - if inList: - start, lines = self._MarkdownParser__linesUntil(lines, (lambda line: - CORE_RE['ul'].match(line) - or CORE_RE['ol'].match(line) - or not line.strip())) - self.parseChunk(parent_elem, start, inList-1, - looseList=looseList) - inList = inList-1 - - else: # Ok, so it's just a simple block - test = lambda line: not line.strip() or line[0] == '>' - paragraph, lines = self._MarkdownParser__linesUntil(lines, test) - if len(paragraph) and paragraph[0].startswith('#'): - self._MarkdownParser__processHeader(parent_elem, paragraph) - elif len(paragraph) and CORE_RE["isline3"].match(paragraph[0]): - self._MarkdownParser__processHR(parent_elem) - lines = paragraph[1:] + lines - elif paragraph: - paragraph, lines, looseList = self._processDefs(parent_elem, - paragraph, - lines, - looseList) - if len(paragraph): - self._MarkdownParser__processParagraph(parent_elem, - paragraph, - inList, - looseList) - - if lines and not lines[0].strip(): - lines = lines[1:] # skip the first (blank) line - - - def _processDefs(self, parentElem, paragraph, lines, looseList): - """ Check a paragraph for definition lists and process. """ - terms = [] - defs = [] - i = 0 - while i < len(paragraph): - m = DEF_RE.match(paragraph[i]) - if m: - d, theRest = self.detectTabbed(paragraph[i+1:]) - d.insert(0, m.group(1)) - if d: - defs.append(d) - i += len(d) - else: - terms.append(paragraph[i]) - i += 1 - if defs: - if not terms: - # The previous paragraph must contain the terms - c = parentElem.getchildren() - if c and c[-1].tag == "p" and c[-1].text: - terms = c[-1].text.split("\n") - parentElem.remove(c[-1]) - looseList = 1 - # check for extra paragraphs of a def - extradef, lines = self.detectTabbed(lines) - if extradef: - looseList = 1 - defs[-1].extend(extradef) - # Build a tree from the terms and defs - c = parentElem.getchildren() - if c and c[-1].tag == "dl": - dl = c[-1] - else: - dl = etree.SubElement(parentElem, "dl") - for term in terms: - dt = etree.SubElement(dl, "dt") - dt.text = term - for d in defs: - dd = etree.SubElement(dl, "dd") - self.parseChunk(dd, d, looseList = looseList) - return [], lines, looseList - else: - return terms, lines, looseList - - def _MarkdownParser__processParagraph(self, parentElem, paragraph, - inList, looseList): - - if ((parentElem.tag == 'li' or parentElem.tag == 'dd') - and not (looseList or parentElem.getchildren())): - - # If this is the first paragraph inside "li", don't - # put <p> around it - append the paragraph bits directly - # onto parentElem - el = parentElem - else: - # Otherwise make a "p" element - el = etree.SubElement(parentElem, "p") - - dump = [] - - # Searching for hr or header - for line in paragraph: - # it's hr - if CORE_RE["isline3"].match(line): - el.text = "\n".join(dump) - self._MarkdownParser__processHR(el) - dump = [] - # it's header - elif line.startswith("#"): - el.text = "\n".join(dump) - self._MarkdownParser__processHeader(parentElem, [line]) - dump = [] - else: - dump.append(line) - if dump: - text = "\n".join(dump) - el.text = text - - -class DefListExtension(markdown.Extension): - """ Add definition lists to Markdown. """ - - def extendMarkdown(self, md, md_globals): - """ Set the core parser to an instance of DefListParser. """ - md.parser = DefListParser() - - -def makeExtension(configs={}): - return DefListExtension(configs=configs) - diff --git a/markdown_extensions/extra.py b/markdown_extensions/extra.py deleted file mode 100644 index e99bec4..0000000 --- a/markdown_extensions/extra.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python -""" -Python-Markdown Extra Extension -=============================== - -A compilation of various Python-Markdown extensions that imitates -[PHP Markdown Extra](http://michelf.com/projects/php-markdown/extra/). - -As no-one has yet written a Definition List extension for Python- -Markdown, definition lists are not yet supported by Extra. - -Note that each of the individual extensions still need to be available -on your PYTHONPATH. This extension simply wraps them all up as a -convenience so that only one extension needs to be listed when -initiating Markdown. See the documentation for each individual -extension for specifics about that extension. - -In the event that one or more of the supported extensions are not -available for import, Markdown will simply continue without that -extension. If you would like to be notified of such failures, -you may set Python-Markdown's logger level to "WARN". - -There may be additional extensions that are distributed with -Python-Markdown that are not included here in Extra. Those extensions -are not part of PHP Markdown Extra, and therefore, not part of -Python-Markdown Extra. If you really would like Extra to include -additional extensions, we suggest creating your own clone of Extra -under a differant name. You could also edit the `extensions` global -variable defined below, but be aware that such changes may be lost -when you upgrade to any future version of Python-Markdown. - -""" - -import markdown - -extensions = ['fenced_code', - 'footnotes', - 'headerid', - 'tables', - 'abbr', - ] - - -class ExtraExtension(markdown.Extension): - """ Add various extensions to Markdown class.""" - - def extendMarkdown(self, md, md_globals): - """ Register extension instances. """ - md.registerExtensions(extensions, self.config) - -def makeExtension(configs={}): - return ExtraExtension(configs=dict(configs)) diff --git a/markdown_extensions/fenced_code.py b/markdown_extensions/fenced_code.py deleted file mode 100644 index fa768c0..0000000 --- a/markdown_extensions/fenced_code.py +++ /dev/null @@ -1,117 +0,0 @@ -#!/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'<p>A paragraph before a fenced code block:</p>\\n<pre><code>Fenced code block\\n</code></pre>' - -Works with safe_mode also (we check this because we are using the HtmlStash): - - >>> markdown.markdown(text, extensions=['fenced_code'], safe_mode='replace') - u'<p>A paragraph before a fenced code block:</p>\\n<pre><code>Fenced code block\\n</code></pre>' - -Include tilde's in a code block and wrap with blank lines: - - >>> text = ''' - ... ~~~~~~~~ - ... - ... ~~~~ - ... - ... ~~~~~~~~''' - >>> markdown.markdown(text, extensions=['fenced_code']) - u'<pre><code>\\n~~~~\\n\\n</code></pre>' - -Multiple blocks and language tags: - - >>> text = ''' - ... ~~~~ - ... block one - ... ~~~~{.python} - ... - ... ~~~~ - ... <p>block two</p> - ... ~~~~{.html}''' - >>> markdown.markdown(text, extensions=['fenced_code']) - u'<pre><code class="python">block one\\n</code></pre>\\n\\n<pre><code class="html"><p>block two</p>\\n</code></pre>' - -Copyright 2007-2008 [Waylan Limberg](http://achinghead.com/). - -Project website: <http://www.freewisdom.org/project/python-markdown/Fenced__Code__Blocks> -Contact: markdown@freewisdom.org - -License: BSD (see ../docs/LICENSE for details) - -Dependencies: -* [Python 2.3+](http://python.org) -* [Markdown 2.0+](http://www.freewisdom.org/projects/python-markdown/) - -""" - -import markdown, re - -# Global vars -FENCED_BLOCK_RE = re.compile( \ - r'(?P<fence>^~{3,})[ ]*\n(?P<code>.*?)(?P=fence)[ ]*(\{\.(?P<lang>[a-zA-Z0-9_-]*)\})?[ ]*$', - re.MULTILINE|re.DOTALL - ) -CODE_WRAP = '<pre><code%s>%s</code></pre>' -LANG_TAG = ' class="%s"' - - -class FencedCodeExtension(markdown.Extension): - - def extendMarkdown(self, md, md_globals): - """ Add FencedBlockPreprocessor to the Markdown instance. """ - - md.preprocessors.add('fenced_code_block', - FencedBlockPreprocessor(md), - "_begin") - - -class FencedBlockPreprocessor(markdown.Preprocessor): - - def run(self, lines): - """ Match and store Fenced Code Blocks in the HtmlStash. """ - 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') - 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() - - -if __name__ == "__main__": - import doctest - doctest.testmod() diff --git a/markdown_extensions/footnotes.py b/markdown_extensions/footnotes.py deleted file mode 100644 index f65a79d..0000000 --- a/markdown_extensions/footnotes.py +++ /dev/null @@ -1,293 +0,0 @@ -""" -========================= FOOTNOTES ================================= - -This section adds footnote handling to markdown. It can be used as -an example for extending python-markdown with relatively complex -functionality. While in this case the extension is included inside -the module itself, it could just as easily be added from outside the -module. Not that all markdown classes above are ignorant about -footnotes. All footnote functionality is provided separately and -then added to the markdown instance at the run time. - -Footnote functionality is attached by calling extendMarkdown() -method of FootnoteExtension. The method also registers the -extension to allow it's state to be reset by a call to reset() -method. - -Example: - Footnotes[^1] have a label[^label] and a definition[^!DEF]. - - [^1]: This is a footnote - [^label]: A footnote on "label" - [^!DEF]: The footnote for definition - -""" - -import re, markdown -from markdown import etree - -FN_BACKLINK_TEXT = "zz1337820767766393qq" -NBSP_PLACEHOLDER = "qq3936677670287331zz" -DEF_RE = re.compile(r'(\ ?\ ?\ ?)\[\^([^\]]*)\]:\s*(.*)') -TABBED_RE = re.compile(r'((\t)|( ))(.*)') - -class FootnoteExtension(markdown.Extension): - """ Footnote Extension. """ - - def __init__ (self, configs): - """ Setup configs. """ - self.config = {'PLACE_MARKER': - ["///Footnotes Go Here///", - "The text string that marks where the footnotes go"]} - - for key, value in configs: - self.config[key][0] = value - - self.reset() - - def extendMarkdown(self, md, md_globals): - """ Add pieces to Markdown. """ - md.registerExtension(self) - self.parser = md.parser - # Insert a preprocessor before ReferencePreprocessor - md.preprocessors.add("footnote", FootnotePreprocessor(self), - "<reference") - # Insert an inline pattern before ImageReferencePattern - FOOTNOTE_RE = r'\[\^([^\]]*)\]' # blah blah [^1] blah - md.inlinePatterns.add("footnote", FootnotePattern(FOOTNOTE_RE, self), - "<reference") - # Insert a tree-processor that would actually add the footnote div - # This must be before the inline treeprocessor so inline patterns - # run on the contents of the div. - md.treeprocessors.add("footnote", FootnoteTreeprocessor(self), - "<inline") - # Insert a postprocessor after amp_substitute oricessor - md.postprocessors.add("footnote", FootnotePostprocessor(self), - ">amp_substitute") - - def reset(self): - """ Clear the footnotes on reset. """ - self.footnotes = markdown.OrderedDict() - - def findFootnotesPlaceholder(self, root): - """ Return ElementTree Element that contains Footnote placeholder. """ - def finder(element): - for child in element: - if child.text: - if child.text.find(self.getConfig("PLACE_MARKER")) > -1: - return child, True - if child.tail: - if child.tail.find(self.getConfig("PLACE_MARKER")) > -1: - return (child, element), False - finder(child) - return None - - res = finder(root) - return res - - def setFootnote(self, id, text): - """ Store a footnote for later retrieval. """ - self.footnotes[id] = text - - def makeFootnoteId(self, id): - """ Return footnote link id. """ - return 'fn:%s' % id - - def makeFootnoteRefId(self, id): - """ Return footnote back-link id. """ - return 'fnref:%s' % id - - def makeFootnotesDiv(self, root): - """ Return div of footnotes as et Element. """ - - if not self.footnotes.keys(): - return None - - div = etree.Element("div") - div.set('class', 'footnote') - hr = etree.SubElement(div, "hr") - ol = etree.SubElement(div, "ol") - - for id in self.footnotes.keys(): - li = etree.SubElement(ol, "li") - li.set("id", self.makeFootnoteId(id)) - self.parser.parseChunk(li, self.footnotes[id]) - backlink = etree.Element("a") - backlink.set("href", "#" + self.makeFootnoteRefId(id)) - backlink.set("rev", "footnote") - backlink.set("title", "Jump back to footnote %d in the text" % \ - (self.footnotes.index(id)+1)) - backlink.text = FN_BACKLINK_TEXT - - if li.getchildren(): - node = li[-1] - if node.tag == "p": - node.text = node.text + NBSP_PLACEHOLDER - node.append(backlink) - else: - p = etree.SubElement(li, "p") - p.append(backlink) - return div - - -class FootnotePreprocessor(markdown.Preprocessor): - """ Find all footnote references and store for later use. """ - - def __init__ (self, footnotes): - self.footnotes = footnotes - - def run(self, lines): - lines = self._handleFootnoteDefinitions(lines) - text = "\n".join(lines) - return text.split("\n") - - def _handleFootnoteDefinitions(self, lines): - """ - Recursively find all footnote definitions in lines. - - Keywords: - - * lines: A list of lines of text - - Return: A list of lines with footnote definitions removed. - - """ - i, id, footnote = self._findFootnoteDefinition(lines) - - if id : - plain = lines[:i] - detabbed, theRest = self.detectTabbed(lines[i+1:]) - self.footnotes.setFootnote(id, - footnote + "\n" - + "\n".join(detabbed)) - more_plain = self._handleFootnoteDefinitions(theRest) - return plain + [""] + more_plain - else : - return lines - - def _findFootnoteDefinition(self, lines): - """ - Find the parts of a footnote definition. - - Keywords: - - * lines: A list of lines of text. - - Return: A three item tuple containing the index of the first line of a - footnote definition, the id of the definition and the body of the - definition. - - """ - counter = 0 - for line in lines: - m = DEF_RE.match(line) - if m: - return counter, m.group(2), m.group(3) - counter += 1 - return counter, None, None - - def detectTabbed(self, lines): - """ Find indented text and remove indent before further proccesing. - - Keyword arguments: - - * lines: an array of strings - - Returns: a list of post processed items and the unused - remainder of the original list - - """ - items = [] - item = -1 - i = 0 # to keep track of where we are - - def detab(line): - match = TABBED_RE.match(line) - if match: - return match.group(4) - - for line in lines: - if line.strip(): # Non-blank line - line = detab(line) - if line: - items.append(line) - i += 1 - continue - else: - return items, lines[i:] - - else: # Blank line: _maybe_ we are done. - i += 1 # advance - - # Find the next non-blank line - for j in range(i, len(lines)): - if lines[j].strip(): - next_line = lines[j]; break - else: - break # There is no more text; we are done. - - # Check if the next non-blank line is tabbed - if detab(next_line): # Yes, more work to do. - items.append("") - continue - else: - break # No, we are done. - else: - i += 1 - - return items, lines[i:] - - -class FootnotePattern(markdown.Pattern): - """ InlinePattern for footnote markers in a document's body text. """ - - def __init__(self, pattern, footnotes): - markdown.Pattern.__init__(self, pattern) - self.footnotes = footnotes - - def handleMatch(self, m): - sup = etree.Element("sup") - a = etree.SubElement(sup, "a") - id = m.group(2) - sup.set('id', self.footnotes.makeFootnoteRefId(id)) - a.set('href', '#' + self.footnotes.makeFootnoteId(id)) - a.set('rel', 'footnote') - a.text = str(self.footnotes.footnotes.index(id) + 1) - return sup - - -class FootnoteTreeprocessor(markdown.Treeprocessor): - """ Build and append footnote div to end of document. """ - - def __init__ (self, footnotes): - self.footnotes = footnotes - - def run(self, root): - footnotesDiv = self.footnotes.makeFootnotesDiv(root) - if footnotesDiv: - result = self.footnotes.findFootnotesPlaceholder(root) - if result: - node, isText = result - if isText: - node.text = None - node.getchildren().insert(0, footnotesDiv) - else: - child, element = node - ind = element.getchildren().find(child) - element.getchildren().insert(ind + 1, footnotesDiv) - child.tail = None - fnPlaceholder.parent.replaceChild(fnPlaceholder, footnotesDiv) - else: - root.append(footnotesDiv) - -class FootnotePostprocessor(markdown.Postprocessor): - """ Replace placeholders with html entities. """ - - def run(self, text): - text = text.replace(FN_BACKLINK_TEXT, "↩") - return text.replace(NBSP_PLACEHOLDER, " ") - -def makeExtension(configs=[]): - """ Return an instance of the FootnoteExtension """ - return FootnoteExtension(configs=configs) - diff --git a/markdown_extensions/headerid.py b/markdown_extensions/headerid.py deleted file mode 100644 index 294bfd9..0000000 --- a/markdown_extensions/headerid.py +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/python - -""" -HeaderID Extension for Python-Markdown -====================================== - -Adds ability to set HTML IDs for headers. - -Basic usage: - - >>> import markdown - >>> text = "# Some Header # {#some_id}" - >>> md = markdown.markdown(text, ['headerid']) - >>> md - u'<h1 id="some_id">Some Header</h1>' - -All header IDs are unique: - - >>> text = ''' - ... #Header - ... #Another Header {#header} - ... #Third Header {#header}''' - >>> md = markdown.markdown(text, ['headerid']) - >>> md - u'<h1 id="header">Header</h1>\\n<h1 id="header_1">Another Header</h1>\\n<h1 id="header_2">Third Header</h1>' - -To fit within a html template's hierarchy, set the header base level: - - >>> text = ''' - ... #Some Header - ... ## Next Level''' - >>> md = markdown.markdown(text, ['headerid(level=3)']) - >>> md - u'<h3 id="some_header">Some Header</h3>\\n<h4 id="next_level">Next Level</h4>' - -Turn off auto generated IDs: - - >>> text = ''' - ... # Some Header - ... # Header with ID # { #foo }''' - >>> md = markdown.markdown(text, ['headerid(forceid=False)']) - >>> md - u'<h1>Some Header</h1>\\n<h1 id="foo">Header with ID</h1>' - -Use with MetaData extension: - - >>> text = '''header_level: 2 - ... header_forceid: Off - ... - ... # A Header''' - >>> md = markdown.markdown(text, ['headerid', 'meta']) - >>> md - u'<h2>A Header</h2>' - -Copyright 2007-2008 [Waylan Limberg](http://achinghead.com/). - -Project website: <http://www.freewisdom.org/project/python-markdown/HeaderId> -Contact: markdown@freewisdom.org - -License: BSD (see ../docs/LICENSE for details) - -Dependencies: -* [Python 2.3+](http://python.org) -* [Markdown 2.0+](http://www.freewisdom.org/projects/python-markdown/) - -""" - -import markdown -from markdown import etree -import re -from string import ascii_lowercase, digits, punctuation - -ID_CHARS = ascii_lowercase + digits + '-_' - -HEADER_RE = re.compile(r'''^(\#{1,6}) # group(1) = string of hashes - ( [^{^#]*) # group(2) = Header text - [\#]* # optional closing hashes - (?:[ \t]*\{[ \t]*\#([-_:a-zA-Z0-9]+)[ \t]*\})? # group(3) = id attr''', - re.VERBOSE) - -IDCOUNT_RE = re.compile(r'^(.*)_([0-9]+)$') - -class HeaderIdExtension (markdown.Extension) : - def __init__(self, configs): - # set defaults - self.config = { - 'level' : ['1', 'Base level for headers.'], - 'forceid' : ['True', 'Force all headers to have an id.'] - } - - for key, value in configs: - self.setConfig(key, value) - - - def extendMarkdown(self, md, md_globals) : - - md.IDs = [] - - def _processHeaderId(parent_elem, paragraph) : - ''' - Overrides __processHeader of MarkdownParser() and - adds an 'id' to the header. - ''' - m = HEADER_RE.match(paragraph[0]) - if m : - start_level, force_id = _get_meta() - level = len(m.group(1)) + start_level - if level > 6: - level = 6 - h = etree.Element("h%d" % level) - parent_elem.append(h) - h.text = m.group(2).strip() - if m.group(3) : - h.set('id', _unique_id(m.group(3))) - elif force_id: - h.set('id', _create_id(m.group(2).strip())) - else : - message(CRITICAL, "We've got a problem header!") - - md.parser._MarkdownParser__processHeader = _processHeaderId - - def _get_meta(): - ''' Return meta data suported by this ext as a tuple ''' - level = int(self.config['level'][0]) - 1 - force = _str2bool(self.config['forceid'][0]) - if hasattr(md, 'Meta'): - if md.Meta.has_key('header_level'): - level = int(md.Meta['header_level'][0]) - 1 - if md.Meta.has_key('header_forceid'): - force = _str2bool(md.Meta['header_forceid'][0]) - return level, force - - def _str2bool(s, default=False): - ''' Convert a string to a booleen value. ''' - s = str(s) - if s.lower() in ['0', 'f', 'false', 'off', 'no', 'n']: - return False - elif s.lower() in ['1', 't', 'true', 'on', 'yes', 'y']: - return True - return default - - def _unique_id(id): - ''' Ensure ID is unique. Append '_1', '_2'... if not ''' - while id in md.IDs: - m = IDCOUNT_RE.match(id) - if m: - id = '%s_%d'% (m.group(1), int(m.group(2))+1) - else: - id = '%s_%d'% (id, 1) - md.IDs.append(id) - return id - - - def _create_id(header): - ''' Return ID from Header text. ''' - h = '' - for c in header.lower().replace(' ', '_'): - if c in ID_CHARS: - h += c - elif c not in punctuation: - h += '+' - return _unique_id(h) - - - -def makeExtension(configs=None) : - return HeaderIdExtension(configs=configs) - -if __name__ == "__main__": - import doctest - doctest.testmod() - diff --git a/markdown_extensions/imagelinks.py b/markdown_extensions/imagelinks.py deleted file mode 100644 index ba86d24..0000000 --- a/markdown_extensions/imagelinks.py +++ /dev/null @@ -1,119 +0,0 @@ -""" -========================= IMAGE LINKS ================================= - - -Turns paragraphs like - -<~~~~~~~~~~~~~~~~~~~~~~~~ -dir/subdir -dir/subdir -dir/subdir -~~~~~~~~~~~~~~ -dir/subdir -dir/subdir -dir/subdir -~~~~~~~~~~~~~~~~~~~> - -Into mini-photo galleries. - -""" - -import re, markdown -import url_manager - - -IMAGE_LINK = """<a href="%s"><img src="%s" title="%s"/></a>""" -SLIDESHOW_LINK = """<a href="%s" target="_blank">[slideshow]</a>""" -ALBUM_LINK = """ <a href="%s">[%s]</a>""" - - -class ImageLinksExtension (markdown.Extension): - - def extendMarkdown(self, md, md_globals) : - - md.preprocessors.add("imagelink", ImageLinkPreprocessor(md), "_begin") - - -class ImageLinkPreprocessor (markdown.Preprocessor): - - def run(self, lines) : - - url = url_manager.BlogEntryUrl(url_manager.BlogUrl("all"), - "2006/08/29/the_rest_of_our") - - - all_images = [] - blocks = [] - in_image_block = False - - new_lines = [] - - for line in lines : - - if line.startswith("<~~~~~~~") : - albums = [] - rows = [] - in_image_block = True - - if not in_image_block : - - new_lines.append(line) - - else : - - line = line.strip() - - if line.endswith("~~~~~~>") or not line : - in_image_block = False - new_block = "<div><br/><center><span class='image-links'>\n" - - album_url_hash = {} - - for row in rows : - for photo_url, title in row : - new_block += " " - new_block += IMAGE_LINK % (photo_url, - photo_url.get_thumbnail(), - title) - - album_url_hash[str(photo_url.get_album())] = 1 - - new_block += "<br/>" - - new_block += "</span>" - new_block += SLIDESHOW_LINK % url.get_slideshow() - - album_urls = album_url_hash.keys() - album_urls.sort() - - if len(album_urls) == 1 : - new_block += ALBUM_LINK % (album_urls[0], "complete album") - else : - for i in range(len(album_urls)) : - new_block += ALBUM_LINK % (album_urls[i], - "album %d" % (i + 1) ) - - new_lines.append(new_block + "</center><br/></div>") - - elif line[1:6] == "~~~~~" : - rows.append([]) # start a new row - else : - parts = line.split() - line = parts[0] - title = " ".join(parts[1:]) - - album, photo = line.split("/") - photo_url = url.get_photo(album, photo, - len(all_images)+1) - all_images.append(photo_url) - rows[-1].append((photo_url, title)) - - if not album in albums : - albums.append(album) - - return new_lines - - -def makeExtension(configs) : - return ImageLinksExtension(configs) - diff --git a/markdown_extensions/legacy.py b/markdown_extensions/legacy.py deleted file mode 100644 index 5b3a9c2..0000000 --- a/markdown_extensions/legacy.py +++ /dev/null @@ -1,468 +0,0 @@ -""" -Legacy Extension for Python-Markdown -==================================== - -Replaces the core parser with the old one. - -""" - -import markdown, re -from markdown import etree - -"""Basic and reusable regular expressions.""" - -def wrapRe(raw_re) : return re.compile("^%s$" % raw_re, re.DOTALL) -CORE_RE = { - 'header': wrapRe(r'(#{1,6})[ \t]*(.*?)[ \t]*(#*)'), # # A title - 'reference-def': wrapRe(r'(\ ?\ ?\ ?)\[([^\]]*)\]:\s*([^ ]*)(.*)'), - # [Google]: http://www.google.com/ - 'containsline': wrapRe(r'([-]*)$|^([=]*)'), # -----, =====, etc. - 'ol': wrapRe(r'[ ]{0,3}[\d]*\.\s+(.*)'), # 1. text - 'ul': wrapRe(r'[ ]{0,3}[*+-]\s+(.*)'), # "* text" - 'isline1': wrapRe(r'(\**)'), # *** - 'isline2': wrapRe(r'(\-*)'), # --- - 'isline3': wrapRe(r'(\_*)'), # ___ - 'tabbed': wrapRe(r'((\t)|( ))(.*)'), # an indented line - 'quoted': wrapRe(r'[ ]{0,2}> ?(.*)'), # a quoted block ("> ...") - 'containsline': re.compile(r'^([-]*)$|^([=]*)$', re.M), - 'attr': re.compile("\{@([^\}]*)=([^\}]*)}") # {@id=123} -} - -class MarkdownParser: - """Parser Markdown into a ElementTree.""" - - def __init__(self): - pass - - def parseDocument(self, lines): - """Parse a markdown string into an ElementTree.""" - # Create a ElementTree from the lines - root = etree.Element("div") - buffer = [] - for line in lines: - if line.startswith("#"): - self.parseChunk(root, buffer) - buffer = [line] - else: - buffer.append(line) - - self.parseChunk(root, buffer) - - return etree.ElementTree(root) - - def parseChunk(self, parent_elem, lines, inList=0, looseList=0): - """Process a chunk of markdown-formatted text and attach the parse to - an ElementTree node. - - Process a section of a source document, looking for high - level structural elements like lists, block quotes, code - segments, html blocks, etc. Some those then get stripped - of their high level markup (e.g. get unindented) and the - lower-level markup is processed recursively. - - Keyword arguments: - - * parent_elem: The ElementTree element to which the content will be - added. - * lines: a list of lines - * inList: a level - - Returns: None - - """ - # Loop through lines until none left. - while lines: - # Skipping empty line - if not lines[0]: - lines = lines[1:] - continue - - # Check if this section starts with a list, a blockquote or - # a code block. If so, process them. - processFn = { 'ul': self.__processUList, - 'ol': self.__processOList, - 'quoted': self.__processQuote, - 'tabbed': self.__processCodeBlock} - for regexp in ['ul', 'ol', 'quoted', 'tabbed']: - m = CORE_RE[regexp].match(lines[0]) - if m: - processFn[regexp](parent_elem, lines, inList) - return - - # We are NOT looking at one of the high-level structures like - # lists or blockquotes. So, it's just a regular paragraph - # (though perhaps nested inside a list or something else). If - # we are NOT inside a list, we just need to look for a blank - # line to find the end of the block. If we ARE inside a - # list, however, we need to consider that a sublist does not - # need to be separated by a blank line. Rather, the following - # markup is legal: - # - # * The top level list item - # - # Another paragraph of the list. This is where we are now. - # * Underneath we might have a sublist. - # - - if inList: - start, lines = self.__linesUntil(lines, (lambda line: - CORE_RE['ul'].match(line) - or CORE_RE['ol'].match(line) - or not line.strip())) - self.parseChunk(parent_elem, start, inList-1, - looseList=looseList) - inList = inList-1 - - else: # Ok, so it's just a simple block - test = lambda line: not line.strip() or line[0] == '>' - paragraph, lines = self.__linesUntil(lines, test) - if len(paragraph) and paragraph[0].startswith('#'): - self.__processHeader(parent_elem, paragraph) - elif len(paragraph) and CORE_RE["isline3"].match(paragraph[0]): - self.__processHR(parent_elem) - lines = paragraph[1:] + lines - elif paragraph: - self.__processParagraph(parent_elem, paragraph, - inList, looseList) - - if lines and not lines[0].strip(): - lines = lines[1:] # skip the first (blank) line - - def __processHR(self, parentElem): - hr = etree.SubElement(parentElem, "hr") - - def __processHeader(self, parentElem, paragraph): - m = CORE_RE['header'].match(paragraph[0]) - if m: - level = len(m.group(1)) - h = etree.SubElement(parentElem, "h%d" % level) - h.text = m.group(2).strip() - else: - message(CRITICAL, "We've got a problem header!") - - def __processParagraph(self, parentElem, paragraph, inList, looseList): - - if ( parentElem.tag == 'li' - and not (looseList or parentElem.getchildren())): - - # If this is the first paragraph inside "li", don't - # put <p> around it - append the paragraph bits directly - # onto parentElem - el = parentElem - else: - # Otherwise make a "p" element - el = etree.SubElement(parentElem, "p") - - dump = [] - - # Searching for hr or header - for line in paragraph: - # it's hr - if CORE_RE["isline3"].match(line): - el.text = "\n".join(dump) - self.__processHR(el) - dump = [] - # it's header - elif line.startswith("#"): - el.text = "\n".join(dump) - self.__processHeader(parentElem, [line]) - dump = [] - else: - dump.append(line) - if dump: - text = "\n".join(dump) - el.text = text - - def __processUList(self, parentElem, lines, inList): - self.__processList(parentElem, lines, inList, listexpr='ul', tag='ul') - - def __processOList(self, parentElem, lines, inList): - self.__processList(parentElem, lines, inList, listexpr='ol', tag='ol') - - def __processList(self, parentElem, lines, inList, listexpr, tag): - """ - Given a list of document lines starting with a list item, - finds the end of the list, breaks it up, and recursively - processes each list item and the remainder of the text file. - - Keyword arguments: - - * parentElem: A ElementTree element to which the content will be added - * lines: a list of lines - * inList: a level - - Returns: None - - """ - ul = etree.SubElement(parentElem, tag) # ul might actually be '<ol>' - - looseList = 0 - - # Make a list of list items - items = [] - item = -1 - - i = 0 # a counter to keep track of where we are - for line in lines: - loose = 0 - if not line.strip(): - # If we see a blank line, this _might_ be the end of the list - i += 1 - loose = 1 - - # Find the next non-blank line - for j in range(i, len(lines)): - if lines[j].strip(): - next = lines[j] - break - else: - # There is no more text => end of the list - break - - # Check if the next non-blank line is still a part of the list - - if ( CORE_RE[listexpr].match(next) or - CORE_RE['tabbed'].match(next) ): - # get rid of any white space in the line - items[item].append(line.strip()) - looseList = loose or looseList - continue - else: - break # found end of the list - - # Now we need to detect list items (at the current level) - # while also detabing child elements if necessary - - for expr in ['ul', 'ol', 'tabbed']: - m = CORE_RE[expr].match(line) - if m: - if expr in ['ul', 'ol']: # We are looking at a new item - #if m.group(1) : - # Removed the check to allow for a blank line - # at the beginning of the list item - items.append([m.group(1)]) - item += 1 - elif expr == 'tabbed': # This line needs to be detabbed - items[item].append(m.group(4)) #after the 'tab' - i += 1 - break - else: - items[item].append(line) # Just regular continuation - i += 1 # added on 2006.02.25 - else: - i += 1 - - # Add the ElementTree elements - for item in items: - li = etree.SubElement(ul, "li") - self.parseChunk(li, item, inList + 1, looseList = looseList) - - # Process the remaining part of the section - self.parseChunk(parentElem, lines[i:], inList) - - def __linesUntil(self, lines, condition): - """ - A utility function to break a list of lines upon the - first line that satisfied a condition. The condition - argument should be a predicate function. - - """ - i = -1 - for line in lines: - i += 1 - if condition(line): - break - else: - i += 1 - return lines[:i], lines[i:] - - def __processQuote(self, parentElem, lines, inList): - """ - Given a list of document lines starting with a quote finds - the end of the quote, unindents it and recursively - processes the body of the quote and the remainder of the - text file. - - Keyword arguments: - - * parentElem: ElementTree element to which the content will be added - * lines: a list of lines - * inList: a level - - Returns: None - - """ - dequoted = [] - i = 0 - blank_line = False # allow one blank line between paragraphs - for line in lines: - m = CORE_RE['quoted'].match(line) - if m: - dequoted.append(m.group(1)) - i += 1 - blank_line = False - elif not blank_line and line.strip() != '': - dequoted.append(line) - i += 1 - elif not blank_line and line.strip() == '': - dequoted.append(line) - i += 1 - blank_line = True - else: - break - - blockquote = etree.SubElement(parentElem, "blockquote") - - self.parseChunk(blockquote, dequoted, inList) - self.parseChunk(parentElem, lines[i:], inList) - - def __processCodeBlock(self, parentElem, lines, inList): - """ - Given a list of document lines starting with a code block - finds the end of the block, puts it into the ElementTree verbatim - wrapped in ("<pre><code>") and recursively processes the - the remainder of the text file. - - Keyword arguments: - - * parentElem: ElementTree element to which the content will be added - * lines: a list of lines - * inList: a level - - Returns: None - - """ - detabbed, theRest = self.detectTabbed(lines) - pre = etree.SubElement(parentElem, "pre") - code = etree.SubElement(pre, "code") - text = "\n".join(detabbed).rstrip()+"\n" - code.text = markdown.AtomicString(text) - self.parseChunk(parentElem, theRest, inList) - - def detectTabbed(self, lines): - """ Find indented text and remove indent before further proccesing. - - Keyword arguments: - - * lines: an array of strings - - Returns: a list of post processed items and the unused - remainder of the original list - - """ - items = [] - item = -1 - i = 0 # to keep track of where we are - - def detab(line): - match = CORE_RE['tabbed'].match(line) - if match: - return match.group(4) - - for line in lines: - if line.strip(): # Non-blank line - line = detab(line) - if line: - items.append(line) - i += 1 - continue - else: - return items, lines[i:] - - else: # Blank line: _maybe_ we are done. - i += 1 # advance - - # Find the next non-blank line - for j in range(i, len(lines)): - if lines[j].strip(): - next_line = lines[j]; break - else: - break # There is no more text; we are done. - - # Check if the next non-blank line is tabbed - if detab(next_line): # Yes, more work to do. - items.append("") - continue - else: - break # No, we are done. - else: - i += 1 - - return items, lines[i:] - -class HeaderPreprocessor(markdown.Preprocessor): - - """Replace underlined headers with hashed headers. - - (To avoid the need for lookahead later.) - - """ - - def run (self, lines): - i = -1 - while i+1 < len(lines): - i = i+1 - if not lines[i].strip(): - continue - - if lines[i].startswith("#"): - lines.insert(i+1, "\n") - - if (i+1 <= len(lines) - and lines[i+1] - and lines[i+1][0] in ['-', '=']): - - underline = lines[i+1].strip() - - if underline == "="*len(underline): - lines[i] = "# " + lines[i].strip() - lines[i+1] = "" - elif underline == "-"*len(underline): - lines[i] = "## " + lines[i].strip() - lines[i+1] = "" - - return lines - - -class LinePreprocessor(markdown.Preprocessor): - """Convert HR lines to "___" format.""" - blockquote_re = re.compile(r'^(> )+') - - def run (self, lines): - for i in range(len(lines)): - prefix = '' - m = self.blockquote_re.search(lines[i]) - if m: - prefix = m.group(0) - if self._isLine(lines[i][len(prefix):]): - lines[i] = prefix + "___" - return lines - - def _isLine(self, block): - """Determine if a block should be replaced with an <HR>""" - if block.startswith(" "): - return False # a code block - text = "".join([x for x in block if not x.isspace()]) - if len(text) <= 2: - return False - for pattern in ['isline1', 'isline2', 'isline3']: - m = CORE_RE[pattern].match(text) - if (m and m.group(1)): - return True - else: - return False - - -class LegacyExtension(markdown.Extension): - """ Replace Markdown's core parser. """ - - def extendMarkdown(self, md, md_globals): - """ Set the core parser to an instance of MarkdownParser. """ - md.parser = MarkdownParser() - md.preprocessors.add ("header", HeaderPreprocessor(self), "<reference") - md.preprocessors.add("line", LinePreprocessor(self), "<reference") - - -def makeExtension(configs={}): - return LegacyExtension(configs=configs) - diff --git a/markdown_extensions/meta.py b/markdown_extensions/meta.py deleted file mode 100644 index 52b71d9..0000000 --- a/markdown_extensions/meta.py +++ /dev/null @@ -1,90 +0,0 @@ -#!usr/bin/python - -""" -Meta Data Extension for Python-Markdown -======================================= - -This extension adds Meta Data handling to markdown. - -Basic Usage: - - >>> import markdown - >>> text = '''Title: A Test Doc. - ... Author: Waylan Limberg - ... John Doe - ... Blank_Data: - ... - ... The body. This is paragraph one. - ... ''' - >>> md = markdown.Markdown(['meta']) - >>> md.convert(text) - u'<p>The body. This is paragraph one.</p>' - >>> md.Meta - {u'blank_data': [u''], u'author': [u'Waylan Limberg', u'John Doe'], u'title': [u'A Test Doc.']} - -Make sure text without Meta Data still works (markdown < 1.6b returns a <p>). - - >>> text = ' Some Code - not extra lines of meta data.' - >>> md = markdown.Markdown(['meta']) - >>> md.convert(text) - u'<pre><code>Some Code - not extra lines of meta data.\\n</code></pre>' - >>> md.Meta - {} - -Copyright 2007-2008 [Waylan Limberg](http://achinghead.com). - -Project website: <http://www.freewisdom.org/project/python-markdown/Meta-Data> -Contact: markdown@freewisdom.org - -License: BSD (see ../docs/LICENSE for details) - -""" - -import markdown, re - -# Global Vars -META_RE = re.compile(r'^[ ]{0,3}(?P<key>[A-Za-z0-9_-]+):\s*(?P<value>.*)') -META_MORE_RE = re.compile(r'^[ ]{4,}(?P<value>.*)') - -class MetaExtension (markdown.Extension): - """ Meta-Data extension for Python-Markdown. """ - - def extendMarkdown(self, md, md_globals): - """ Add MetaPreprocessor to Markdown instance. """ - - md.preprocessors.add("meta", MetaPreprocessor(md), "_begin") - - -class MetaPreprocessor(markdown.Preprocessor): - """ Get Meta-Data. """ - - def run(self, lines): - """ Parse Meta-Data and store in Markdown.Meta. """ - meta = {} - key = None - while 1: - line = lines.pop(0) - if line.strip() == '': - break # blank line - done - m1 = META_RE.match(line) - if m1: - key = m1.group('key').lower().strip() - meta[key] = [m1.group('value').strip()] - else: - m2 = META_MORE_RE.match(line) - if m2 and key: - # Add another line to existing key - meta[key].append(m2.group('value').strip()) - else: - lines.insert(0, line) - break # no meta data - done - self.markdown.Meta = meta - return lines - - -def makeExtension(configs={}): - return MetaExtension(configs=configs) - -if __name__ == "__main__": - import doctest - doctest.testmod() diff --git a/markdown_extensions/rss.py b/markdown_extensions/rss.py deleted file mode 100644 index fa05fef..0000000 --- a/markdown_extensions/rss.py +++ /dev/null @@ -1,118 +0,0 @@ -import markdown -from markdown import etree - -DEFAULT_URL = "http://www.freewisdom.org/projects/python-markdown/" -DEFAULT_CREATOR = "Yuri Takhteyev" -DEFAULT_TITLE = "Markdown in Python" -GENERATOR = "http://www.freewisdom.org/projects/python-markdown/markdown2rss" - -month_map = { "Jan" : "01", - "Feb" : "02", - "March" : "03", - "April" : "04", - "May" : "05", - "June" : "06", - "July" : "07", - "August" : "08", - "September" : "09", - "October" : "10", - "November" : "11", - "December" : "12" } - -def get_time(heading): - - heading = heading.split("-")[0] - heading = heading.strip().replace(",", " ").replace(".", " ") - - month, date, year = heading.split() - month = month_map[month] - - return rdftime(" ".join((month, date, year, "12:00:00 AM"))) - -def rdftime(time): - - time = time.replace(":", " ") - time = time.replace("/", " ") - time = time.split() - return "%s-%s-%sT%s:%s:%s-08:00" % (time[0], time[1], time[2], - time[3], time[4], time[5]) - - -def get_date(text): - return "date" - -class RssExtension (markdown.Extension): - - def extendMarkdown(self, md, md_globals): - - self.config = { 'URL' : [DEFAULT_URL, "Main URL"], - 'CREATOR' : [DEFAULT_CREATOR, "Feed creator's name"], - 'TITLE' : [DEFAULT_TITLE, "Feed title"] } - - md.xml_mode = True - - # Insert a tree-processor that would actually add the title tag - treeprocessor = RssTreeProcessor(self) - treeprocessor.ext = self - md.treeprocessors.append(treeprocessor) - md.stripTopLevelTags = 0 - md.docType = '<?xml version="1.0" encoding="utf-8"?>\n' - -class RssTreeProcessor (markdown.Treeprocessor): - - def __init__(self, md): - - pass - - def run (self, root): - - rss = etree.Element("rss") - rss.set("version", "2.0") - - channel = etree.SubElement(rss, "channel") - - for tag, text in (("title", self.ext.getConfig("TITLE")), - ("link", self.ext.getConfig("URL")), - ("description", None)): - - element = etree.SubElement(channel, tag) - element.text = text - - for child in root: - - - if child.tag in ["h1", "h2", "h3", "h4", "h5"] : - - heading = child.text.strip() - - item = etree.SubElement(channel, "item") - - link = etree.SubElement(item, "link") - link.text = self.ext.getConfig("URL") - - title = etree.SubElement(item, "title") - title.text = heading - - guid = ''.join([x for x in heading if x.isalnum()]) - - guidElem = etree.SubElement(item, "guid") - guidElem.text = guid - guidElem.set("isPermaLink", "false") - - elif child.tag in ["p"] : - if item: - description = etree.SubElement(item, "description") - if len(child): - content = "\n".join([etree.tostring(node) - for node in child]) - else: - content = child.text - pholder = self.stash.store("<![CDATA[ %s]]>" % content) - description.text = pholder - - return rss - - -def makeExtension(configs): - - return RssExtension(configs) diff --git a/markdown_extensions/tables.py b/markdown_extensions/tables.py deleted file mode 100644 index 0e5cd96..0000000 --- a/markdown_extensions/tables.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python - -""" -Table extension for Python-Markdown -""" - -import markdown -from markdown import etree - -class TablePattern(markdown.Pattern) : - def __init__ (self, md): - markdown.Pattern.__init__(self, r'(^|\n)\|([^\n]*)\|') - self.md = md - - def handleMatch(self, m): - - # a single line represents a row - tr = etree.Element('tr') - - # chunks between pipes represent cells - - for t in m.group(3).split('|'): - - if len(t) >= 2 and t.startswith('*') and t.endswith('*'): - # if a cell is bounded by asterisks, it is a <th> - td = etree.Element('th') - t = t[1:-1] - else: - # otherwise it is a <td> - td = etree.Element('td') - - # add text ot inline section, later it will be - # processed by core - - td.text = t - tr.append(td) - tr.tail = "\n" - - return tr - -class TableTreeprocessor(markdown.Treeprocessor): - - def _findElement(self, element, name): - result = [] - for child in element: - if child.tag == name: - result.append(child) - result += self._findElement(child, name) - return result - - def run(self, root): - - for element in self._findElement(root, "p"): - for child in element: - if child.tail: - element.tag = "table" - break - - - - -class TableExtension(markdown.Extension): - def extendMarkdown(self, md, md_globals): - md.inlinePatterns.add('table', TablePattern(md), "<backtick") - md.treeprocessors.add('table', TableTreeprocessor(), "<prettify") - - -def makeExtension(configs): - return TableExtension(configs) - diff --git a/markdown_extensions/toc.py b/markdown_extensions/toc.py deleted file mode 100644 index cc8c93b..0000000 --- a/markdown_extensions/toc.py +++ /dev/null @@ -1,137 +0,0 @@ -""" -Table of Contents Extension for Python-Markdown -* * * - -(c) 2008 [Jack Miller](http://codezen.org) - -Dependencies: -* [Markdown 2.0+](http://www.freewisdom.org/projects/python-markdown/) - -""" -import markdown -from markdown import etree -import re - -class TocTreeprocessor (markdown.Treeprocessor): - # Iterator wrapper to get parent and child all at once - def iterparent(self, root): - for parent in root.getiterator(): - for child in parent: - yield parent, child - - def run(self, doc) : - div = etree.Element("div") - div.attrib["class"] = "toc" - last_li = None - - # Add title to the div - if self.config["title"][0]: - header = etree.SubElement(div, "span") - header.attrib["class"] = "toctitle" - header.text = self.config["title"][0] - - level = 0 - list_stack=[div] - header_rgx = re.compile("[Hh][123456]") - - # Get a list of id attributes - used_ids = [] - for c in doc.getiterator(): - if c.attrib.has_key("id"): - used_ids.append(c.attrib["id"]) - - for (p, c) in self.iterparent(doc): - if not c.text: - continue - - # To keep the output from screwing up the - # validation by putting a <div> inside of a <p> - # we actually replace the <p> in its entirety. - - if c.text.find(self.config["marker"][0]) > -1: - for i in range(len(p)): - if p[i] == c: - p[i] = div - break - - if header_rgx.match(c.tag): - tag_level = int(c.tag[-1]) - - # Regardless of how many levels we jumped - # only one list should be created, since - # empty lists containing lists are illegal. - - if tag_level < level: - list_stack.pop() - level = tag_level - - if tag_level > level: - newlist = etree.Element("ul") - if last_li: - last_li.append(newlist) - else: - list_stack[-1].append(newlist) - list_stack.append(newlist) - level = tag_level - - # Do not override pre-existing ids - if not c.attrib.has_key("id"): - id = self.config["slugify"][0](c.text) - if id in used_ids: - ctr = 1 - while "%s_%d" % (id, ctr) in used_ids: - ctr += 1 - id = "%s_%d" % (id, ctr) - used_ids.append(id) - c.attrib["id"] = id - else: - id = c.attrib["id"] - - # List item link, to be inserted into the toc div - last_li = etree.Element("li") - link = etree.SubElement(last_li, "a") - link.text = c.text - link.attrib["href"] = '#' + id - - if int(self.config["anchorlink"][0]): - anchor = etree.SubElement(c, "a") - anchor.text = c.text - anchor.attrib["href"] = "#" + id - anchor.attrib["class"] = "toclink" - c.text = "" - - list_stack[-1].append(last_li) - -class TocExtension (markdown.Extension): - def __init__(self, configs): - self.config = { "marker" : ["[TOC]", - "Text to find and replace with Table of Contents -" - "Defaults to \"[TOC]\""], - "slugify" : [self.slugify, - "Function to generate anchors based on header text-" - "Defaults to a built in slugify function."], - "title" : [None, - "Title to insert into TOC <div> - " - "Defaults to None"], - "anchorlink" : [0, - "1 if header should be a self link" - "Defaults to 0"]} - - for key, value in configs: - self.setConfig(key, value) - - # This is exactly the same as Django's slugify - def slugify(self, value): - """ Slugify a string, to make it URL friendly. """ - import unicodedata - value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore') - value = unicode(re.sub('[^\w\s-]', '', value).strip().lower()) - return re.sub('[-\s]+','-',value) - - def extendMarkdown(self, md, md_globals) : - tocext = TocTreeprocessor(md) - tocext.config = self.config - md.treeprocessors.add("toc", tocext, "_begin") - -def makeExtension(configs={}) : - return TocExtension(configs=configs) diff --git a/markdown_extensions/wikilinks.py b/markdown_extensions/wikilinks.py deleted file mode 100644 index 0caeb48..0000000 --- a/markdown_extensions/wikilinks.py +++ /dev/null @@ -1,138 +0,0 @@ -#!/usr/bin/env python - -''' -WikiLinks Extension for Python-Markdown -====================================== - -Converts [[WikiLinks]] to relative links. Requires Python-Markdown 2.0+ - -Basic usage: - - >>> import markdown - >>> text = "Some text with a [[WikiLink]]." - >>> html = markdown.markdown(text, ['wikilinks']) - >>> html - u'<p>Some text with a <a class="wikilink" href="/WikiLink/">WikiLink</a>.</p>' - -Whitespace behavior: - - >>> markdown.markdown('[[ foo bar_baz ]]', ['wikilinks']) - u'<p><a class="wikilink" href="/foo_bar_baz/">foo bar_baz</a></p>' - >>> markdown.markdown('foo [[ ]] bar', ['wikilinks']) - u'<p>foo bar</p>' - -To define custom settings the simple way: - - >>> markdown.markdown(text, - ... ['wikilinks(base_url=/wiki/,end_url=.html,html_class=foo)'] - ... ) - u'<p>Some text with a <a class="foo" href="/wiki/WikiLink.html">WikiLink</a>.</p>' - -Custom settings the complex way: - - >>> md = markdown.Markdown( - ... extensions = ['wikilinks'], - ... extension_configs = {'wikilinks': [ - ... ('base_url', 'http://example.com/'), - ... ('end_url', '.html'), - ... ('html_class', '') ]}, - ... safe_mode = True) - >>> md.convert(text) - u'<p>Some text with a <a href="http://example.com/WikiLink.html">WikiLink</a>.</p>' - -Use MetaData with mdx_meta.py (Note the blank html_class in MetaData): - - >>> text = """wiki_base_url: http://example.com/ - ... wiki_end_url: .html - ... wiki_html_class: - ... - ... Some text with a [[WikiLink]].""" - >>> md = markdown.Markdown(extensions=['meta', 'wikilinks']) - >>> md.convert(text) - u'<p>Some text with a <a href="http://example.com/WikiLink.html">WikiLink</a>.</p>' - -MetaData should not carry over to next document: - - >>> md.convert("No [[MetaData]] here.") - u'<p>No <a class="wikilink" href="/MetaData/">MetaData</a> here.</p>' - -From the command line: - - python markdown.py -x wikilinks(base_url=http://example.com/,end_url=.html,html_class=foo) src.txt - -By [Waylan Limberg](http://achinghead.com/). - -License: [BSD](http://www.opensource.org/licenses/bsd-license.php) - -Dependencies: -* [Python 2.3+](http://python.org) -* [Markdown 2.0+](http://www.freewisdom.org/projects/python-markdown/) -''' - -import markdown - -class WikiLinkExtension (markdown.Extension) : - def __init__(self, configs): - # set extension defaults - self.config = { - 'base_url' : ['/', 'String to append to beginning or URL.'], - 'end_url' : ['/', 'String to append to end of URL.'], - 'html_class' : ['wikilink', 'CSS hook. Leave blank for none.'] - } - - # Override defaults with user settings - for key, value in configs : - self.setConfig(key, value) - - def extendMarkdown(self, md, md_globals): - self.md = md - - # append to end of inline patterns - WIKILINK_RE = r'\[\[([A-Za-z0-9_ -]+)\]\]' - wikilinkPattern = WikiLinks(WIKILINK_RE, self.config) - wikilinkPattern.md = md - md.inlinePatterns.add('wikilink', wikilinkPattern, "_end") - - -class WikiLinks (markdown.BasePattern) : - def __init__(self, pattern, config): - markdown.BasePattern.__init__(self, pattern) - self.config = config - - def handleMatch(self, m): - if m.group(2).strip(): - base_url, end_url, html_class = self._getMeta() - label = m.group(2).strip() - url = '%s%s%s'% (base_url, label.replace(' ', '_'), end_url) - a = markdown.etree.Element('a') - a.text = markdown.AtomicString(label) - a.set('href', url) - if html_class: - a.set('class', html_class) - else: - a = '' - return a - - def _getMeta(self): - """ Return meta data or config data. """ - base_url = self.config['base_url'][0] - end_url = self.config['end_url'][0] - html_class = self.config['html_class'][0] - if hasattr(self.md, 'Meta'): - if self.md.Meta.has_key('wiki_base_url'): - base_url = self.md.Meta['wiki_base_url'][0] - if self.md.Meta.has_key('wiki_end_url'): - end_url = self.md.Meta['wiki_end_url'][0] - if self.md.Meta.has_key('wiki_html_class'): - html_class = self.md.Meta['wiki_html_class'][0] - return base_url, end_url, html_class - - -def makeExtension(configs=None) : - return WikiLinkExtension(configs=configs) - - -if __name__ == "__main__": - import doctest - doctest.testmod() - |