use ratatui::buffer::CellWidth;
pub fn display_width(s: &str) -> usize {
s.cell_width() as usize
}
pub fn fit_display_width(s: &str, width: usize) -> String {
let mut out = String::new();
let mut used = 0usize;
if display_width(s) <= width {
out.push_str(s);
used = display_width(s);
} else {
let ellipsis = if width > 3 { 3 } else { 0 };
let budget = width - ellipsis;
let mut buf = [0u8; 4];
for ch in s.chars() {
let w = display_width(ch.encode_utf8(&mut buf));
if used + w > budget {
break;
}
out.push(ch);
used += w;
}
for _ in 0..ellipsis {
out.push('.');
}
used += ellipsis;
}
for _ in used..width {
out.push(' ');
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ascii_pads_to_width() {
assert_eq!(fit_display_width("main", 8), "main ");
assert_eq!(fit_display_width("exactly8", 8), "exactly8");
}
#[test]
fn ascii_truncates_with_ellipsis() {
assert_eq!(fit_display_width("feature/login-flow", 10), "feature...");
}
#[test]
fn cjk_counts_two_cells_per_char() {
assert_eq!(fit_display_width("あいうえお", 10), "あいうえお");
assert_eq!(fit_display_width("あいうえお", 8), "あい... ");
}
#[test]
fn cjk_pads_by_cells_not_chars() {
assert_eq!(fit_display_width("あい", 8), "あい ");
}
#[test]
fn mixed_ascii_cjk() {
assert_eq!(fit_display_width("fix-バグ", 8), "fix-バグ");
assert_eq!(fit_display_width("fix-バグ修正", 8), "fix-... ");
}
#[test]
fn tiny_width_no_ellipsis() {
assert_eq!(fit_display_width("abcdef", 3), "abc");
assert_eq!(fit_display_width("あいう", 3), "あ "); }
#[test]
fn empty_and_zero() {
assert_eq!(fit_display_width("", 4), " ");
assert_eq!(fit_display_width("abc", 0), "");
}
}