use unicode_width::UnicodeWidthStr;
pub fn display_width(s: &str) -> usize {
let mut result = String::new();
let mut chars = s.chars().peekable();
while let Some(ch) = chars.next() {
if ch == '\x1b' {
if chars.peek() == Some(&'[') {
chars.next(); while let Some(&next_ch) = chars.peek() {
chars.next();
if next_ch.is_ascii_alphabetic() {
break;
}
}
} else {
result.push(ch);
}
} else {
result.push(ch);
}
}
result.width()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_display_width_ascii() {
assert_eq!(display_width("hello"), 5);
assert_eq!(display_width("test"), 4);
assert_eq!(display_width(""), 0);
}
#[test]
fn test_display_width_ansi_codes() {
assert_eq!(display_width("\x1b[31mred\x1b[0m"), 3);
assert_eq!(display_width("\x1b[1mbold\x1b[0m"), 4);
assert_eq!(display_width("\x1b[38;2;255;0;0mRGB red\x1b[0m"), 7);
}
#[test]
fn test_display_width_unicode() {
assert_eq!(display_width("→"), 1);
assert_eq!(display_width("café"), 4);
assert_eq!(display_width("⚡"), 2);
assert_eq!(display_width("😀"), 2);
}
#[test]
fn test_display_width_mixed() {
assert_eq!(
display_width("\x1b[32m→ CONSEC\x1b[0m"),
8 );
assert_eq!(
display_width("\x1b[31m⚡ REPEAT\x1b[0m"),
9 );
}
}