blob: 368321d4624a6440b7c9e5eeb72c6dc78d4cd20b [file] [log] [blame]
dprankeb08af212015-10-06 17:44:36 -07001"""
2Tables Extension for Python-Markdown
3====================================
4
5Added parsing of tables to Python-Markdown.
6
7See <https://pythonhosted.org/Markdown/extensions/tables.html>
8for documentation.
9
10Original code Copyright 2009 [Waylan Limberg](http://achinghead.com)
11
12All changes Copyright 2008-2014 The Python Markdown Project
13
14License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
15
16"""
17
18from __future__ import absolute_import
19from __future__ import unicode_literals
20from . import Extension
21from ..blockprocessors import BlockProcessor
22from ..util import etree
23
24
25class TableProcessor(BlockProcessor):
26 """ Process Tables. """
27
28 def test(self, parent, block):
29 rows = block.split('\n')
30 return (len(rows) > 1 and '|' in rows[0] and
31 '|' in rows[1] and '-' in rows[1] and
32 rows[1].strip()[0] in ['|', ':', '-'])
33
34 def run(self, parent, blocks):
35 """ Parse a table block and build table. """
36 block = blocks.pop(0).split('\n')
37 header = block[0].strip()
38 seperator = block[1].strip()
39 rows = [] if len(block) < 3 else block[2:]
40 # Get format type (bordered by pipes or not)
41 border = False
42 if header.startswith('|'):
43 border = True
44 # Get alignment of columns
45 align = []
46 for c in self._split_row(seperator, border):
47 if c.startswith(':') and c.endswith(':'):
48 align.append('center')
49 elif c.startswith(':'):
50 align.append('left')
51 elif c.endswith(':'):
52 align.append('right')
53 else:
54 align.append(None)
55 # Build table
56 table = etree.SubElement(parent, 'table')
57 thead = etree.SubElement(table, 'thead')
58 self._build_row(header, thead, align, border)
59 tbody = etree.SubElement(table, 'tbody')
60 for row in rows:
61 self._build_row(row.strip(), tbody, align, border)
62
63 def _build_row(self, row, parent, align, border):
64 """ Given a row of text, build table cells. """
65 tr = etree.SubElement(parent, 'tr')
66 tag = 'td'
67 if parent.tag == 'thead':
68 tag = 'th'
69 cells = self._split_row(row, border)
70 # We use align here rather than cells to ensure every row
71 # contains the same number of columns.
72 for i, a in enumerate(align):
73 c = etree.SubElement(tr, tag)
74 try:
75 c.text = cells[i].strip()
76 except IndexError: # pragma: no cover
77 c.text = ""
78 if a:
79 c.set('align', a)
80
81 def _split_row(self, row, border):
82 """ split a row of text into list of cells. """
83 if border:
84 if row.startswith('|'):
85 row = row[1:]
86 if row.endswith('|'):
87 row = row[:-1]
88 return row.split('|')
89
90
91class TableExtension(Extension):
92 """ Add tables to Markdown. """
93
94 def extendMarkdown(self, md, md_globals):
95 """ Add an instance of TableProcessor to BlockParser. """
96 md.parser.blockprocessors.add('table',
97 TableProcessor(md.parser),
98 '<hashheader')
99
100
101def makeExtension(*args, **kwargs):
102 return TableExtension(*args, **kwargs)