diff options
Diffstat (limited to 'src/mistune/plugins/table.py')
-rw-r--r-- | src/mistune/plugins/table.py | 179 |
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') |