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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions