fix(html): keyword-in-heading badge + brackets in wikilink descriptions
Two export bugs reported against v0.4.0: 1. A heading whose text is a keyword (`== TODO ==`) rendered as an inline `<span class="todo">` badge with an *empty* id, instead of a styled header. Root causes: `inline_text` dropped Keyword nodes (so the heading id/anchor came out empty, or `My TODO list` → `My list`), and the keyword span was emitted inside the heading. Fix: `inline_text` now includes the keyword's literal text (via new `Keyword::label()`), and heading content renders keywords as plain text (flatten_keywords) so a keyword title looks like a header. Keyword badges still render in body text as before. 2. A `]` inside a wikilink description (e.g. `[[page|Task [B-1]]]`) closed the link at the first `]]`, truncating the description and leaking a stray `]`. The close-scan is now bracket-aware (find_wikilink_close): inner `[ ]` pairs are balanced so the link closes at the correct `]]`. Added regression tests for both. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -77,6 +77,43 @@ fn centered_heading_gets_centered_class() {
|
||||
assert!(out.contains("<h1 class=\"centered\" id=\"T\">T</h1>"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn heading_keyword_renders_as_plain_text_not_badge() {
|
||||
// `== TODO ==` is a section title, not an inline keyword: it must render as
|
||||
// a normal header (with a correct, non-empty id), not a `<span class="todo">`
|
||||
// badge. Regression for the exported header losing its header styling + id.
|
||||
let out = render("== TODO ==\n");
|
||||
assert!(
|
||||
out.contains(
|
||||
"<div id=\"TODO\"><h2 id=\"TODO\" class=\"header\">\
|
||||
<a href=\"#TODO\">TODO</a></h2></div>"
|
||||
),
|
||||
"got: {out}"
|
||||
);
|
||||
assert!(!out.contains("class=\"todo\""), "no keyword badge: {out}");
|
||||
|
||||
// A keyword mid-title keeps its text in the id (no dropped word / double
|
||||
// space) and still renders plainly.
|
||||
let out2 = render("= My TODO list =\n");
|
||||
assert!(out2.contains("id=\"My TODO list\""), "got: {out2}");
|
||||
assert!(!out2.contains("class=\"todo\""), "got: {out2}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wikilink_description_keeps_inner_brackets() {
|
||||
// A `]` inside a wikilink description (e.g. a `[TICKET-1]`) must not close
|
||||
// the link early. Regression for descriptions with brackets being mangled.
|
||||
let out = render("[[page|Task [B-1]]]\n");
|
||||
assert!(
|
||||
out.contains("<a href=\"page.html\">Task [B-1]</a>"),
|
||||
"got: {out}"
|
||||
);
|
||||
// Two bracketed links on one line both resolve.
|
||||
let out2 = render("[[a|X [1]]] and [[b|Y [2]]]\n");
|
||||
assert!(out2.contains("<a href=\"a.html\">X [1]</a>"), "got: {out2}");
|
||||
assert!(out2.contains("<a href=\"b.html\">Y [2]</a>"), "got: {out2}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn paragraph_wraps_inline_content() {
|
||||
let out = render("Hello world\n");
|
||||
|
||||
Reference in New Issue
Block a user