Add ListTableExtension (list-as-table for block-content cells)#252
Open
dereuromark wants to merge 3 commits into
Open
Add ListTableExtension (list-as-table for block-content cells)#252dereuromark wants to merge 3 commits into
dereuromark wants to merge 3 commits into
Conversation
Renders ::: list-table divs as real HTML tables, with the table authored as a nested list so cells can hold full block content (paragraphs, lists, code) that the native pipe-table syntax cannot express. Outer list items are rows, inner list items are cells. The caption, header-rows, and header-cols are read from the div's preceding attribute line. Single-paragraph cells collapse to inline content; multi-block cells keep their wrappers. Ragged rows pad with empty cells. Only list-table divs whose sole block child is the table list are claimed; everything else (and any malformed row) defers to the default div so no content is dropped. Builds on the marker-line nested-list block-absorption fix (PR #251).
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #252 +/- ##
============================================
+ Coverage 92.34% 92.43% +0.09%
- Complexity 3593 3680 +87
============================================
Files 107 109 +2
Lines 10165 10393 +228
============================================
+ Hits 9387 9607 +220
- Misses 778 786 +8 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
A list-table cell whose sole inline content is a lone caret merges into the cell above (rowspan) and a lone less-than merges into the cell to the left (colspan), reusing djot-php's native pipe-table span markers and the same continuation semantics: colspan of three is two trailing markers, rowspan of N is N-1 markers in the rows below the origin. The marker detection runs after the inline parse, so an escaped marker or an attributed cell keeps its literal content and is never a span marker. The grid is resolved into effective columns that account for colspans and for rowspans reserved by earlier rows, padding ragged rows with empty cells; well-formed spans produce the same table markup the equivalent pipe table emits. No span markers leaves the previous behavior unchanged. Span resolution lives in a small SpanDescriptors value object so the rowspan/colspan mutations stay on one typed list. Docs: document the marker syntax, the Sales authoring example with its rendered HTML, and the note that it reuses the pipe-table span markers.
…wspan
Three edge-case defects in the list-table extension:
- An attributed cell (e.g. -{.x} ^) was still treated as a span marker
because the marker check only looked at the paragraph's attributes, not
the cell list item's. The class and the literal ^ were dropped and the
neighbor wrongly gained a rowspan. spanMarker() now returns null when the
cell carries its own attributes, and the cell attributes are emitted onto
the <td>/<th> with the same safe-mode filtering the core renderer applies.
- A malformed list-table that defers to the default div renderer could
duplicate user content: extractCells() appended a stray trailing block to
the previous cell before the defer decision was made, leaving the mutation
on the AST. Cells and pending appends are now collected without mutating;
the appends are applied only once every row validates and the div is
claimed, so a deferred render is byte-identical to the plain div.
- A rowspan authored across the header/body boundary emitted a cell inside
<thead> with a rowspan reaching into <tbody>, which browsers misrender.
The rowspan is now clamped at the boundary: a ^ in the first body row whose
origin lives in the header rows degrades to a fresh empty body cell, and
the header cell keeps its rowspan within the header rows. Rowspans entirely
within the header or entirely within the body are unaffected.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What it does
Adds a
ListTableExtensionthat renders::: list-tabledivs as real HTML<table>markup. The table is authored as a nested list: each outer list item is a row, each inner list item is a cell.The point is that cells are list items, so they can hold full block content (paragraphs, lists, code blocks) that the native pipe-table syntax cannot express.
Behavior:
header-rows=Npromotes the first N rows to<thead>with<th>cells.header-cols=Npromotes the first N cells of every row to row-header<th>.<td>text</td>); a multi-block cell keeps its<p>/<ul>wrappers.<td>to the widest row.list-tabledivs whose sole block child is the table list are claimed; any other div, a div without a usable list, or a row authored without an inner cell list defers to the default div renderer, so no content is ever silently dropped. Without the extension registered the block degrades to the literal<div class="list-table">nested list.Builds on the block-absorption fix
This relies on the marker-line nested-list block-absorption fix in #251, which lets a cell hold full block content (otherwise the nested list would not absorb the cell's block children).
Caption via attribute
djot has no
::: type "title"parse, so a quoted title on the opener would land in the class name. Instead the caption is read from acaption="..."attribute on the preceding attribute line, alongsideheader-rows/header-cols:Note jgm/djot#27 is still debating an official list-table syntax. An alternative that aligns with some of that discussion is putting the caption as content directly below the opener (a leading paragraph/heading) rather than an attribute; this PR uses the attribute form because it keeps the body purely the cell grid and round-trips cleanly. Open to switching if the upstream discussion settles on the content-below-caption shape.
Spanning rows and columns
Cells span via djot-php's existing pipe-table span markers, so the same syntax works in both table flavors: a cell that is a lone
^merges with the cell above (rowspan); a lone<merges with the cell to the left (colspan). Continuation-style -colspan=3is two<cells;rowspan=Nis N-1^cells in the following rows. An attributed or escaped marker (\^) stays literal.renders EMEA with
rowspan="2"and Total withcolspan="3", matching the equivalent pipe table's span markup (asserted by a parity test).Tests and docs
24 list-table tests cover block-content cells, caption, header rows/cols, single-paragraph collapse, ragged padding, the defer-on-no-cells guard, spans (rowspan, colspan, combined, escape), and parity with the equivalent pipe table. Full suite green; PHPStan and phpcs clean. Documented in
docs/extensions/index.md, including a "Spanning rows and columns" subsection.