brian_jaress ([info]brian_jaress) wrote,
@ 2007-09-27 16:03:00
Previous Entry  Add to memories!  Tell a Friend  Next Entry
Current mood: satisfied
Entry tags:markdown

Markdown-Python Wrapped Tables Extension

I've been using markdown syntax for a while, and I (very) recently switched from the reference interpreter to markdown-python which has some nice extensions1.

One extension I wanted to use was for tables, but when I tried it I found out that it won't let you put multiple lines in a table cell. (I mean multiple lines in the markdown. I assume you can force multiple lines in the HTML by using <br/>.)

So, I wrote my own extension called "wtables."2

Please do not hassle me for writing my own instead of modifying the existing one. The basic approach they were taking was incompatible with the feature I wanted to add.

Markdown-python has a wiki, so I should probably add it there eventually. For now, I'm sticking it here. This is very untested code. I've used it once on real data and that's it. Please let me know if it gives you any trouble.

One thing it won't do is turn blank lines into paragraph breaks3, so If you want a cell to have multiple paragraphs, you have to write the <p> tags yourself. But at least you can put them on their own lines.

Contents of the file mdx_wtables.py:

"""
Wrapped Tables extension for Markdown Python

2007-09-27

brian_jaress@gna.org

"Wrapped" means that you can wrap lines inside a cell.
It's inspired by another extension I found at

https://libprs500.kovidgoyal.net/browser/trunk/src/libprs500/ebooks/markdown/mdx_tables.py?format=txt

but that extension didn't have wrapping and is implemented in a very
different way.

An example of the syntax is:

|*heading*|*heading*|cell|
|cell a|cell b| cell c
    still cell c
    blah blah blah
    still cell c|
|third row|cell|cell|

That gives you a three by three table with two headings (top left and
top center) and one cell with a lot of text (mid right).

"""

from os import linesep
from csv import reader, QUOTE_NONE
from markdown import Preprocessor, Extension

class TableExtension(Extension):
    def __init__(self, configs):
        self.configs = {
                'delim': '|',
                'wrap': 4 * ' ',
                'header': '*'
                }
        self.configs.update(configs)

    def extendMarkdown(self, md, md_globals):
        md.preprocessors.append(TablePre(**self.configs))

class TablePre(Preprocessor):
    def __init__(self, delim, wrap, header):
        self.delim = delim
        self.wrap = wrap
        self.header = header

    def run(self, lines):
        table_lines = []
        #Group contiguous table lines and convert each group to a table
        for l in lines:
            if l.startswith(self.delim) or (len(table_lines) > 0 and
                    l.startswith(self.wrap)):
                table_lines.append(l)
            else:
                if len(table_lines) > 0:
                    for new_line in self.table(table_lines):
                        yield new_line
                    table_lines = []
                yield l

    def clean(self, cells):
        """Remove the empty cells at the beginning and end."""
        return cells[1:-1]

    def table(self, lines):
        yield "<table>"
        for r in self.parse(lines):
            yield self.row(r)
        yield "</table>"

    def parse(self, lines):
        """Generate table rows as lists of cell strings."""
        read = reader(lines,
                delimiter=self.delim,
                quoting=QUOTE_NONE,
                escapechar='\\',
                lineterminator=linesep
                )

        accumulated = []
        for line in read:
            if not line[0].startswith(self.wrap):
                #Start new row
                if len(accumulated) > 0:
                    yield self.clean(accumulated)
                accumulated = line
            else:
                #Continue existing cell
                accumulated[-1] = accumulated[-1] + '\n' + line[0].lstrip()
                accumulated.extend(line[1:])
        yield self.clean(accumulated)

    def row(self, data):
        return self.tag(''.join(map(self.cell, data)), "tr")

    def cell(self, data):
        if (data.startswith(self.header) and data.endswith(self.header) and
                len(data) > 1):
            data = data[len(self.header):-len(self.header)]
            tag = "th"
        else:
            tag = "td"
        return self.tag(data, tag)

    def tag(self, data, tag):
        return "<%s>%s</%s>" % (tag, data, tag)

def makeExtension(configs={}) :
    return TableExtension(configs)

UPDATE: fixed wrong word.




Create an Account
Forgot your login or password?
Login w/ OpenID
English • Español • Deutsch • Русский…