Skip to content

Fix markdown formatting issues in TUI streaming: block-level element newline omission and consecutive empty line accumulation #3259

Description

@ibmany-spec

Describe the bug

When rendering Markdown text in TUI streaming mode (specifically in non-compact mode):

  1. 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.
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions