# External (non-OpenAI) Pull Request Requirements Before opening this Pull Request, please read the dedicated "Contributing" markdown file or your PR may be closed: https://github.com/openai/codex/blob/main/docs/contributing.md If your PR conforms to our contribution guidelines, replace this text with a detailed and high quality description of your changes.
148 lines
3.9 KiB
Rust
148 lines
3.9 KiB
Rust
use ratatui::prelude::*;
|
|
use ratatui::style::Stylize;
|
|
use std::collections::BTreeSet;
|
|
use unicode_width::UnicodeWidthChar;
|
|
use unicode_width::UnicodeWidthStr;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub(crate) struct FieldFormatter {
|
|
indent: &'static str,
|
|
label_width: usize,
|
|
value_offset: usize,
|
|
value_indent: String,
|
|
}
|
|
|
|
impl FieldFormatter {
|
|
pub(crate) const INDENT: &'static str = " ";
|
|
|
|
pub(crate) fn from_labels<S>(labels: impl IntoIterator<Item = S>) -> Self
|
|
where
|
|
S: AsRef<str>,
|
|
{
|
|
let label_width = labels
|
|
.into_iter()
|
|
.map(|label| UnicodeWidthStr::width(label.as_ref()))
|
|
.max()
|
|
.unwrap_or(0);
|
|
let indent_width = UnicodeWidthStr::width(Self::INDENT);
|
|
let value_offset = indent_width + label_width + 1 + 3;
|
|
|
|
Self {
|
|
indent: Self::INDENT,
|
|
label_width,
|
|
value_offset,
|
|
value_indent: " ".repeat(value_offset),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn line(
|
|
&self,
|
|
label: &'static str,
|
|
value_spans: Vec<Span<'static>>,
|
|
) -> Line<'static> {
|
|
Line::from(self.full_spans(label, value_spans))
|
|
}
|
|
|
|
pub(crate) fn continuation(&self, mut spans: Vec<Span<'static>>) -> Line<'static> {
|
|
let mut all_spans = Vec::with_capacity(spans.len() + 1);
|
|
all_spans.push(Span::from(self.value_indent.clone()).dim());
|
|
all_spans.append(&mut spans);
|
|
Line::from(all_spans)
|
|
}
|
|
|
|
pub(crate) fn value_width(&self, available_inner_width: usize) -> usize {
|
|
available_inner_width.saturating_sub(self.value_offset)
|
|
}
|
|
|
|
pub(crate) fn full_spans(
|
|
&self,
|
|
label: &str,
|
|
mut value_spans: Vec<Span<'static>>,
|
|
) -> Vec<Span<'static>> {
|
|
let mut spans = Vec::with_capacity(value_spans.len() + 1);
|
|
spans.push(self.label_span(label));
|
|
spans.append(&mut value_spans);
|
|
spans
|
|
}
|
|
|
|
fn label_span(&self, label: &str) -> Span<'static> {
|
|
let mut buf = String::with_capacity(self.value_offset);
|
|
buf.push_str(self.indent);
|
|
|
|
buf.push_str(label);
|
|
buf.push(':');
|
|
|
|
let label_width = UnicodeWidthStr::width(label);
|
|
let padding = 3 + self.label_width.saturating_sub(label_width);
|
|
for _ in 0..padding {
|
|
buf.push(' ');
|
|
}
|
|
|
|
Span::from(buf).dim()
|
|
}
|
|
}
|
|
|
|
pub(crate) fn push_label(labels: &mut Vec<String>, seen: &mut BTreeSet<String>, label: &str) {
|
|
if seen.contains(label) {
|
|
return;
|
|
}
|
|
|
|
let owned = label.to_string();
|
|
seen.insert(owned.clone());
|
|
labels.push(owned);
|
|
}
|
|
|
|
pub(crate) fn line_display_width(line: &Line<'static>) -> 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 mut used = 0usize;
|
|
let mut spans_out: Vec<Span<'static>> = Vec::new();
|
|
|
|
for span in line.spans {
|
|
let text = span.content.into_owned();
|
|
let style = span.style;
|
|
let span_width = UnicodeWidthStr::width(text.as_str());
|
|
|
|
if span_width == 0 {
|
|
spans_out.push(Span::styled(text, style));
|
|
continue;
|
|
}
|
|
|
|
if used >= max_width {
|
|
break;
|
|
}
|
|
|
|
if used + span_width <= max_width {
|
|
used += span_width;
|
|
spans_out.push(Span::styled(text, style));
|
|
continue;
|
|
}
|
|
|
|
let mut truncated = String::new();
|
|
for ch in text.chars() {
|
|
let ch_width = UnicodeWidthChar::width(ch).unwrap_or(0);
|
|
if used + ch_width > max_width {
|
|
break;
|
|
}
|
|
truncated.push(ch);
|
|
used += ch_width;
|
|
}
|
|
|
|
if !truncated.is_empty() {
|
|
spans_out.push(Span::styled(truncated, style));
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
Line::from(spans_out)
|
|
}
|