Repository navigation
feat(md): preserve inline formatting in Markdown table cells - #4488
Conversation
|
✅ DCO Check Passed Thanks @ceberam, all your commits are properly signed off. 🎉 |
Merge Protections🟢 All 2 merge protections satisfied — ready to merge. Show 2 satisfied protections🟢 Enforce conventional commitMake sure that we follow https://www.conventionalcommits.org/en/v1.0.0/
🟢 Require two reviewer for test updatesWhen test data is updated, we require two reviewers
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
It is indeed related to #4479 and also #4454 and other issues in the past around Markdown tables.
|
cd3f855 to
f9b09dd
Compare
DanielNg0729
left a comment
There was a problem hiding this comment.
hi Dr @ceberam, thank you very much for this PR. Moving the tables to marko's GFM AST is a big improvement. a <br> in a cell no longer splits the table into pieces, a table inside a list item stays in place, and links, code and images in cells are kept.
I tried the branch locally and have a few questions, please see the comments here. I may well be missing context, so please feel free to disregard any of them.
Thank you very much again!
Enable the marko GFM extension so tables are parsed as structured AST nodes (Table → TableRow → TableCell) with inline children intact. Cells that contain bold, italic, code spans, links, autolinks, images, or strikethrough are emitted as RichTableCell objects backed by an InlineGroup; plain-text cells remain TableCell. New helpers: _apply_formatting, _resolve_link_dest (shared between the paragraph walker and the new cell walker), _cell_plain_text, _cell_has_rich_content, _parse_gfm_table, _iterate_cell_inline. Whitespace handling in cell InlineGroups follows the same strip-and-let- the-serializer-join strategy used for paragraph/list InlineGroups. Signed-off-by: Cesar Berrospi Ramis <ceb@zurich.ibm.com>
Signed-off-by: Cesar Berrospi Ramis <ceb@zurich.ibm.com>
Signed-off-by: Cesar Berrospi Ramis <ceb@zurich.ibm.com>
Signed-off-by: Cesar Berrospi Ramis <ceb@zurich.ibm.com>
Signed-off-by: Cesar Berrospi Ramis <ceb@zurich.ibm.com> Signed-off-by: Cesar Berrospi Ramis <ceb@zurich.ibm.com>
Signed-off-by: Cesar Berrospi Ramis <ceb@zurich.ibm.com>
Signed-off-by: Cesar Berrospi Ramis <ceb@zurich.ibm.com>
Signed-off-by: Cesar Berrospi Ramis <ceb@zurich.ibm.com>
Signed-off-by: Cesar Berrospi Ramis <ceb@zurich.ibm.com>
…removals Signed-off-by: Cesar Berrospi Ramis <ceb@zurich.ibm.com>
f9b09dd to
185f00d
Compare
|
Thanks @DanielNg0729 for you detailed review and good suggestions. I have added some commits to address them. Please, feel free to have a second look. |
DanielNg0729
left a comment
There was a problem hiding this comment.
Thank you Dr @ceberam, looks good to me now!
GFM requires a delimiter row right after a table header. Without one, lines that start with a pipe are paragraph text. docling-project#4488 moved table parsing to marko's GFM extension, which fixes this; this test guards it. It fails on 1a264db~1 (the text is parsed as a table) and passes on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: breken-ai <312387581+breken-ai@users.noreply.github.com>
Closes #3991
Supersedes #4454
Summary
Previously, the
MarkdownDocumentBackendparsed table cells as plain text using a custom text-buffer and regex splitting approach, discarding all inline markup. A cell containing**bold**or[link](url)was stored as aTableCellwith the textboldorlink, with no record of the formatting or hyperlink.This PR upgrades Markdown table parsing to be strictly AST-driven using Marko's GitHub Flavored Markdown (
gfm) extension, preserving inline markup by emittingRichTableCellobjects for formatted cells.Changes
GFM Table AST & RichTableCell Support
gfmextension (Markdown(extensions=["gfm"])), generating structured AST nodes (Table → TableRow → TableCell) with inline children per cell._split_table_row,_starts_pipeless_table,_close_table, etc.) with_parse_gfm_table.RawText/Literalchildren only) continue to produce aTableCell, keeping the common case lightweight with zero overhead.RichTableCellbacked by anInlineGroupparented under the table._iterate_cell_inline): Traverses cell inline AST nodes and generatesTextItem,CodeItem, orPictureItemchildren. Preserves bold, italic, strikethrough, inline code spans, hyperlinks (including GFM bare-URL autolinks), and embedded images. Only content permitted inside table cells per the GFM specification is processed.Shared Formatting & Link Helpers
Extracted reusable static helpers shared between block-level prose and table-cell inline element walkers:
_apply_formatting(current, *, bold, italic, strikethrough): Returns an updatedFormattinginstance without mutating parent references in place._resolve_link_dest(dest): Parses and validates link destination strings intoAnyUrlorPathobjects viaTypeAdapter.Modernized Typing & Codebase Consistency
md_backend.pyto Python 3.10+ union syntax (X | None,X | Y), eliminatingUnionandOptionalimports._CreationPayloaddiscriminated union to use_HeadingCreationPayload | _ListItemCreationPayloadwhile preservingAnnotated[..., Field(discriminator="kind")].in_table,in_pipeless_table,md_table_buffer) and unneeded inline comments.Tests
tests/test_backend_markdown.py:test_rich_table_cell_bold_and_italic: Verifies bold/italic in headers and data cells, mixed formatting, and plain cell fallback.test_rich_table_cell_inline_code: Verifies code spans becomeCodeIteminstances with literal unescaped text.test_rich_table_cell_hyperlink: Verifies inline links attach hyperlinks to cell items.test_rich_table_cell_autolinks: Verifies angle-bracket<https://...>and GFM bare URLs.test_rich_table_cell_no_stray_body_items: Verifies rich cell groups are parented under the table and do not leak intodoc.body.test_convert_table_keeps_inline_code_spans.inline_and_formatting.md(.yamland.md).Checklist: