Home | History | Annotate | Download | only in extensions
      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 """
     34 Definition List Extension for Python-Markdown
     35 =============================================
     36 
     37 Added parsing of Definition Lists to Python-Markdown.
     38 
     39 A simple example:
     40 
     41     Apple
     42     :   Pomaceous fruit of plants of the genus Malus in 
     43         the family Rosaceae.
     44     :   An american computer company.
     45 
     46     Orange
     47     :   The fruit of an evergreen tree of the genus Citrus.
     48 
     49 Copyright 2008 - [Waylan Limberg](http://achinghead.com)
     50 
     51 """
     52 
     53 from __future__ import absolute_import
     54 from __future__ import unicode_literals
     55 from . import Extension
     56 from ..blockprocessors import BlockProcessor, ListIndentProcessor
     57 from ..util import etree
     58 import re
     59 
     60 
     61 class DefListProcessor(BlockProcessor):
     62     """ Process Definition Lists. """
     63 
     64     RE = re.compile(r'(^|\n)[ ]{0,3}:[ ]{1,3}(.*?)(\n|$)')
     65     NO_INDENT_RE = re.compile(r'^[ ]{0,3}[^ :]')
     66 
     67     def test(self, parent, block):
     68         return bool(self.RE.search(block))
     69 
     70     def run(self, parent, blocks):
     71 
     72         raw_block = blocks.pop(0)
     73         m = self.RE.search(raw_block)
     74         terms = [l.strip() for l in raw_block[:m.start()].split('\n') if l.strip()]
     75         block = raw_block[m.end():]
     76         no_indent = self.NO_INDENT_RE.match(block)
     77         if no_indent:
     78             d, theRest = (block, None)
     79         else:
     80             d, theRest = self.detab(block)
     81         if d:
     82             d = '%s\n%s' % (m.group(2), d)
     83         else:
     84             d = m.group(2)
     85         sibling = self.lastChild(parent)
     86         if not terms and sibling is None:
     87             # This is not a definition item. Most likely a paragraph that 
     88             # starts with a colon at the begining of a document or list.
     89             blocks.insert(0, raw_block)
     90             return False
     91         if not terms and sibling.tag == 'p':
     92             # The previous paragraph contains the terms
     93             state = 'looselist'
     94             terms = sibling.text.split('\n')
     95             parent.remove(sibling)
     96             # Aquire new sibling
     97             sibling = self.lastChild(parent)
     98         else:
     99             state = 'list'
    100 
    101         if sibling and sibling.tag == 'dl':
    102             # This is another item on an existing list
    103             dl = sibling
    104             if len(dl) and dl[-1].tag == 'dd' and len(dl[-1]):
    105                 state = 'looselist'
    106         else:
    107             # This is a new list
    108             dl = etree.SubElement(parent, 'dl')
    109         # Add terms
    110         for term in terms:
    111             dt = etree.SubElement(dl, 'dt')
    112             dt.text = term
    113         # Add definition
    114         self.parser.state.set(state)
    115         dd = etree.SubElement(dl, 'dd')
    116         self.parser.parseBlocks(dd, [d])
    117         self.parser.state.reset()
    118 
    119         if theRest:
    120             blocks.insert(0, theRest)
    121 
    122 class DefListIndentProcessor(ListIndentProcessor):
    123     """ Process indented children of definition list items. """
    124 
    125     ITEM_TYPES = ['dd']
    126     LIST_TYPES = ['dl']
    127 
    128     def create_item(self, parent, block):
    129         """ Create a new dd and parse the block with it as the parent. """
    130         dd = etree.SubElement(parent, 'dd')
    131         self.parser.parseBlocks(dd, [block])
    132  
    133 
    134 
    135 class DefListExtension(Extension):
    136     """ Add definition lists to Markdown. """
    137 
    138     def extendMarkdown(self, md, md_globals):
    139         """ Add an instance of DefListProcessor to BlockParser. """
    140         md.parser.blockprocessors.add('defindent',
    141                                       DefListIndentProcessor(md.parser),
    142                                       '>indent')
    143         md.parser.blockprocessors.add('deflist', 
    144                                       DefListProcessor(md.parser),
    145                                       '>ulist')
    146 
    147 
    148 def makeExtension(configs={}):
    149     return DefListExtension(configs=configs)
    150 
    151