Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## python-markdown2 2.5.6 (not yet released)

- [pull #730] Fix `tables` extra splitting multi-backtick code spans at a leading pipe and merging cells after a code span containing literal backticks.
- [pull #729] Fix `tables` extra merging cells when a pipe directly follows a code span, as in compact rows like `|`-v`|verbose|`.
- [pull #725] Fix `tables` extra dropping escaped pipes at the end of header and body rows.
- [pull #687] Fix AssertionError hashing HTML blocks spread over multiple lines (#686)
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ as a script:
The `footnotes` extra keeps the same number when a footnote is referenced more
than once.

The `tables` extra supports code spans delimited by single or multiple backticks,
including an operator cell such as ```` ``|`` ````. A pipe immediately after the
closing delimiter separates the next cell.

There are a number of currently implemented extras for tables, footnotes,
syntax coloring of `<pre>`-blocks, auto-linking patterns, table of contents,
Smarty Pants (for fancy quotes, dashes, etc.) and more. See the [Extras
Expand Down
15 changes: 11 additions & 4 deletions lib/markdown2.py
Original file line number Diff line number Diff line change
Expand Up @@ -4260,12 +4260,19 @@ def _split_row(row: str) -> list[str]:
# An escaped final pipe belongs to the last cell, not the table border.
if not row.endswith(r'\|'):
row = row.removesuffix('|')
code_spans = list(Markdown._code_span_re.finditer(row))
code_starts = {m.start(2) for m in code_spans}
code_ends = {m.end() for m in code_spans}
cells, start = [], 0
for m in re.finditer(r'(?<!\\)\|', row):
# A pipe right after the backtick that opens a code span (`|`) stays in
# the cell; one right after a closing backtick is a cell border.
if m.start() and row[m.start() - 1] == '`' and row.count('`', 0, m.start()) % 2:
continue
pos = m.start()
# A pipe after an opening backtick delimiter stays in the cell.
# A pipe after the matching closing delimiter is a cell border.
if pos and row[pos - 1] == '`':
if pos in code_starts or (
pos not in code_ends and row.count('`', 0, pos) % 2
):
continue
cells.append(row[start:m.start()])
start = m.end()
cells.append(row[start:])
Expand Down
34 changes: 34 additions & 0 deletions test/tm-cases/tables_multibacktick_code_spans.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<table>
<thead>
<tr>
<th>Operator</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>|</code></td>
<td>bitwise or</td>
</tr>
<tr>
<td><code>|</code></td>
<td>bitwise or</td>
</tr>
<tr>
<td><code>|</code></td>
<td>bitwise or</td>
</tr>
<tr>
<td><code>x`y</code></td>
<td>literal backtick</td>
</tr>
<tr>
<td><code>x``y</code></td>
<td>two literal backticks</td>
</tr>
<tr>
<td><code>|</code></td>
<td><code>or</code></td>
</tr>
</tbody>
</table>
1 change: 1 addition & 0 deletions test/tm-cases/tables_multibacktick_code_spans.opts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"extras": ["tables"]}
8 changes: 8 additions & 0 deletions test/tm-cases/tables_multibacktick_code_spans.text
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
| Operator | Meaning |
| --- | --- |
| `|` | bitwise or |
| ``|`` | bitwise or |
| ````|```` | bitwise or |
|``x`y``|literal backtick|
|````x``y````|two literal backticks|
|``|``|`or`|
Loading