Describe the bug
When rendering Markdown text in TUI streaming mode (specifically in non-compact mode):
- Newline omission / output stickiness: When a text description is immediately followed by a block-level element (e.g., a fenced code block),
pulldown-cmark does not emit any line-break or soft-break events between them. This results in the code block top border rendering immediately after the text (e.g., 2. Enter directory:╭─ code), causing layout stickiness.
- Consecutive empty line accumulation: Multiple block closing events (such as code block end + list item end + list end) consecutively append newlines unconditionally. This leads to redundant empty lines stacking up in the terminal (e.g., 3-4 consecutive empty lines), which wastes screen space.
Solution / Implementation
We solved this by introducing an idempotent, smart newline-ensuring helper:
fn ensure_newlines(output: &mut String, count: usize) {
if output.is_empty() {
return;
}
let current = output.chars().rev().take_while(|&c| c == '\n').count();
if current < count {
output.push_str(&"\n".repeat(count - current));
}
}
And applied it defensively to block-level start/end event handlers in render.rs:
- Code Block Start / Item Start / Blockquote Start: Call
ensure_newlines(output, 1) to prevent inline stickiness.
- Heading Start: Call
ensure_newlines(output, 2) to ensure space before headers.
- Block End Events: Replace unconditional newline pushing with
ensure_newlines(output, 2) (for paragraphs, headings, lists, table closings) or ensure_newlines(output, 1) (for items, blockquotes), which prevents consecutive newlines from accumulating beyond 1 empty line.
Describe the bug
When rendering Markdown text in TUI streaming mode (specifically in non-compact mode):
pulldown-cmarkdoes not emit any line-break or soft-break events between them. This results in the code block top border rendering immediately after the text (e.g.,2. Enter directory:╭─ code), causing layout stickiness.Solution / Implementation
We solved this by introducing an idempotent, smart newline-ensuring helper:
And applied it defensively to block-level start/end event handlers in
render.rs:ensure_newlines(output, 1)to prevent inline stickiness.ensure_newlines(output, 2)to ensure space before headers.ensure_newlines(output, 2)(for paragraphs, headings, lists, table closings) orensure_newlines(output, 1)(for items, blockquotes), which prevents consecutive newlines from accumulating beyond 1 empty line.