summaryrefslogtreecommitdiff
path: root/src/mistune/plugins/table.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/mistune/plugins/table.py')
-rw-r--r--src/mistune/plugins/table.py179
1 files changed, 179 insertions, 0 deletions
diff --git a/src/mistune/plugins/table.py b/src/mistune/plugins/table.py
new file mode 100644
index 0000000..d3bc4c2
--- /dev/null
+++ b/src/mistune/plugins/table.py
@@ -0,0 +1,179 @@
+import re
+from ..helpers import PREVENT_BACKSLASH
+
+# https://michelf.ca/projects/php-markdown/extra/#table
+
+__all__ = ['table', 'table_in_quote', 'table_in_list']
+
+
+TABLE_PATTERN = (
+ r'^ {0,3}\|(?P<table_head>.+)\|[ \t]*\n'
+ r' {0,3}\|(?P<table_align> *[-:]+[-| :]*)\|[ \t]*\n'
+ r'(?P<table_body>(?: {0,3}\|.*\|[ \t]*(?:\n|$))*)\n*'
+)
+NP_TABLE_PATTERN = (
+ r'^ {0,3}(?P<nptable_head>\S.*\|.*)\n'
+ r' {0,3}(?P<nptable_align>[-:]+ *\|[-| :]*)\n'
+ r'(?P<nptable_body>(?:.*\|.*(?:\n|$))*)\n*'
+)
+
+TABLE_CELL = re.compile(r'^ {0,3}\|(.+)\|[ \t]*$')
+CELL_SPLIT = re.compile(r' *' + PREVENT_BACKSLASH + r'\| *')
+ALIGN_CENTER = re.compile(r'^ *:-+: *$')
+ALIGN_LEFT = re.compile(r'^ *:-+ *$')
+ALIGN_RIGHT = re.compile(r'^ *-+: *$')
+
+
+def parse_table(block, m, state):
+ pos = m.end()
+ header = m.group('table_head')
+ align = m.group('table_align')
+ thead, aligns = _process_thead(header, align)
+ if not thead:
+ return
+
+ rows = []
+ body = m.group('table_body')
+ for text in body.splitlines():
+ m = TABLE_CELL.match(text)
+ if not m: # pragma: no cover
+ return
+ row = _process_row(m.group(1), aligns)
+ if not row:
+ return
+ rows.append(row)
+
+ children = [thead, {'type': 'table_body', 'children': rows}]
+ state.append_token({'type': 'table', 'children': children})
+ return pos
+
+
+def parse_nptable(block, m, state):
+ header = m.group('nptable_head')
+ align = m.group('nptable_align')
+ thead, aligns = _process_thead(header, align)
+ if not thead:
+ return
+
+ rows = []
+ body = m.group('nptable_body')
+ for text in body.splitlines():
+ row = _process_row(text, aligns)
+ if not row:
+ return
+ rows.append(row)
+
+ children = [thead, {'type': 'table_body', 'children': rows}]
+ state.append_token({'type': 'table', 'children': children})
+ return m.end()
+
+
+def _process_thead(header, align):
+ headers = CELL_SPLIT.split(header)
+ aligns = CELL_SPLIT.split(align)
+ if len(headers) != len(aligns):
+ return None, None
+
+ for i, v in enumerate(aligns):
+ if ALIGN_CENTER.match(v):
+ aligns[i] = 'center'
+ elif ALIGN_LEFT.match(v):
+ aligns[i] = 'left'
+ elif ALIGN_RIGHT.match(v):
+ aligns[i] = 'right'
+ else:
+ aligns[i] = None
+
+ children = [
+ {
+ 'type': 'table_cell',
+ 'text': text.strip(),
+ 'attrs': {'align': aligns[i], 'head': True}
+ }
+ for i, text in enumerate(headers)
+ ]
+ thead = {'type': 'table_head', 'children': children}
+ return thead, aligns
+
+
+def _process_row(text, aligns):
+ cells = CELL_SPLIT.split(text)
+ if len(cells) != len(aligns):
+ return None
+
+ children = [
+ {
+ 'type': 'table_cell',
+ 'text': text.strip(),
+ 'attrs': {'align': aligns[i], 'head': False}
+ }
+ for i, text in enumerate(cells)
+ ]
+ return {'type': 'table_row', 'children': children}
+
+
+def render_table(renderer, text):
+ return '<table>\n' + text + '</table>\n'
+
+
+def render_table_head(renderer, text):
+ return '<thead>\n<tr>\n' + text + '</tr>\n</thead>\n'
+
+
+def render_table_body(renderer, text):
+ return '<tbody>\n' + text + '</tbody>\n'
+
+
+def render_table_row(renderer, text):
+ return '<tr>\n' + text + '</tr>\n'
+
+
+def render_table_cell(renderer, text, align=None, head=False):
+ if head:
+ tag = 'th'
+ else:
+ tag = 'td'
+
+ html = ' <' + tag
+ if align:
+ html += ' style="text-align:' + align + '"'
+
+ return html + '>' + text + '</' + tag + '>\n'
+
+
+def table(md):
+ """A mistune plugin to support table, spec defined at
+ https://michelf.ca/projects/php-markdown/extra/#table
+
+ Here is an example:
+
+ .. code-block:: text
+
+ First Header | Second Header
+ ------------- | -------------
+ Content Cell | Content Cell
+ Content Cell | Content Cell
+
+ :param md: Markdown instance
+ """
+ md.block.register('table', TABLE_PATTERN, parse_table, before='paragraph')
+ md.block.register('nptable', NP_TABLE_PATTERN, parse_nptable, before='paragraph')
+
+ if md.renderer and md.renderer.NAME == 'html':
+ md.renderer.register('table', render_table)
+ md.renderer.register('table_head', render_table_head)
+ md.renderer.register('table_body', render_table_body)
+ md.renderer.register('table_row', render_table_row)
+ md.renderer.register('table_cell', render_table_cell)
+
+
+def table_in_quote(md):
+ """Enable table plugin in block quotes."""
+ md.block.insert_rule(md.block.block_quote_rules, 'table', before='paragraph')
+ md.block.insert_rule(md.block.block_quote_rules, 'nptable', before='paragraph')
+
+
+def table_in_list(md):
+ """Enable table plugin in list."""
+ md.block.insert_rule(md.block.list_rules, 'table', before='paragraph')
+ md.block.insert_rule(md.block.list_rules, 'nptable', before='paragraph')