mirror of
https://github.com/openai/codex.git
synced 2026-05-05 05:42:33 +03:00
This PR replicates the `tui` code directory and creates a temporary parallel `tui_app_server` directory. It also implements a new feature flag `tui_app_server` to select between the two tui implementations. Once the new app-server-based TUI is stabilized, we'll delete the old `tui` directory and feature flag.
101 lines
2.6 KiB
Rust
101 lines
2.6 KiB
Rust
use ratatui::text::Line;
|
|
use ratatui::text::Span;
|
|
use unicode_width::UnicodeWidthChar;
|
|
use unicode_width::UnicodeWidthStr;
|
|
|
|
pub(crate) fn line_width(line: &Line<'_>) -> usize {
|
|
line.iter()
|
|
.map(|span| UnicodeWidthStr::width(span.content.as_ref()))
|
|
.sum()
|
|
}
|
|
|
|
pub(crate) fn truncate_line_to_width(line: Line<'static>, max_width: usize) -> Line<'static> {
|
|
if max_width == 0 {
|
|
return Line::from(Vec::<Span<'static>>::new());
|
|
}
|
|
|
|
let Line {
|
|
style,
|
|
alignment,
|
|
spans,
|
|
} = line;
|
|
let mut used = 0usize;
|
|
let mut spans_out: Vec<Span<'static>> = Vec::with_capacity(spans.len());
|
|
|
|
for span in spans {
|
|
let span_width = UnicodeWidthStr::width(span.content.as_ref());
|
|
|
|
if span_width == 0 {
|
|
spans_out.push(span);
|
|
continue;
|
|
}
|
|
|
|
if used >= max_width {
|
|
break;
|
|
}
|
|
|
|
if used + span_width <= max_width {
|
|
used += span_width;
|
|
spans_out.push(span);
|
|
continue;
|
|
}
|
|
|
|
let style = span.style;
|
|
let text = span.content.as_ref();
|
|
let mut end_idx = 0usize;
|
|
for (idx, ch) in text.char_indices() {
|
|
let ch_width = UnicodeWidthChar::width(ch).unwrap_or(0);
|
|
if used + ch_width > max_width {
|
|
break;
|
|
}
|
|
end_idx = idx + ch.len_utf8();
|
|
used += ch_width;
|
|
}
|
|
|
|
if end_idx > 0 {
|
|
spans_out.push(Span::styled(text[..end_idx].to_string(), style));
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
Line {
|
|
style,
|
|
alignment,
|
|
spans: spans_out,
|
|
}
|
|
}
|
|
|
|
/// Truncate a styled line to `max_width` and append an ellipsis on overflow.
|
|
///
|
|
/// Intended for short UI rows. This preserves a fast no-overflow path (width
|
|
/// pre-scan + return original line unchanged) and uses `truncate_line_to_width`
|
|
/// for the overflow case.
|
|
/// Performance should be reevaluated if using this method in loops/over larger content in the future.
|
|
pub(crate) fn truncate_line_with_ellipsis_if_overflow(
|
|
line: Line<'static>,
|
|
max_width: usize,
|
|
) -> Line<'static> {
|
|
if max_width == 0 {
|
|
return Line::from(Vec::<Span<'static>>::new());
|
|
}
|
|
|
|
if line_width(&line) <= max_width {
|
|
return line;
|
|
}
|
|
|
|
let truncated = truncate_line_to_width(line, max_width.saturating_sub(1));
|
|
let Line {
|
|
style,
|
|
alignment,
|
|
mut spans,
|
|
} = truncated;
|
|
let ellipsis_style = spans.last().map(|span| span.style).unwrap_or_default();
|
|
spans.push(Span::styled("…", ellipsis_style));
|
|
Line {
|
|
style,
|
|
alignment,
|
|
spans,
|
|
}
|
|
}
|