feat(render): list_ignore_newline / text_ignore_newline = false → <br>
CI / cargo fmt --check (push) Failing after 22s
CI / cargo clippy (push) Successful in 22s
CI / cargo test (push) Successful in 36s
CI / editor keymaps (push) Successful in 1m26s

Implements the deferred half of these options: a soft line break inside a
paragraph / list item can render as <br /> instead of a space.

- Parser: soft breaks now emit a dedicated InlineNode::SoftBreak node
  (parse_inline_seq K::Newline + the list-continuation synthetic join) instead
  of collapsing to Text(" "). Added SoftBreakNode + the variant.
- All inline consumers treat SoftBreak as whitespace: AST visitor (no-op),
  text extraction (index.rs + lib.rs → space), nav/semantic_tokens span_of_inline,
  semantic emit (skip like Text), parser span_of_inline.
- Renderer: render_inlines_break renders top-level SoftBreaks as the
  context-specific string; render_paragraph uses text_ignore_newline,
  render_list_item uses list_ignore_newline; default render_inline → space.
  with_newline_handling builder; export.rs wires both from HtmlConfig.

Default (true/true = upstream default) behaviour is unchanged (space). Updated
3 core tests + 1 parser test that asserted the old Text(" ") shape to treat
SoftBreak as whitespace. New test render_page_html_ignore_newline_controls_soft_breaks.

Gap doc: P3 Config item closed — only the deferred markdown cluster remains.
Full rust suite + clippy clean; Neovim 307, Vim 301/18/21.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-03 13:06:38 +00:00
parent 1246c99c3c
commit e6e3f70dd2
14 changed files with 122 additions and 26 deletions
+37
View File
@@ -253,6 +253,43 @@ fn render_page_html_passes_through_valid_html_tags() {
assert!(html.contains("&lt;script&gt;"), "script escaped: {html}");
}
#[test]
fn render_page_html_ignore_newline_controls_soft_breaks() {
// Paragraph + list item, each with a soft (single) newline inside.
let ast = parse("alpha\nbeta\n\n- one\n two\n");
// Default (true/true): soft breaks render as spaces, no <br>.
let c = cfg("/tmp/x");
let def = export::render_page_html(
&ast,
Some("{{content}}".into()),
"Home",
DiaryDate::today_utc(),
&c.html,
None,
HashMap::new(),
)
.unwrap();
assert!(!def.contains("<br"), "default joins with spaces: {def}");
assert!(def.contains("alpha beta"), "para space: {def}");
// false/false: soft breaks become <br /> in both paragraph and list item.
let mut c2 = cfg("/tmp/x");
c2.html.text_ignore_newline = false;
c2.html.list_ignore_newline = false;
let br = export::render_page_html(
&ast,
Some("{{content}}".into()),
"Home",
DiaryDate::today_utc(),
&c2.html,
None,
HashMap::new(),
)
.unwrap();
assert!(br.contains("alpha<br />beta"), "para <br>: {br}");
assert!(br.contains("one<br />two") || br.contains("one<br /> two"), "list <br>: {br}");
}
#[test]
fn render_page_html_substitutes_emoji_when_enabled() {
let ast = parse("ship it :rocket: and :tada:\n");