1#!/usr/bin/env Python
2"""
3Definition List Extension for Python-Markdown
4=============================================
5
6Added parsing of Definition Lists to Python-Markdown.
7
8A simple example:
9
10    Apple
11    :   Pomaceous fruit of plants of the genus Malus in
12        the family Rosaceae.
13    :   An american computer company.
14
15    Orange
16    :   The fruit of an evergreen tree of the genus Citrus.
17
18Copyright 2008 - [Waylan Limberg](http://achinghead.com)
19
20"""
21
22import markdown, re
23from markdown import etree
24
25
26class DefListProcessor(markdown.blockprocessors.BlockProcessor):
27    """ Process Definition Lists. """
28
29    RE = re.compile(r'(^|\n)[ ]{0,3}:[ ]{1,3}(.*?)(\n|$)')
30
31    def test(self, parent, block):
32        return bool(self.RE.search(block))
33
34    def run(self, parent, blocks):
35        block = blocks.pop(0)
36        m = self.RE.search(block)
37        terms = [l.strip() for l in block[:m.start()].split('\n') if l.strip()]
38        d, theRest = self.detab(block[m.end():])
39        if d:
40            d = '%s\n%s' % (m.group(2), d)
41        else:
42            d = m.group(2)
43        #import ipdb; ipdb.set_trace()
44        sibling = self.lastChild(parent)
45        if not terms and sibling.tag == 'p':
46            # The previous paragraph contains the terms
47            state = 'looselist'
48            terms = sibling.text.split('\n')
49            parent.remove(sibling)
50            # Aquire new sibling
51            sibling = self.lastChild(parent)
52        else:
53            state = 'list'
54
55        if sibling and sibling.tag == 'dl':
56            # This is another item on an existing list
57            dl = sibling
58            if len(dl) and dl[-1].tag == 'dd' and len(dl[-1]):
59                state = 'looselist'
60        else:
61            # This is a new list
62            dl = etree.SubElement(parent, 'dl')
63        # Add terms
64        for term in terms:
65            dt = etree.SubElement(dl, 'dt')
66            dt.text = term
67        # Add definition
68        self.parser.state.set(state)
69        dd = etree.SubElement(dl, 'dd')
70        self.parser.parseBlocks(dd, [d])
71        self.parser.state.reset()
72
73        if theRest:
74            blocks.insert(0, theRest)
75
76class DefListIndentProcessor(markdown.blockprocessors.ListIndentProcessor):
77    """ Process indented children of definition list items. """
78
79    ITEM_TYPES = ['dd']
80    LIST_TYPES = ['dl']
81
82    def create_item(parent, block):
83        """ Create a new dd and parse the block with it as the parent. """
84        dd = markdown.etree.SubElement(parent, 'dd')
85        self.parser.parseBlocks(dd, [block])
86
87
88
89class DefListExtension(markdown.Extension):
90    """ Add definition lists to Markdown. """
91
92    def extendMarkdown(self, md, md_globals):
93        """ Add an instance of DefListProcessor to BlockParser. """
94        md.parser.blockprocessors.add('defindent',
95                                      DefListIndentProcessor(md.parser),
96                                      '>indent')
97        md.parser.blockprocessors.add('deflist',
98                                      DefListProcessor(md.parser),
99                                      '>ulist')
100
101
102def makeExtension(configs={}):
103    return DefListExtension(configs=configs)
104
105