Make --- divide cards, and stop drawing ASCII rules
WML has no horizontal rule element -- its entire %layout entity is
`<br>` -- so a thematic break was being rendered as `<p>------------</p>`,
a hardcoded twelve-dash guess at screen width that wraps into nonsense on
a narrow device and is short on a wide one. It was the one construct
where the renderer invented a width out of nothing.
A `---` already looks like a divider and reads as one in every Markdown
document ever written, so it now is one: it starts a new card, by
default. Rules are never drawn, whether or not they divide.
Thematic breaks and `{.card Title}` markers are now a single mechanism --
an explicit divider, optionally carrying a title -- and compose freely in
one document. Both still win over --split-level. A section takes its
title from its own first heading when the divider does not supply one, so
the common shape needs no titles at all:
Intro on the menu.
---
## Weather
Cold and clear.
`--no-split-on-rule` / `split_on_rule: false` opts out of the splitting;
it does not bring the dashes back.
Three edges this opened, each fixed here rather than left to bite:
- Setext headings. `Heading` over `---` is an H2 in Markdown, but the
parser was ATX-only, so it produced a paragraph plus a rule. Once a
rule divides cards, a setext document would have split at every heading
and stranded each heading as body copy on the card before it. Setext
headings are now parsed, so such a document keeps its structure.
- Empty sections. A trailing `---`, or two in a row, produced an empty
"Untitled" card and a menu entry leading to it. Sections with no
renderable content are dropped.
- The multi-card threshold was two sections, so a document with a single
divider collapsed back into one card. Any explicit divider now makes a
multi-card deck: `A --- B` asks for two screens.
Also fixes a latent frontmatter bug this made far more likely: a document
opening with `---` and no `key: value` lines had its opening swallowed as
if the break were a frontmatter fence. A fenced block with no keys in it
is not frontmatter.
222 tests. Rendering examples/trail.md still matches md2txt's committed
output byte for byte; no golden file contains an ASCII rule any more.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b83e24e2f7
commit
3b728d9d74
17 changed files with 383 additions and 36 deletions
|
|
@ -211,3 +211,108 @@ class TestSlugify:
|
|||
assert slugify("", "card1") == "card1"
|
||||
assert slugify(None, "card1") == "card1"
|
||||
assert slugify("!!!", "card1") == "card1"
|
||||
|
||||
|
||||
TWO_BY_RULE = """\
|
||||
Intro text.
|
||||
|
||||
---
|
||||
|
||||
## Weather
|
||||
|
||||
Cold and clear.
|
||||
|
||||
---
|
||||
|
||||
## Status
|
||||
|
||||
North Loop open.
|
||||
"""
|
||||
|
||||
|
||||
class TestRuleDividers:
|
||||
"""A thematic break divides cards; WML has no rule element to draw."""
|
||||
|
||||
def test_rules_divide_by_default(self, render):
|
||||
assert card_ids(render(TWO_BY_RULE)) == ["menu", "card1", "card2"]
|
||||
|
||||
def test_sections_take_their_own_heading_as_title(self, render):
|
||||
# A rule carries no title, so the section's first heading supplies it.
|
||||
markup = render(TWO_BY_RULE)
|
||||
assert '<card id="card1" title="Weather">' in markup
|
||||
assert '<card id="card2" title="Status">' in markup
|
||||
|
||||
def test_a_single_rule_makes_two_cards(self, render):
|
||||
# Regression: the multi-card threshold used to be two sections, so
|
||||
# one divider collapsed back into a single card.
|
||||
assert card_ids(render("Intro.\n\n---\n\n## Alpha\n\nBody.\n")) == [
|
||||
"menu",
|
||||
"card1",
|
||||
]
|
||||
|
||||
def test_no_rule_is_ever_drawn(self, render):
|
||||
assert "------" not in render(TWO_BY_RULE)
|
||||
|
||||
def test_disabling_keeps_one_card_and_still_draws_nothing(self, render):
|
||||
markup = render(TWO_BY_RULE, split_on_rule=False)
|
||||
assert card_ids(markup) == ["card1"]
|
||||
assert "------" not in markup
|
||||
|
||||
@pytest.mark.parametrize("glyph", ["---", "***", "___", "- - -", "*****"])
|
||||
def test_every_thematic_break_glyph_divides(self, render, glyph):
|
||||
markup = render(f"Intro.\n\n{glyph}\n\n## Alpha\n\nBody.\n")
|
||||
assert card_ids(markup) == ["menu", "card1"]
|
||||
|
||||
def test_rules_and_card_markers_compose(self, render):
|
||||
markup = render("Intro.\n\n---\n## A\nx\n\n{.card Named}\ny\n")
|
||||
assert card_ids(markup) == ["menu", "card1", "card2"]
|
||||
assert '<card id="card2" title="Named">' in markup
|
||||
|
||||
def test_explicit_dividers_win_over_split_level(self, render):
|
||||
markup = render("Intro.\n\n---\n\n## A\n\n## B\n", split_level=2)
|
||||
assert card_ids(markup) == ["menu", "card1"]
|
||||
|
||||
|
||||
class TestEmptySections:
|
||||
def test_trailing_rule_adds_no_empty_card(self, render):
|
||||
# Ending a document with `---` is common and must not produce an
|
||||
# "Untitled" card with a menu entry leading nowhere.
|
||||
markup = render("Intro.\n\n---\n\n## A\n\nBody.\n\n---\n")
|
||||
assert card_ids(markup) == ["menu", "card1"]
|
||||
assert "Untitled" not in markup
|
||||
|
||||
def test_consecutive_rules_collapse(self, render):
|
||||
markup = render("Intro.\n\n---\n\n---\n\n## A\n\nBody.\n")
|
||||
assert card_ids(markup) == ["menu", "card1"]
|
||||
|
||||
def test_leading_rule_is_harmless(self, render):
|
||||
markup = render("---\n\n## A\n\nBody.\n")
|
||||
assert "Untitled" not in markup
|
||||
|
||||
def test_document_of_only_a_rule(self, render):
|
||||
assert card_ids(render("---\n")) == ["card1"]
|
||||
|
||||
|
||||
class TestSetextHeadings:
|
||||
"""`---` under text is an H2, not a divider -- Markdown says so, and
|
||||
getting it wrong would split at every heading of a setext document."""
|
||||
|
||||
def test_dashes_under_text_make_a_heading_not_a_split(self, render):
|
||||
markup = render("My Heading\n---\n\nBody text.\n")
|
||||
assert card_ids(markup) == ["card1"]
|
||||
assert "<p><b><big>My Heading</big></b></p>" in markup
|
||||
|
||||
def test_equals_make_a_level_one_heading(self, render):
|
||||
assert "<p><b><big>Big Title</big></b></p>" in render("Big Title\n===\n\nBody.\n")
|
||||
|
||||
def test_setext_heading_can_drive_split_level(self, render):
|
||||
markup = render("# Top\n\nIntro.\n\nAlpha\n-----\n\nA.\n\nBeta\n----\n\nB.\n", split_level=2)
|
||||
assert card_ids(markup) == ["menu", "card1", "card2"]
|
||||
|
||||
def test_a_rule_after_a_blank_line_is_still_a_rule(self, render):
|
||||
# The paragraph has been flushed, so this underlines nothing.
|
||||
assert card_ids(render("Text.\n\n---\n\n## A\n\nBody.\n")) == ["menu", "card1"]
|
||||
|
||||
def test_two_dashes_underline_but_do_not_divide(self, render):
|
||||
# `--` is a valid setext underline but not a thematic break.
|
||||
assert card_ids(render("Heading\n--\n\nBody.\n")) == ["card1"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue