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