From 46021a35917a1de80ecbe4c2c15372034cca9246 Mon Sep 17 00:00:00 2001 From: Felipe Coury Date: Tue, 10 Feb 2026 21:50:36 -0300 Subject: [PATCH] fix(tui): wrap syntax spans before first overflowing wide character --- codex-rs/tui/src/diff_render.rs | 37 ++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/codex-rs/tui/src/diff_render.rs b/codex-rs/tui/src/diff_render.rs index 7eaea8cb4e..bc8ba11d5e 100644 --- a/codex-rs/tui/src/diff_render.rs +++ b/codex-rs/tui/src/diff_render.rs @@ -618,9 +618,11 @@ fn wrap_styled_spans(spans: &[RtSpan<'static>], max_cols: usize) -> Vec max_cols && byte_end > 0 { - // Adding this character would exceed the line width and we - // already have some content — break here. + if col + chars_col + w > max_cols { + // Adding this character would exceed the line width. + // Break here; if this is the first character in `remaining` + // we will flush/start a new line in the `byte_end == 0` + // branch below before consuming it. break; } byte_end += ch.len_utf8(); @@ -1092,6 +1094,35 @@ mod tests { ); } + #[test] + fn wrap_styled_spans_wraps_before_first_overflowing_char() { + let spans = vec![RtSpan::raw("abcd\t界")]; + let result = wrap_styled_spans(&spans, 5); + + let line_text: Vec = result + .iter() + .map(|line| { + line.iter() + .map(|span| span.content.as_ref()) + .collect::() + }) + .collect(); + assert_eq!(line_text, vec!["abcd", "\t", "界"]); + + let line_width = |line: &[RtSpan<'static>]| -> usize { + line.iter() + .flat_map(|span| span.content.chars()) + .map(|ch| ch.width().unwrap_or(if ch == '\t' { TAB_WIDTH } else { 0 })) + .sum() + }; + for line in &result { + assert!( + line_width(line) <= 5, + "wrapped line exceeded width 5: {line:?}" + ); + } + } + #[test] fn large_update_diff_skips_highlighting() { // Build a patch large enough to exceed MAX_HIGHLIGHT_LINES (10_000).