feat(utils): add visible_len function for ANSI strings
Swadhin Biswas committed 7 months ago
ceae050
1 parent d2edc4a
commit ceae050
1 file changed
+50 -0
Showing 1 changed file+50-0
▼src/utils/mod.rs
+50-0
| @@ -98,3 +98,53 @@ pub fn strip_ansi(s: &str) -> String { | ||
98 | 98 | chars.next(); |
99 | 99 | if c == '(' || c == ')' { |
100 | 100 | chars.next(); // consume charset designator |
101 | + } | |
102 | + } | |
103 | + _ => {} // Lone ESC, skip it | |
104 | + } | |
105 | + } else { | |
106 | + result.push(ch); | |
107 | + } | |
108 | + } | |
109 | + result | |
110 | +} | |
111 | + | |
112 | +/// Calculate the visible width of a string (strip ANSI, use unicode-width). | |
113 | +pub fn visible_width(s: &str) -> usize { | |
114 | + use unicode_width::UnicodeWidthStr; | |
115 | + let stripped = strip_ansi(s); | |
116 | + UnicodeWidthStr::width(stripped.as_str()) | |
117 | +} | |
118 | + | |
119 | +/// Pad a string to a given visible width with spaces on the right. | |
120 | +pub fn pad_right(s: &str, target_width: usize) -> String { | |
121 | + let current = visible_width(s); | |
122 | + if current >= target_width { | |
123 | + s.to_string() | |
124 | + } else { | |
125 | + format!("{}{}", s, " ".repeat(target_width - current)) | |
126 | + } | |
127 | +} | |
128 | + | |
129 | +/// Truncate a string (which may contain ANSI escapes) so its visible width | |
130 | +/// does not exceed `max_width`. Preserves ANSI sequences but cuts visible chars. | |
131 | +pub fn truncate_to_width(s: &str, max_width: usize) -> String { | |
132 | + use unicode_width::UnicodeWidthChar; | |
133 | + let mut result = String::with_capacity(s.len()); | |
134 | + let mut vis_w: usize = 0; | |
135 | + let mut chars = s.chars().peekable(); | |
136 | + while let Some(ch) = chars.next() { | |
137 | + if ch == '\x1b' { | |
138 | + // Pass through the entire escape sequence | |
139 | + result.push(ch); | |
140 | + match chars.peek() { | |
141 | + Some(&'[') => { | |
142 | + result.push(chars.next().unwrap()); | |
143 | + while let Some(&c) = chars.peek() { | |
144 | + result.push(chars.next().unwrap()); | |
145 | + if c.is_ascii_alphabetic() { | |
146 | + break; | |
147 | + } | |
148 | + } | |
149 | + } | |
150 | + Some(&']') | Some(&'_') | Some(&'^') => { | |
101 | 151 | |