mirror of
https://github.com/lexmount/moli.git
synced 2026-10-05 00:00:55 +00:00
fix(markdown): expand presentational tables into blocks
Honor presentation and none table roles with ASCII case-insensitive matching and HTML whitespace trimming. Preserve captions, cell content, and independent conversion of nested data tables. Validation: cargo fmt --all; cargo clippy --workspace --all-targets --all-features -- -D warnings; cargo nextest run --no-fail-fast (18,162 passed, 13 skipped).
This commit is contained in:
@@ -64,13 +64,15 @@ and cell line breaks and paragraph boundaries use inline `<br>` tags. Attribute
|
||||
newlines are encoded or normalized so they cannot break a table row. GFM supplies
|
||||
missing trailing cells in short rows; the converter does not allocate a padded grid.
|
||||
|
||||
Complex tables retain Turndown core's ordinary block expansion, in DOM order with
|
||||
blank lines between cells. This includes non-unit spans, multiple `thead` rows,
|
||||
rows wider than an explicit header, or cells containing nested tables, lists,
|
||||
headings, quotes or preformatted blocks. Inner simple tables can still produce
|
||||
GFM when their containing layout table is expanded. Roles, borders and CSS do
|
||||
not select another conversion path, so structurally simple layout tables also
|
||||
produce GFM. Complex layouts remain readable blocks instead of raw HTML tables.
|
||||
Tables with `role="presentation"` or `role="none"` retain ordinary block expansion,
|
||||
in DOM order with blank lines between cells, even when structurally simple. Role
|
||||
matching ignores ASCII case and surrounding HTML whitespace. The same fallback
|
||||
applies to complex tables: non-unit spans, multiple `thead` rows, rows wider than
|
||||
an explicit header, or cells containing nested tables, lists, headings, quotes
|
||||
or preformatted blocks. Inner simple tables can still produce GFM when their
|
||||
containing layout table is expanded. Other roles, borders and CSS do not select
|
||||
another conversion path. Complex layouts remain readable blocks instead of raw
|
||||
HTML tables.
|
||||
|
||||
Supported output includes headings, paragraphs, emphasis, strikethrough, links,
|
||||
images, lists, blockquotes, hard breaks, and fenced code. Inline HTML is used when
|
||||
|
||||
@@ -26,6 +26,12 @@ impl<Id: Copy> Table<Id> {
|
||||
depth: usize,
|
||||
max_depth: usize,
|
||||
) -> Option<Self> {
|
||||
if dom.attribute(node, "role").is_some_and(|role| {
|
||||
let role = role.trim_matches(is_space);
|
||||
role.eq_ignore_ascii_case("presentation") || role.eq_ignore_ascii_case("none")
|
||||
}) {
|
||||
return None;
|
||||
}
|
||||
let mut table = Self {
|
||||
captions: Vec::new(),
|
||||
cells: Vec::new().into_iter(),
|
||||
|
||||
@@ -55,14 +55,55 @@ fn wrapping_a_table_preserves_its_content() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn table_roles_do_not_change_conversion() {
|
||||
fn presentational_tables_expand_into_blocks() {
|
||||
for role in ["presentation", "none", " PRESENTATION ", "\tNoNe\r\n"] {
|
||||
for header in [
|
||||
"",
|
||||
"<tr><th>Name</th><th>Value</th></tr>",
|
||||
"<thead><tr><td>Name</td><td>Value</td></tr></thead>",
|
||||
] {
|
||||
let html = format!(
|
||||
"<table role='{role}'><caption><strong>Title</strong></caption>{header}<tbody><tr><td><a href='/one'>one</a></td><td><code>two|three</code></td></tr></tbody><tfoot><tr><td>end</td></tr></tfoot></table>"
|
||||
);
|
||||
let heading = if header.is_empty() {
|
||||
""
|
||||
} else {
|
||||
"Name\n\nValue\n\n"
|
||||
};
|
||||
assert_eq!(
|
||||
markdown(&html),
|
||||
format!("**Title**\n\n{heading}[one](/one)\n\n`two|three`\n\nend"),
|
||||
"{html}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn presentational_table_roles_do_not_propagate_to_nested_tables() {
|
||||
for role in ["presentation", "none"] {
|
||||
let inner =
|
||||
"<table><tr><th>Name</th><th>Value</th></tr><tr><td>one</td><td>two</td></tr></table>";
|
||||
let html = format!(
|
||||
"<table role='{role}'><tr><td>before</td><td>{inner}</td><td>after</td></tr></table>"
|
||||
);
|
||||
assert_eq!(
|
||||
markdown(&html),
|
||||
"before\n\n| Name | Value |\n| --- | --- |\n| one | two |\n\nafter",
|
||||
"{html}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn other_table_roles_do_not_change_conversion() {
|
||||
for role in [
|
||||
"",
|
||||
"presentation",
|
||||
"none",
|
||||
" PRESENTATION ",
|
||||
"table",
|
||||
"grid",
|
||||
"treegrid",
|
||||
"presentationish",
|
||||
"none-other",
|
||||
] {
|
||||
let html = format!(
|
||||
"<table role='{role}'><tr><th>Name</th><th>Value</th></tr><tr><td>one</td><td>two</td></tr></table>"
|
||||
|
||||
Reference in New Issue
Block a user