Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
Table Cell and Row Span
=======================

NOTE: This is a fork of an excellent idea implemented by @Neepawa, however
the upstream seems to be abandoned for a while, so I collected different
patches across all forks and also updated the extension to work with the
latest version of [Python-Markdown][markdown]. I plan to maintain this
version (and push changes to upstream) as long as I am running my personal
blog on [Pelican][pelican], which heavily relies on Python-Markdown.

Comment on lines +4 to +10
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block should be deleted if the change is to be accepted since it is a part of my local fork's README.md.

Summary
-------

Adds spanning for rows and cells in tables.
A Python-Markdown extension to add the `colspan` and `rowspan` support for rows
and cells in tables.

Syntax
------
Expand Down Expand Up @@ -118,7 +126,7 @@ different, change `~~` to another value in the following line in the code:
RE_empty_cell = re.compile(r'\s*(~~)?\s*$')

Keep in mind that many characters have special meaning in regular expressions.
If you use any of the following characters in the expression, preceed them with
If you use any of the following characters in the expression, precede them with
a backslash ("\\") to avoid problems:

~~~ text
Expand All @@ -143,3 +151,5 @@ and Python 3.
License: [BSD](http://www.opensource.org/licenses/bsd-license.php)

[extensions]: https://python-markdown.github.io/extensions/
[markdown]: https://github.com/Python-Markdown/markdown
[pelican]: https://github.com/getpelican/pelican
52 changes: 29 additions & 23 deletions cell_row_span.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@
from markdown.extensions import Extension
from markdown.blockprocessors import BlockProcessor
from markdown.treeprocessors import Treeprocessor
from markdown.util import etree
import re


Expand All @@ -135,13 +134,13 @@ class CellRowSpanExtension(Extension):
# the extension itself.
table_blocks = []

def extendMarkdown(self, md, md_globals):
def extendMarkdown(self, md):
""" Add our block and tree processors """
if 'table' in md.parser.blockprocessors:
md.parser.blockprocessors.add('cell_row_span',
CellRowSpanBlockProcessor(self, md.parser), '<table')
md.treeprocessors.add('cell_row_span',
CellRowSpanTreeProcessor(self), '<inline')
md.parser.blockprocessors.register(
CellRowSpanBlockProcessor(self, md.parser), 'cell_row_span', 76)
md.treeprocessors.register(
CellRowSpanTreeProcessor(self), 'cell_row_span', 26)


class CellRowSpanBlockProcessor(BlockProcessor):
Expand All @@ -167,7 +166,8 @@ class CellRowSpanTreeProcessor(Treeprocessor):
""" Add cell and row spans to table as needed """

RE_adjacent_bars = re.compile(r'\|(~~)?\|')
RE_remove_lead_pipe = re.compile(r'^ *\|') # ... Colonel Mustard in the Library? ;)
# ... Colonel Mustard in the Library? ;)
RE_remove_lead_pipe = re.compile(r'^ *\|')
RE_row_span_marker = re.compile(r'^_[_^= ]*_$')
RE_valign_top = re.compile(r'\^')
RE_valign_bottom = re.compile(r'=')
Expand All @@ -179,23 +179,26 @@ def __init__(self, extension_obj):

def _update_colspan_attrib(self, text, t_index, tr_index, tr, td_remove):
""" Update 'colspan' attributes in 'td' entries """
text = self.RE_remove_lead_pipe.sub('', text) # Remove leading '|' from text
# Remove leading '|' from text
text = self.RE_remove_lead_pipe.sub('', text)
td_index = 0
td_last_active_index = 0

for c in text.split('|'):
if len(c) == 0 or c == '~~':
try:
td = tr[td_last_active_index] # Update 'colspan' on previous cell
# Update 'colspan' on previous cell
td = tr[td_last_active_index]
except IndexError:
row_content = ''
for i in range(len(tr)):
x = tr[i].text
row_content += " Cell %i: %s\n" % (i+1, x if x else 'Empty')
row_content += " Cell %i: %s\n" % (
i+1, x if x else 'Empty')
raise IndexError(
'Cannot merge cell beyond end of row '
"(one too many '|' characters in row?)\n"
'Check row %i of table %i in your document. Row contents:\n%s' % (
'Cannot merge cell beyond end of the row '
"(one too many '|' characters in the row?)\n"
"Check row %i of table %i in your document. Row's contents:\n%s" % (
tr_index+1, t_index, row_content
)
)
Expand All @@ -204,7 +207,7 @@ def _update_colspan_attrib(self, text, t_index, tr_index, tr, td_remove):
if 'colspan' in td.keys():
span = int(td.get('colspan'))
td.set('colspan', str(span+1))
td_remove.append( (tr, tr[td_index]) )
td_remove.append((tr, tr[td_index]))
else:
td_last_active_index = td_index
td_index += 1
Expand All @@ -223,26 +226,25 @@ def _update_rowspan_attrib(self, tbody, tr_index, td_index, td_remove):
'marker\nCheck row %i, column %i in table %i in your '
'document' % (tr_index+1, td_index+1, self.table_count)
)
v_align='bottom'
v_align = 'bottom'

# Starting from the current row, go up the rows and delete columns
# until we hit a non-empty column (or the start of the table)
for row_num in reversed(range(tr_index+1)):
td = tbody[row_num][td_index]
if row_num == tr_index or self.RE_empty_cell.match(td.text):
td_remove.append( (tbody[row_num], td) )
td_remove.append((tbody[row_num], td))
else:
break

# Update the colspan and valign attributes on the row
td = tbody[row_num][td_index] if row_num >=0 else tbody[0][td_index]
td = tbody[row_num][td_index] if row_num >= 0 else tbody[0][td_index]
td.set('rowspan', str(tr_index-row_num+1))


def run(self, root):
# Process all the tables in the ElementTree
t_index = 0
for table in root.findall('.//table'):
for table in root.iter('table'):
# Retrieve the block saved by the BlockProcessor
rows = self.table_blocks[t_index].split('\n')
t_index += 1
Expand All @@ -253,13 +255,16 @@ def run(self, root):
tr_index = 0
for tr in tbody:
# Check for adjacent columns
if tr_index + 2 in rows and self.RE_adjacent_bars.search(rows[tr_index+2]):
self._update_colspan_attrib(rows[tr_index+2], t_index, tr_index, tr, td_remove)
if (tr_index + 2 < len(rows) and
self.RE_adjacent_bars.search(rows[tr_index+2])):
self._update_colspan_attrib(
rows[tr_index+2], t_index, tr_index, tr, td_remove)
# Check for spanned rows
td_index = 0
for td in tr:
if self.RE_row_span_marker.match(td.text):
self._update_rowspan_attrib(tbody, tr_index, td_index, td_remove)
if td.text and self.RE_row_span_marker.match(td.text):
self._update_rowspan_attrib(
tbody, tr_index, td_index, td_remove)
td_index += 1
tr_index += 1

Expand All @@ -268,5 +273,6 @@ def run(self, root):
if td in tr:
tr.remove(td)


def makeExtension(*args, **kwargs):
return CellRowSpanExtension(*args, **kwargs)
10 changes: 6 additions & 4 deletions setup.py
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Obviously, this file is not applicable to be merged upstream since it points to a fork, but given no activity in @Neepawa's repository I did not want to spend time to split out the changes acceptable vs what is my fork only stuff.

Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from distutils.core import setup
from setuptools import setup

setup(name='cell_row_span',
version='1.0',
description='Markdown extension to add spanning for rows and cells in tables.',
url='https://github.com/Neepawa/cell_row_span',
version='1.0.1',
description='Markdown extension to add spanning for rows and cells '
'in tables.',
url='https://github.com/galaxy4public/cell_row_span',
py_modules=['cell_row_span'],
require=['markdown'],
)