Skip to content

feat(statusline): wrap onto multiple rows on narrow terminalsFeat/responsive statusline wrapping#130

Open
fawaikuangtuzhangfei wants to merge 3 commits into
Haleclipse:masterfrom
fawaikuangtuzhangfei:feat/responsive-statusline-wrapping
Open

feat(statusline): wrap onto multiple rows on narrow terminalsFeat/responsive statusline wrapping#130
fawaikuangtuzhangfei wants to merge 3 commits into
Haleclipse:masterfrom
fawaikuangtuzhangfei:feat/responsive-statusline-wrapping

Conversation

@fawaikuangtuzhangfei

@fawaikuangtuzhangfei fawaikuangtuzhangfei commented Jul 6, 2026

Copy link
Copy Markdown

Problem

On narrow terminals the statusline is a single long line, so Claude Code
truncates it to .... This hides not just usage info but the git branch name
and its ahead/behind/dirty markers.

Solution

Claude Code renders each stdout line as a separate status row and sets the
COLUMNS env var to the terminal width. When the rendered line would exceed
COLUMNS, wrap it segment-by-segment onto multiple rows instead of letting it
be truncated.

  • generate_responsive() falls back to the existing single-line output when
    COLUMNS is unknown or the content fits → **no
  • Visible width is measured with Unicode display w
    emoji / CJK glyphs count as 2 columns. This also fixes the existing TUI
    preview wrapping, which used a character count.
  • A 1-column safety margin absorbs variation-selector emoji (e.g. ⚡️) that
    render wider than the Unicode tables report.
  • Adds unit tests (single-line / wide / narrow).

unicode-width is already a transitive dependency (via ratatui), so this only
promotes it to a direct dependency.

The branch also includes a small fix(patcher) commit to satisfy
clippy::unnecessary_sort_by on recent toolchains, so CI stays green.

Before / After (COLUMNS≈50)

Before: 🤖 Opus 4.8 | 📁 repo | 🌿 feature_2026… (branch + git status cut off)
After:

🤖 Opus 4.8 | 📁 CCometixLine
🌿 feature_20260616_v3CarInfo ●⇡
⚡️ 8.1% · 81.1k tokens

Notes

COLUMNS is provided by Claude Code v2.1.153+. On older versions the width is
unknown and the statusline stays single-line (unchanged).

Summary by Sourcery

Add responsive, multi-line statusline rendering based on terminal width to keep content visible on narrow Claude Code terminals.

New Features:

  • Introduce a responsive statusline generator that wraps segments onto multiple rows when they would exceed the terminal width.
  • Detect terminal width from the COLUMNS environment variable so statusline output can adapt to Claude Code-managed terminals.

Bug Fixes:

  • Adjust patch sorting in ClaudeCodePatcher to use sort_by_key with Reverse to satisfy newer clippy lint requirements.

Enhancements:

  • Use Unicode display width for visible-length calculation so emoji and CJK characters wrap correctly, with a small safety margin to avoid truncation.
  • Refactor statusline generation to share segment-joining logic and add segment-based wrapping while preserving existing separators and color behavior.

Build:

  • Promote unicode-width to a direct dependency for accurate terminal column-width measurement.

Documentation:

  • Document responsive statusline wrapping behavior in the English and Chinese READMEs.
  • Clarify that narrow-terminal wrapping prevents truncation of statusline content to ellipsis in Claude Code.

Tests:

  • Add unit tests covering unknown, wide, and narrow terminal widths and ensure responsive statusline output matches the single-line generator when it fits.

yechaoa added 2 commits June 17, 2026 19:18
Claude Code truncates a single long statusline to `...` on narrow
terminals. Since Claude Code renders each stdout line as a separate
status row and exposes the terminal width via the COLUMNS env var,
wrap the statusline by segment when it would overflow instead of
letting it be truncated (which also hid the git branch and its
ahead/behind/dirty markers).

- Add generate_responsive(), which falls back to the existing
  single-line output when COLUMNS is unknown or the content fits,
  so behavior is unchanged for everyone else.
- Read COLUMNS in main() to drive the wrapping.
- Measure visible text with Unicode display width (unicode-width) so
  emoji and CJK glyphs count as the two columns they occupy; this also
  fixes the existing TUI preview wrapping. A one-column safety margin
  absorbs variation-selector emoji that render wider than the Unicode
  tables report.
- Add unit tests covering the single-line, wide and narrow cases.
`patches.sort_by(|a, b| b.cmp(&a))` trips clippy::unnecessary_sort_by on
recent toolchains, which fails CI (`cargo clippy -D warnings`). Use
`sort_by_key` with `Reverse` for the same descending order.
@sourcery-ai

