This commit is contained in:
parent
320af54b3d
commit
4666c1995f
14 changed files with 876 additions and 346 deletions
|
|
@ -95,10 +95,94 @@ int main() {
|
|||
Check(md("```\nx").View().find("<code>x") != std::string_view::npos,
|
||||
"md: unterminated fence still renders its content");
|
||||
|
||||
// ── tables ────────────────────────────────────────────────────────
|
||||
CheckEq(md("| a | b |\n|---|---|\n| 1 | 2 |"),
|
||||
R"(<div class="post-body__table"><table>)"
|
||||
R"(<thead><tr><th>a</th><th>b</th></tr></thead>)"
|
||||
R"(<tbody><tr><td>1</td><td>2</td></tr></tbody>)"
|
||||
R"(</table></div>)",
|
||||
"md: pipe table");
|
||||
// The shape the carrier table in the camera post is actually written in:
|
||||
// leading pipe, no trailing one. Requiring both would leave it as prose.
|
||||
CheckEq(md("| Device | OS\n|---|---|\n| FP6 | pmOS |"),
|
||||
R"(<div class="post-body__table"><table>)"
|
||||
R"(<thead><tr><th>Device</th><th>OS</th></tr></thead>)"
|
||||
R"(<tbody><tr><td>FP6</td><td>pmOS</td></tr></tbody>)"
|
||||
R"(</table></div>)",
|
||||
"md: outer pipes are optional independently");
|
||||
// The whole reason recognition needs two lines. These posts paste pipelines
|
||||
// and or-lists into prose constantly; none of them is a table.
|
||||
CheckEq(md("dmesg | grep ufs"), "<p>dmesg | grep ufs</p>",
|
||||
"md: a pipe in prose is not a table");
|
||||
// A mismatched column count is far more likely to be prose with pipes in it
|
||||
// than a table whose author miscounted — so it stays prose, visibly odd.
|
||||
Check(md("| a | b |\n|---|").View().starts_with("<p>"),
|
||||
"md: delimiter row must agree about the column count",
|
||||
md("| a | b |\n|---|").View());
|
||||
// `---` is a rule, as it is everywhere else in this file. A one-column
|
||||
// table has to write the pipe.
|
||||
CheckEq(md("| Device\n---"), "<p>| Device</p><hr>",
|
||||
"md: a bare dash row is still a thematic break");
|
||||
CheckEq(md("| a | b | c |\n|:---|:---:|---:|\n| 1 | 2 | 3 |"),
|
||||
R"(<div class="post-body__table"><table>)"
|
||||
R"(<thead><tr><th>a</th>)"
|
||||
R"(<th class="post-body__cell--center">b</th>)"
|
||||
R"(<th class="post-body__cell--right">c</th></tr></thead>)"
|
||||
R"(<tbody><tr><td>1</td>)"
|
||||
R"(<td class="post-body__cell--center">2</td>)"
|
||||
R"(<td class="post-body__cell--right">3</td></tr></tbody>)"
|
||||
R"(</table></div>)",
|
||||
"md: alignment row moves the column, left needs no class");
|
||||
// A short row is padded so the grid stays rectangular.
|
||||
Check(md("| a | b |\n|---|---|\n| 1 |").View().find("<td>1</td><td></td>")
|
||||
!= std::string_view::npos,
|
||||
"md: a short row is padded to the header width",
|
||||
md("| a | b |\n|---|---|\n| 1 |").View());
|
||||
// Cells are prose, so everything inline works in them — and everything
|
||||
// inline is still escaped in them.
|
||||
Check(md("| [x](https://e.example) | `<b>` |\n|---|---|").View().find(
|
||||
R"(<th><a href="https://e.example" rel="noopener">x</a></th>)"
|
||||
R"(<th><code><b></code></th>)") != std::string_view::npos,
|
||||
"md: cells render inline markup and stay escaped",
|
||||
md("| [x](https://e.example) | `<b>` |\n|---|---|").View());
|
||||
// `\|` is how a cell contains a pipe.
|
||||
Check(md("| a \\| b | c |\n|---|---|").View().find("<th>a | b</th><th>c</th>")
|
||||
!= std::string_view::npos,
|
||||
"md: an escaped pipe is cell content, not a cell boundary",
|
||||
md("| a \\| b | c |\n|---|---|").View());
|
||||
// The GPU comparison in one of these posts is a table inside a quote.
|
||||
Check(md("> | a | b |\n> |---|---|\n> | 1 | 2 |").View().starts_with(
|
||||
R"(<blockquote class="post-body__quote"><div class="post-body__table">)"),
|
||||
"md: a table inside a blockquote is still a table",
|
||||
md("> | a | b |\n> |---|---|\n> | 1 | 2 |").View());
|
||||
// A table under a prose line with no blank line between is still a table:
|
||||
// absorbing it into the paragraph is the exact failure this all fixes.
|
||||
Check(md("text\n| a | b |\n|---|---|").View().starts_with("<p>text</p><div"),
|
||||
"md: a table interrupts a paragraph",
|
||||
md("text\n| a | b |\n|---|---|").View());
|
||||
// Prose resumes after the table rather than being eaten as a one-cell row.
|
||||
Check(md("| a | b |\n|---|---|\n| 1 | 2 |\nafter").View().ends_with("<p>after</p>"),
|
||||
"md: a line with no pipe ends the table",
|
||||
md("| a | b |\n|---|---|\n| 1 | 2 |\nafter").View());
|
||||
// A list marker wins over a table: a line that opens one is a list item.
|
||||
Check(md("- a | b\n|---|---|").View().starts_with("<ul"),
|
||||
"md: a list item is not a table header",
|
||||
md("- a | b\n|---|---|").View());
|
||||
|
||||
// ── inline ────────────────────────────────────────────────────────
|
||||
CheckEq(md("**bold**"), "<p><strong>bold</strong></p>", "md: strong");
|
||||
CheckEq(md("*em*"), "<p><em>em</em></p>", "md: emphasis");
|
||||
CheckEq(md("2 * 3 * 4"), "<p>2 * 3 * 4</p>", "md: spaced asterisks stay literal");
|
||||
// <s>, not GFM's <del>: nothing was removed from this document, and the one
|
||||
// body that uses this is striking a joke through for effect.
|
||||
CheckEq(md("~~struck~~"), "<p><s>struck</s></p>", "md: strikethrough");
|
||||
CheckEq(md("~~**both**~~"), "<p><s><strong>both</strong></s></p>",
|
||||
"md: strikethrough nests");
|
||||
// A lone tilde is a home directory or an approximation, never a delimiter.
|
||||
CheckEq(md("~/.local and ~5 minutes"), "<p>~/.local and ~5 minutes</p>",
|
||||
"md: a single tilde strikes nothing");
|
||||
CheckEq(md("a ~~ b ~~ c"), "<p>a ~~ b ~~ c</p>",
|
||||
"md: spaced tildes stay literal");
|
||||
// Underscores are deliberately inert: these posts paste kernel symbol
|
||||
// names into prose, and italicising half of one is worse than not
|
||||
// italicising a word that used the underscore form.
|
||||
|
|
@ -172,7 +256,11 @@ int main() {
|
|||
Check(!md("[unclosed](").View().empty(), "md: unclosed link terminates");
|
||||
Check(!md(".View().empty(), "md: unclosed image terminates");
|
||||
Check(!md("`unclosed").View().empty(), "md: unclosed code span terminates");
|
||||
Check(!md("~~unclosed").View().empty(), "md: unclosed strikethrough terminates");
|
||||
Check(!md("> > > > > > > > deep").View().empty(), "md: over-deep nesting terminates");
|
||||
Check(!md("|||\n|||").View().empty(), "md: degenerate table terminates");
|
||||
Check(!md("|---|---|").View().empty(), "md: a lone delimiter row terminates");
|
||||
Check(!md("| a |\n| - |\n|").View().empty(), "md: a ragged table terminates");
|
||||
|
||||
if (failures != 0) {
|
||||
std::println(std::cerr, "{} check(s) failed", failures);
|
||||
|
|
|
|||
Loading…
Reference in a new issue