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
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