mirror of
https://github.com/openai/codex.git
synced 2026-10-29 19:08:43 +00:00
Fix TUI layout for halfwidth Japanese sound marks (#35962)
## What changed - Use Ratatui-compatible terminal widths throughout TUI layout, preserving full-width counters for long lines. - Wrap and truncate on grapheme boundaries so halfwidth kana sound marks and emoji sequences stay intact. - Apply the corrected sizing to text input, selection popups, status and history cells, Markdown, hyperlinks, and scrollable cloud diffs. ## Testing - Add focused unit and snapshot coverage for wrapping, truncation, cursor and mask alignment, popup columns, status output, session headers, Markdown tables, process output, and scrollable diffs. GitOrigin-RevId: 294ec0bcff743c6bb16d55ec26308524ce741879
This commit is contained in:
@@ -49,6 +49,8 @@ use crate::terminal_hyperlinks::annotate_web_urls_in_line;
|
||||
use crate::terminal_hyperlinks::remap_wrapped_line;
|
||||
use crate::terminal_hyperlinks::visible_lines;
|
||||
use crate::terminal_hyperlinks::web_destination;
|
||||
use crate::width::char_width;
|
||||
use crate::width::display_width;
|
||||
use crate::wrapping::RtOptions;
|
||||
use crate::wrapping::adaptive_wrap_line;
|
||||
use crate::wrapping::word_wrap_line;
|
||||
@@ -72,8 +74,6 @@ use std::ops::Range;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::LazyLock;
|
||||
use unicode_width::UnicodeWidthChar;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
use url::Url;
|
||||
|
||||
mod streaming;
|
||||
@@ -1302,7 +1302,7 @@ where
|
||||
let plain = cell.plain_text();
|
||||
let mut word_count = 0usize;
|
||||
for token in plain.split_whitespace() {
|
||||
let token_width = token.width();
|
||||
let token_width = display_width(token);
|
||||
body_token_width = body_token_width.max(token_width);
|
||||
long_body_token_count += usize::from(token_width >= 20);
|
||||
word_count += 1;
|
||||
@@ -1311,7 +1311,7 @@ where
|
||||
body_token_count += word_count;
|
||||
total_words += word_count;
|
||||
total_cells += 1;
|
||||
total_cell_width += plain.width();
|
||||
total_cell_width += display_width(&plain);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1321,7 +1321,7 @@ where
|
||||
total_words as f64 / total_cells as f64
|
||||
};
|
||||
let avg_cell_width = if total_cells == 0 {
|
||||
header_plain.width() as f64
|
||||
display_width(&header_plain) as f64
|
||||
} else {
|
||||
total_cell_width as f64 / total_cells as f64
|
||||
};
|
||||
@@ -1607,7 +1607,7 @@ where
|
||||
} else {
|
||||
current_text.push(ch);
|
||||
}
|
||||
column += UnicodeWidthChar::width(ch).unwrap_or(/*default*/ 0);
|
||||
column += char_width(ch);
|
||||
}
|
||||
flush(&mut out, &mut current_text, current_destination);
|
||||
}
|
||||
@@ -1756,7 +1756,10 @@ where
|
||||
|
||||
#[inline]
|
||||
fn spans_display_width(spans: &[Span<'_>]) -> usize {
|
||||
spans.iter().map(|span| span.content.width()).sum()
|
||||
spans
|
||||
.iter()
|
||||
.map(|span| display_width(span.content.as_ref()))
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -1775,7 +1778,10 @@ where
|
||||
|
||||
#[inline]
|
||||
fn longest_token_width(text: &str) -> usize {
|
||||
text.split_whitespace().map(str::width).max().unwrap_or(0)
|
||||
text.split_whitespace()
|
||||
.map(display_width)
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
fn push_inline_style(&mut self, style: Style) {
|
||||
@@ -1887,7 +1893,7 @@ where
|
||||
}
|
||||
} else {
|
||||
let mut spans = self.current_initial_indent.clone();
|
||||
let shift = spans.iter().map(|span| span.content.width()).sum::<usize>();
|
||||
let shift = Self::spans_display_width(&spans);
|
||||
spans.append(&mut line.line.spans);
|
||||
for hyperlink in &mut line.hyperlinks {
|
||||
hyperlink.columns =
|
||||
@@ -1926,7 +1932,7 @@ where
|
||||
};
|
||||
|
||||
let mut spans = self.prefix_spans(pending_marker_line);
|
||||
let shift = spans.iter().map(|span| span.content.width()).sum::<usize>();
|
||||
let shift = Self::spans_display_width(&spans);
|
||||
spans.append(&mut line.line.spans);
|
||||
for hyperlink in &mut line.hyperlinks {
|
||||
hyperlink.columns = hyperlink.columns.start + shift..hyperlink.columns.end + shift;
|
||||
@@ -2844,6 +2850,13 @@ mod tests {
|
||||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn table_widths_count_halfwidth_sound_marks() {
|
||||
let cell = make_cell("ガパ");
|
||||
assert_eq!(W::cell_display_width(&cell), 4);
|
||||
assert_eq!(W::longest_token_width("ガパtail"), 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_value_table_keeps_web_annotations() {
|
||||
let destination = "https://example.com/a/very/long/path";
|
||||
|
||||
Reference in New Issue
Block a user