OLD | NEW |
1 # markdown is released under the BSD license | |
2 # Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later) | |
3 # Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) | |
4 # Copyright 2004 Manfred Stienstra (the original version) | |
5 # | |
6 # All rights reserved. | |
7 # | |
8 # Redistribution and use in source and binary forms, with or without | |
9 # modification, are permitted provided that the following conditions are met: | |
10 # | |
11 # * Redistributions of source code must retain the above copyright | |
12 # notice, this list of conditions and the following disclaimer. | |
13 # * Redistributions in binary form must reproduce the above copyright | |
14 # notice, this list of conditions and the following disclaimer in the | |
15 # documentation and/or other materials provided with the distribution. | |
16 # * Neither the name of the <organization> nor the | |
17 # names of its contributors may be used to endorse or promote products | |
18 # derived from this software without specific prior written permission. | |
19 # | |
20 # THIS SOFTWARE IS PROVIDED BY THE PYTHON MARKDOWN PROJECT ''AS IS'' AND ANY | |
21 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
22 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
23 # DISCLAIMED. IN NO EVENT SHALL ANY CONTRIBUTORS TO THE PYTHON MARKDOWN PROJECT | |
24 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
25 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
26 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
27 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
28 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
29 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
30 # POSSIBILITY OF SUCH DAMAGE. | |
31 | |
32 | |
33 """ | 1 """ |
34 Definition List Extension for Python-Markdown | 2 Definition List Extension for Python-Markdown |
35 ============================================= | 3 ============================================= |
36 | 4 |
37 Added parsing of Definition Lists to Python-Markdown. | 5 Adds parsing of Definition Lists to Python-Markdown. |
38 | 6 |
39 A simple example: | 7 See <https://pythonhosted.org/Markdown/extensions/definition_lists.html> |
| 8 for documentation. |
40 | 9 |
41 Apple | 10 Original code Copyright 2008 [Waylan Limberg](http://achinghead.com) |
42 : Pomaceous fruit of plants of the genus Malus in | |
43 the family Rosaceae. | |
44 : An american computer company. | |
45 | 11 |
46 Orange | 12 All changes Copyright 2008-2014 The Python Markdown Project |
47 : The fruit of an evergreen tree of the genus Citrus. | |
48 | 13 |
49 Copyright 2008 - [Waylan Limberg](http://achinghead.com) | 14 License: [BSD](http://www.opensource.org/licenses/bsd-license.php) |
50 | 15 |
51 """ | 16 """ |
52 | 17 |
53 from __future__ import absolute_import | 18 from __future__ import absolute_import |
54 from __future__ import unicode_literals | 19 from __future__ import unicode_literals |
55 from . import Extension | 20 from . import Extension |
56 from ..blockprocessors import BlockProcessor, ListIndentProcessor | 21 from ..blockprocessors import BlockProcessor, ListIndentProcessor |
57 from ..util import etree | 22 from ..util import etree |
58 import re | 23 import re |
59 | 24 |
60 | 25 |
61 class DefListProcessor(BlockProcessor): | 26 class DefListProcessor(BlockProcessor): |
62 """ Process Definition Lists. """ | 27 """ Process Definition Lists. """ |
63 | 28 |
64 RE = re.compile(r'(^|\n)[ ]{0,3}:[ ]{1,3}(.*?)(\n|$)') | 29 RE = re.compile(r'(^|\n)[ ]{0,3}:[ ]{1,3}(.*?)(\n|$)') |
65 NO_INDENT_RE = re.compile(r'^[ ]{0,3}[^ :]') | 30 NO_INDENT_RE = re.compile(r'^[ ]{0,3}[^ :]') |
66 | 31 |
67 def test(self, parent, block): | 32 def test(self, parent, block): |
68 return bool(self.RE.search(block)) | 33 return bool(self.RE.search(block)) |
69 | 34 |
70 def run(self, parent, blocks): | 35 def run(self, parent, blocks): |
71 | 36 |
72 raw_block = blocks.pop(0) | 37 raw_block = blocks.pop(0) |
73 m = self.RE.search(raw_block) | 38 m = self.RE.search(raw_block) |
74 terms = [l.strip() for l in raw_block[:m.start()].split('\n') if l.strip
()] | 39 terms = [l.strip() for l in |
| 40 raw_block[:m.start()].split('\n') if l.strip()] |
75 block = raw_block[m.end():] | 41 block = raw_block[m.end():] |
76 no_indent = self.NO_INDENT_RE.match(block) | 42 no_indent = self.NO_INDENT_RE.match(block) |
77 if no_indent: | 43 if no_indent: |
78 d, theRest = (block, None) | 44 d, theRest = (block, None) |
79 else: | 45 else: |
80 d, theRest = self.detab(block) | 46 d, theRest = self.detab(block) |
81 if d: | 47 if d: |
82 d = '%s\n%s' % (m.group(2), d) | 48 d = '%s\n%s' % (m.group(2), d) |
83 else: | 49 else: |
84 d = m.group(2) | 50 d = m.group(2) |
85 sibling = self.lastChild(parent) | 51 sibling = self.lastChild(parent) |
86 if not terms and sibling is None: | 52 if not terms and sibling is None: |
87 # This is not a definition item. Most likely a paragraph that | 53 # This is not a definition item. Most likely a paragraph that |
88 # starts with a colon at the begining of a document or list. | 54 # starts with a colon at the begining of a document or list. |
89 blocks.insert(0, raw_block) | 55 blocks.insert(0, raw_block) |
90 return False | 56 return False |
91 if not terms and sibling.tag == 'p': | 57 if not terms and sibling.tag == 'p': |
92 # The previous paragraph contains the terms | 58 # The previous paragraph contains the terms |
93 state = 'looselist' | 59 state = 'looselist' |
94 terms = sibling.text.split('\n') | 60 terms = sibling.text.split('\n') |
95 parent.remove(sibling) | 61 parent.remove(sibling) |
96 # Aquire new sibling | 62 # Aquire new sibling |
97 sibling = self.lastChild(parent) | 63 sibling = self.lastChild(parent) |
98 else: | 64 else: |
99 state = 'list' | 65 state = 'list' |
100 | 66 |
101 if sibling and sibling.tag == 'dl': | 67 if sibling is not None and sibling.tag == 'dl': |
102 # This is another item on an existing list | 68 # This is another item on an existing list |
103 dl = sibling | 69 dl = sibling |
104 if len(dl) and dl[-1].tag == 'dd' and len(dl[-1]): | 70 if not terms and len(dl) and dl[-1].tag == 'dd' and len(dl[-1]): |
105 state = 'looselist' | 71 state = 'looselist' |
106 else: | 72 else: |
107 # This is a new list | 73 # This is a new list |
108 dl = etree.SubElement(parent, 'dl') | 74 dl = etree.SubElement(parent, 'dl') |
109 # Add terms | 75 # Add terms |
110 for term in terms: | 76 for term in terms: |
111 dt = etree.SubElement(dl, 'dt') | 77 dt = etree.SubElement(dl, 'dt') |
112 dt.text = term | 78 dt.text = term |
113 # Add definition | 79 # Add definition |
114 self.parser.state.set(state) | 80 self.parser.state.set(state) |
115 dd = etree.SubElement(dl, 'dd') | 81 dd = etree.SubElement(dl, 'dd') |
116 self.parser.parseBlocks(dd, [d]) | 82 self.parser.parseBlocks(dd, [d]) |
117 self.parser.state.reset() | 83 self.parser.state.reset() |
118 | 84 |
119 if theRest: | 85 if theRest: |
120 blocks.insert(0, theRest) | 86 blocks.insert(0, theRest) |
121 | 87 |
| 88 |
122 class DefListIndentProcessor(ListIndentProcessor): | 89 class DefListIndentProcessor(ListIndentProcessor): |
123 """ Process indented children of definition list items. """ | 90 """ Process indented children of definition list items. """ |
124 | 91 |
125 ITEM_TYPES = ['dd'] | 92 ITEM_TYPES = ['dd'] |
126 LIST_TYPES = ['dl'] | 93 LIST_TYPES = ['dl'] |
127 | 94 |
128 def create_item(self, parent, block): | 95 def create_item(self, parent, block): |
129 """ Create a new dd and parse the block with it as the parent. """ | 96 """ Create a new dd and parse the block with it as the parent. """ |
130 dd = etree.SubElement(parent, 'dd') | 97 dd = etree.SubElement(parent, 'dd') |
131 self.parser.parseBlocks(dd, [block]) | 98 self.parser.parseBlocks(dd, [block]) |
132 | |
133 | 99 |
134 | 100 |
135 class DefListExtension(Extension): | 101 class DefListExtension(Extension): |
136 """ Add definition lists to Markdown. """ | 102 """ Add definition lists to Markdown. """ |
137 | 103 |
138 def extendMarkdown(self, md, md_globals): | 104 def extendMarkdown(self, md, md_globals): |
139 """ Add an instance of DefListProcessor to BlockParser. """ | 105 """ Add an instance of DefListProcessor to BlockParser. """ |
140 md.parser.blockprocessors.add('defindent', | 106 md.parser.blockprocessors.add('defindent', |
141 DefListIndentProcessor(md.parser), | 107 DefListIndentProcessor(md.parser), |
142 '>indent') | 108 '>indent') |
143 md.parser.blockprocessors.add('deflist', | 109 md.parser.blockprocessors.add('deflist', |
144 DefListProcessor(md.parser), | 110 DefListProcessor(md.parser), |
145 '>ulist') | 111 '>ulist') |
146 | 112 |
147 | 113 |
148 def makeExtension(configs={}): | 114 def makeExtension(*args, **kwargs): |
149 return DefListExtension(configs=configs) | 115 return DefListExtension(*args, **kwargs) |
150 | |
OLD | NEW |