16516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru#!/usr/bin/env Python
26516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru"""
36516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruDefinition List Extension for Python-Markdown
46516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru=============================================
56516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
66516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruAdded parsing of Definition Lists to Python-Markdown.
76516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
86516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruA simple example:
96516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
106516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    Apple
116516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    :   Pomaceous fruit of plants of the genus Malus in
126516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        the family Rosaceae.
136516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    :   An american computer company.
146516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
156516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    Orange
166516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    :   The fruit of an evergreen tree of the genus Citrus.
176516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
186516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste QueruCopyright 2008 - [Waylan Limberg](http://achinghead.com)
196516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
206516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru"""
216516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
226516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruimport markdown, re
236516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Querufrom markdown import etree
246516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
256516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
266516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruclass DefListProcessor(markdown.blockprocessors.BlockProcessor):
276516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """ Process Definition Lists. """
286516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
296516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    RE = re.compile(r'(^|\n)[ ]{0,3}:[ ]{1,3}(.*?)(\n|$)')
306516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
316516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def test(self, parent, block):
326516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        return bool(self.RE.search(block))
336516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
346516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def run(self, parent, blocks):
356516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        block = blocks.pop(0)
366516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        m = self.RE.search(block)
376516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        terms = [l.strip() for l in block[:m.start()].split('\n') if l.strip()]
386516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        d, theRest = self.detab(block[m.end():])
396516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if d:
406516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            d = '%s\n%s' % (m.group(2), d)
416516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        else:
426516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            d = m.group(2)
436516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        #import ipdb; ipdb.set_trace()
446516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        sibling = self.lastChild(parent)
456516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if not terms and sibling.tag == 'p':
466516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            # The previous paragraph contains the terms
476516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            state = 'looselist'
486516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            terms = sibling.text.split('\n')
496516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            parent.remove(sibling)
506516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            # Aquire new sibling
516516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            sibling = self.lastChild(parent)
526516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        else:
536516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            state = 'list'
546516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
556516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if sibling and sibling.tag == 'dl':
566516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            # This is another item on an existing list
576516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            dl = sibling
586516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            if len(dl) and dl[-1].tag == 'dd' and len(dl[-1]):
596516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                state = 'looselist'
606516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        else:
616516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            # This is a new list
626516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            dl = etree.SubElement(parent, 'dl')
636516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        # Add terms
646516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        for term in terms:
656516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            dt = etree.SubElement(dl, 'dt')
666516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            dt.text = term
676516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        # Add definition
686516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        self.parser.state.set(state)
696516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        dd = etree.SubElement(dl, 'dd')
706516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        self.parser.parseBlocks(dd, [d])
716516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        self.parser.state.reset()
726516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
736516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        if theRest:
746516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru            blocks.insert(0, theRest)
756516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
766516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruclass DefListIndentProcessor(markdown.blockprocessors.ListIndentProcessor):
776516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """ Process indented children of definition list items. """
786516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
796516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    ITEM_TYPES = ['dd']
806516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    LIST_TYPES = ['dl']
816516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
826516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def create_item(parent, block):
836516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """ Create a new dd and parse the block with it as the parent. """
846516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        dd = markdown.etree.SubElement(parent, 'dd')
856516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        self.parser.parseBlocks(dd, [block])
866516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
876516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
886516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
896516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queruclass DefListExtension(markdown.Extension):
906516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    """ Add definition lists to Markdown. """
916516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
926516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    def extendMarkdown(self, md, md_globals):
936516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        """ Add an instance of DefListProcessor to BlockParser. """
946516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        md.parser.blockprocessors.add('defindent',
956516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                                      DefListIndentProcessor(md.parser),
966516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                                      '>indent')
976516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru        md.parser.blockprocessors.add('deflist',
986516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                                      DefListProcessor(md.parser),
996516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru                                      '>ulist')
1006516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1016516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
1026516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Querudef makeExtension(configs={}):
1036516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru    return DefListExtension(configs=configs)
1046516b99bb74dfb7187a08f7090bf7ca22a006f15Jean-Baptiste Queru
105