From eeb1fc01b6b96bb4c3b0ea8c211e58bb7da91b7f Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Sun, 23 Dec 2018 18:34:46 +0100 Subject: [PATCH 1/4] Remove whitespaces on empty lines --- mdx_grid_tables.py | 80 +++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/mdx_grid_tables.py b/mdx_grid_tables.py index 011bd10..3f85446 100644 --- a/mdx_grid_tables.py +++ b/mdx_grid_tables.py @@ -81,14 +81,14 @@ def __init__(self, start_row, start_col, width=1, height=1, colspan=1, self._height = max(1, height) self._colspan = max(1, colspan) self._rowspan = max(1, rowspan) - + def __str__(self): """ For simplicity, the string representation is also the python code representation. """ return self.__repr__() - + def __repr__(self): """ This is the python representation of the cell. If ran with eval, the @@ -101,7 +101,7 @@ def __repr__(self): repr(self._width), repr(self._height), repr(self._colspan), repr(self._rowspan), repr(self.text)) - + def __eq__(self, other): """ Checks if another cell is equivalent to this one. @@ -112,14 +112,14 @@ def __eq__(self, other): self.height == other.height and self.colspan == other.colspan and self.rowspan == other.rowspan) - + @property def start_row(self): """ Returns the starting row for the cell. """ return self._start_row - + @start_row.setter def start_row(self, value): """ @@ -127,14 +127,14 @@ def start_row(self, value): depending on which is larger. """ self._start_row = max(0, value) - + @property def start_col(self): """ Returns the starting column for the cell. """ return self._start_col - + @start_col.setter def start_col(self, value): """ @@ -142,14 +142,14 @@ def start_col(self, value): in, depending on which is larger. """ self._start_col = max(0, value) - + @property def width(self): """ Returns the width (in number of characters) of the cell. """ return self._width - + @width.setter def width(self, value): """ @@ -157,14 +157,14 @@ def width(self, value): value passed in, depending on which is larger. """ self._width = max(1, value) - + @property def height(self): """ Returns the height (in number of characters) of the cell. """ return self._height - + @height.setter def height(self, value): """ @@ -172,14 +172,14 @@ def height(self, value): the value passed in, depending on which is larger. """ self._height = max(1, value) - + @property def colspan(self): """ Returns the number of columns that this cell spans. """ return self._colspan - + @colspan.setter def colspan(self, value): """ @@ -187,14 +187,14 @@ def colspan(self, value): value passed in, depending on which is larger. """ self._colspan = max(1, value) - + @property def rowspan(self): """ Returns the number of rows that this cell spans. """ return self._rowspan - + @rowspan.setter def rowspan(self, value): """ @@ -202,7 +202,7 @@ def rowspan(self, value): passed in, depending on which is larger. """ self._rowspan = max(1, value) - + @property def end_row(self): """ @@ -211,7 +211,7 @@ def end_row(self): cell. """ return self._start_row + self._height - + @property def end_col(self): """ @@ -232,7 +232,7 @@ def __init__(self, start_row, is_header=False): self._start_row = start_row self._height = None self.is_header = is_header - + def add_cell(self, cell): """ Adds a cell to the appropriate position in the row based on where its @@ -253,7 +253,7 @@ def add_cell(self, cell): if self._height is None or relative_height < self._height: self._height = relative_height return True - + def get_all_cells(self): """ A generator which returns all cells within the row. I use a generator @@ -262,7 +262,7 @@ def get_all_cells(self): """ for cell in self._cells: yield cell - + def get_all_cells_taller_than_this_row(self): """ A generator that gets all cells that are taller than this row (which @@ -271,7 +271,7 @@ def get_all_cells_taller_than_this_row(self): for cell in self._cells: if cell.start_row + cell.height > self._start_row + self._height: yield cell - + def get_all_cells_starting_at_this_row(self): """ A generator that gets all cells that start at this row (which means @@ -280,7 +280,7 @@ def get_all_cells_starting_at_this_row(self): for cell in self._cells: if cell.start_row == self._start_row: yield cell - + def get_cell_starting_at_this_row_at_column(self, column): """ Returns the cell (or None if no cell is found) that starts in this row, @@ -292,7 +292,7 @@ def get_cell_starting_at_this_row_at_column(self, column): elif cell.start_col > column: break return None - + @property def height(self): """ @@ -300,14 +300,14 @@ def height(self): equal to the height of the shortest cell in this row. """ return self._height - + @property def start_row(self): """ The index of the line in the block at which this row starts. """ return self._start_row - + @property def end_row(self): """ @@ -357,7 +357,7 @@ def __init__(self, start_row, start_col, height, width, first_row_header=False): self._start_col = start_col self._width = width self._height = height - + def new_row(self, is_header=False, header_location=-1): """ Creates a new row which starts at the end of the previous row. Any @@ -375,14 +375,14 @@ def add_cell(self, cell): Adds a cell to the last row in the table. """ return self._rows[-1].add_cell(cell) - + def get_all_rows(self): """ A generator that returns all rows in the table. """ for row in self._rows: yield row - + def get_all_cells_starting_at_column(self, column): """ A generator which yields all cells in all rows that start at a specific @@ -392,7 +392,7 @@ def get_all_cells_starting_at_column(self, column): cell = row.get_cell_starting_at_this_row_at_column(column) if cell is not None: yield cell - + def calculate_colspans(self): """ After all cells are added to the table, this function will calculate @@ -419,7 +419,7 @@ def calculate_colspans(self): del cells[i] start_col = end_col end_col = self.end_col - + @property def start_row(self): """ @@ -427,7 +427,7 @@ def start_row(self): starts at. """ return self._start_row - + @property def start_col(self): """ @@ -435,21 +435,21 @@ def start_col(self): table starts at. """ return self._start_col - + @property def width(self): """ Returns the width (in number of characters) of the table. """ return self._width - + @property def height(self): """ Returns the height (in number of characters) of the table. """ return self._height - + @property def end_row(self): """ @@ -457,7 +457,7 @@ def end_row(self): ends at. It is equal to the starting row plus the height. """ return self._start_row + self._height - + @property def end_col(self): """ @@ -610,7 +610,7 @@ def _scan_cell(self, block, start_row, start_col): if block[start_row][start_col] != '+': return None return self._scan_right(block, start_row, start_col) - + def _scan_right(self, block, start_row, start_col): """ Scans right until it gets to a '+' sign. It then starts scanning down @@ -631,7 +631,7 @@ def _scan_right(self, block, start_row, start_col): else: break return None - + def _scan_down(self, block, start_row, start_col, cur_col): """ Scans down until it gets to a '+' sign. It then starts scanning left @@ -653,7 +653,7 @@ def _scan_down(self, block, start_row, start_col, cur_col): else: break return None - + def _scan_left(self, block, start_row, start_col, cur_col, cur_row): """ Scans left until it gets to a '+' sign. It then starts scanning up to @@ -675,7 +675,7 @@ def _scan_left(self, block, start_row, start_col, cur_col, cur_row): else: break return None - + def _scan_up(self, block, start_row, start_col, cur_col, cur_row, check_col): """ Scans up until it gets to a '+' sign. If the '+' sign is in the @@ -702,7 +702,7 @@ def _scan_up(self, block, start_row, start_col, cur_col, cur_row, check_col): else: break return None - + def _gather_text(self, block, start_row, start_col, end_row, end_col): """ Gathers the text within the cell defined by the start row, start From b39d70ab017d85c5f0a213179d33790955bdc1d3 Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Sun, 23 Dec 2018 18:37:33 +0100 Subject: [PATCH 2/4] Fix tables with cells spanning rows to the right of single cells --- mdx_grid_tables.py | 49 ++++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/mdx_grid_tables.py b/mdx_grid_tables.py index 3f85446..1cdc9a4 100644 --- a/mdx_grid_tables.py +++ b/mdx_grid_tables.py @@ -315,26 +315,7 @@ def end_row(self): equal to the starting row plus the height. """ return self._start_row + self._height - - @property - def start_col(self): - """ - The column in the block at which this row starts. If a cell starts at - this row, that cell's start column is returned. Otherwise, the furthest - right connected cell's (starting from the left) end column is returned. - """ - if len(self._cells) == 0: - return 0 - left_cell = None - for cell in self._cells: - if cell.start_row == self.start_row: - return cell.start_col - if left_cell is None or left_cell.end_col == cell.start_col: - left_cell = cell - else: - break - return left_cell.end_col - + @property def end_col(self): """ @@ -345,9 +326,14 @@ def end_col(self): return 0 return self._cells[-1].end_col + def get_cell_with_start_col(self, start_col): + for cell in self._cells: + if cell.start_col == start_col: + return cell + class GridTable(object): """ - A grid table in its entirity. The start row and start column should be 0, 0 + A grid table in its entirety. The start row and start column should be 0, 0 but can be set differently depending on the block. The width and height are how many characters wide and high the table is. """ @@ -368,8 +354,8 @@ def new_row(self, is_header=False, header_location=-1): for cell in self._rows[-2].get_all_cells_taller_than_this_row(): cell.rowspan += 1 self._rows[-1].add_cell(cell) - return self._rows[-1].start_row, self._rows[-1].start_col - + return self._rows[-1].start_row + def add_cell(self, cell): """ Adds a cell to the last row in the table. @@ -589,17 +575,20 @@ def _get_all_cells(self, block): header_exists, header_location, block = self._header_exists(block) table = GridTable(start_row, start_col, len(block)-1, len(block[0])-1, header_exists) while start_row < len(block)-1: - new_cell = self._scan_cell(block, start_row, start_col) - if new_cell is None or not table.add_cell(new_cell): - return False, table - if start_col + new_cell.width >= len(block[start_row])-1: + cell = table._rows[-1].get_cell_with_start_col(start_col) + if cell is None: + cell = self._scan_cell(block, start_row, start_col) + if cell is None or not table.add_cell(cell): + return False, table + if start_col + cell.width >= len(block[start_row])-1: is_header = header_exists and table._rows[-1].end_row < header_location - start_row, start_col = table.new_row(is_header=is_header) + start_row = table.new_row(is_header=is_header) + start_col = 0 else: - start_col += new_cell.width + start_col += cell.width table.calculate_colspans() return True, table - + def _scan_cell(self, block, start_row, start_col): """ Starts scanning for a specific cell by checking the starting character From 1f484db1b265809c5705718f8417a5ba72611be0 Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Sun, 23 Dec 2018 18:40:55 +0100 Subject: [PATCH 3/4] Better error handling in run method --- mdx_grid_tables.py | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/mdx_grid_tables.py b/mdx_grid_tables.py index 1cdc9a4..9cce535 100644 --- a/mdx_grid_tables.py +++ b/mdx_grid_tables.py @@ -463,6 +463,10 @@ class GridTableProcessor(markdown.blockprocessors.BlockProcessor): """ _header_regex = r'\+=+(\+=+)*\+' + def __init__(self, parser): + super(GridTableProcessor, self).__init__(parser) + self.ignore_next_block = False + def test(self, parent, block): """ This function tests to see if the block of text passed in is a table or @@ -471,6 +475,9 @@ def test(self, parent, block): on both the top an bottom right corners, and has a '|' at the beginning and end of the first and last rows. """ + if self.ignore_next_block: + self.ignore_next_block = False + return False rows = [r.strip() for r in block.split('\n')] return (len(rows) > 2 and rows[0][:2] == "+-" and rows[0][-2:] == "-+" and rows[1][0] == '|' and rows[1][-1] == '|' @@ -487,28 +494,15 @@ def run(self, parent, blocks): Otherwise, it is rendered as a table with the appropriate row and column spans. """ - orig_block = [r.strip() for r in blocks.pop(0).split('\n')] - body_block = orig_block[:] + body_block = [r.strip() for r in blocks[0].split('\n')] success, body = self._get_all_cells(body_block) if not success: - self._render_as_block(parent, '\n'.join(orig_block)) + self.ignore_next_block = True return + del blocks[0] table = etree.SubElement(parent, 'table') self._render_rows(body, table) - def _render_as_block(self, parent, text): - """ - Renders a table as a block of text instead of a table. This isn't done - correctly, since the serialized items are serialized again, but I'll - fix this later. - """ - trans_table = [(' ', ' '), ('<', '<'), ('>', '>'), ('&', '&')] - for from_char, to_char in trans_table: - text = text.replace(from_char, to_char) - div = etree.SubElement(parent, 'div') - div.set('class', 'grid-table-error') - div.text = text - def _header_exists(self, block): """ Checks if a header exists. A header is defined by a row of '=' From d2990587ab82b2efbe34694ab56d97770eb84c11 Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Sun, 23 Dec 2018 18:41:36 +0100 Subject: [PATCH 4/4] Fix exception with different line lengths --- mdx_grid_tables.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mdx_grid_tables.py b/mdx_grid_tables.py index 9cce535..a7617ff 100644 --- a/mdx_grid_tables.py +++ b/mdx_grid_tables.py @@ -479,6 +479,8 @@ def test(self, parent, block): self.ignore_next_block = False return False rows = [r.strip() for r in block.split('\n')] + if len(set([len(r) for r in rows])) != 1: # all rows need the same length + return False return (len(rows) > 2 and rows[0][:2] == "+-" and rows[0][-2:] == "-+" and rows[1][0] == '|' and rows[1][-1] == '|' and rows[-2][0] == '|' and rows[-2][-1] == '|'