sourcery-ai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implements responsive, width-aware statusline wrapping based on terminal column count, refactors statusline generation to support both single-line and wrapped output, adds tests for responsive behavior, and makes a small patcher sorting fix plus doc and dependency updates.

Sequence diagram for responsive statusline generation

sequenceDiagram
    participant Main
    participant Env
    participant StatusLineGenerator

    Main->>StatusLineGenerator: new(Config::default())
    Main->>Env: detect_terminal_width()
    Env-->>Main: Option<usize> max_width
    Main->>StatusLineGenerator: generate_responsive(segments_data, max_width)
    activate StatusLineGenerator
    StatusLineGenerator->>StatusLineGenerator: join_segments(enabled_segments)
    StatusLineGenerator->>StatusLineGenerator: visible_width(single_line)
    alt max_width unknown or fits
        StatusLineGenerator-->>Main: single_line
    else exceeds budget
        StatusLineGenerator->>StatusLineGenerator: wrap_segments(enabled_segments, budget)
        StatusLineGenerator-->>Main: wrapped_lines
    end
    deactivate StatusLineGenerator
    Main->>Main: println(statusline)
Loading

File-Level Changes

Change Details Files
Make statusline width-aware and support responsive multi-row wrapping on narrow terminals.
  • Change visible_width to compute Unicode display width (columns) after stripping ANSI escapes, using unicode-width instead of character count.
  • Refactor StatusLineGenerator::generate to delegate to a new join_segments helper that produces the single-line statusline.
  • Introduce StatusLineGenerator::generate_responsive that wraps or keeps single-line output depending on max_width, with a one-column safety margin for variation-selector emoji.
  • Implement wrap_segments to greedily pack rendered segments into multiple lines within a max_width budget, handling separators, over-wide single segments, and resetting ANSI styles per line.
  • Add unit tests covering unknown width, wide terminals, narrow wrapping behavior, and equality between generate and responsive output when it fits.
src/core/statusline.rs
Use responsive statusline in main and detect terminal width via COLUMNS env var.
  • Change main to render the statusline with generate_responsive instead of generate, passing a detected terminal width.
  • Add detect_terminal_width helper that reads COLUMNS from the environment, parses it as a positive usize, and returns None when unavailable or invalid.
src/main.rs
Fix patch sorting to satisfy clippy and keep behavior stable.
  • Replace sort_by with sort_by_key using std::cmp::Reverse on patch.location.start_index to sort patches in descending position order.
src/utils/claude_code_patcher.rs
Promote unicode-width to a direct dependency and document responsive wrapping in README files.
  • Add unicode-width crate as a direct dependency in Cargo.toml and update Cargo.lock accordingly.
  • Update English README to mention responsive wrapping that flows the statusline onto multiple rows on narrow terminals.
  • Update Chinese README to mention the new adaptive line-wrapping behavior for narrow terminals.
Cargo.toml
Cargo.lock
README.md
README.zh.md

Possibly linked issues

  • #N/A: Issue reports statusline breaking in Claude; PR fixes statusline truncation by responsive multi-line wrapping on narrow terminals.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • In generate_responsive, segments are rendered once via join_segments and then re-rendered in wrap_segments; consider restructuring to reuse the already-rendered content to avoid duplicate work on every call.
  • In wrap_segments, the use of lines.push(current_line.clone()); current_line.clear(); in multiple places could be simplified to avoid unnecessary cloning by moving the string (e.g., via std::mem::take) when finalizing a line.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `generate_responsive`, segments are rendered once via `join_segments` and then re-rendered in `wrap_segments`; consider restructuring to reuse the already-rendered content to avoid duplicate work on every call.
- In `wrap_segments`, the use of `lines.push(current_line.clone()); current_line.clear();` in multiple places could be simplified to avoid unnecessary cloning by moving the string (e.g., via `std::mem::take`) when finalizing a line.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Address review feedback on the wrapping change:

- generate_responsive() rendered segments via join_segments and then
  re-rendered every segment again in the wrapping path. Extract
  render_enabled() so segments (and the configs Powerline needs) are
  produced once and reused by both the single-line join and the wrapped
  layout.
- Use std::mem::take() when finalizing a wrapped line instead of
  clone() + clear().

As a side effect the Powerline join now indexes configs aligned to the
non-empty rendered segments, fixing a latent off-by-one when a segment
renders empty.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant