1use std::default::Default;
4use std::mem;
5
6use beef::lean::Cow;
7use ratatui::style::{Color, Modifier, Style};
8use std::cmp::max;
9use vte::{Params, Perform};
10
11use crate::log_info;
12
13#[derive(Default)]
18pub struct ANSIParser {
19 partial_str: String,
20 last_style: Style,
21
22 stripped: String,
23 stripped_char_count: usize,
24 fragments: Vec<(Style, (u32, u32))>, }
26
27impl Perform for ANSIParser {
28 fn print(&mut self, ch: char) {
29 self.partial_str.push(ch);
30 }
31
32 fn execute(&mut self, byte: u8) {
33 match byte {
34 0x08 => {
36 self.partial_str.pop();
37 }
38 0x00 | 0x0d | 0x0A | 0x09 => self.partial_str.push(byte as char),
40 _ => log_info!("AnsiParser:execute ignored {:?}", byte),
42 }
43 }
44
45 fn hook(&mut self, params: &Params, _intermediates: &[u8], _ignore: bool, _action: char) {
46 log_info!("AnsiParser:hook ignored {:?}", params);
47 }
48
49 fn put(&mut self, byte: u8) {
50 log_info!("AnsiParser:put ignored {:?}", byte);
51 }
52
53 fn unhook(&mut self) {
54 log_info!("AnsiParser:unhook ignored");
55 }
56
57 fn osc_dispatch(&mut self, params: &[&[u8]], _bell_terminated: bool) {
58 log_info!("AnsiParser:osc ignored {:?}", params);
59 }
60
61 fn csi_dispatch(
62 &mut self,
63 params: &Params,
64 _intermediates: &[u8],
65 _ignore: bool,
66 action: char,
67 ) {
68 if action != 'm' {
72 log_info!("ignore: params: {:?}, action : {:?}", params, action);
73 return;
74 }
75
76 let mut style = if params.is_empty() {
78 Style::default()
79 } else {
80 self.last_style
81 };
82
83 let mut iter = params.iter();
84 while let Some(code) = iter.next() {
85 match code[0] {
86 0 => style = Style::default(),
87 1 => style.add_modifier |= Modifier::BOLD,
88 2 => style.add_modifier |= !Modifier::BOLD,
89 4 => style.add_modifier |= Modifier::UNDERLINED,
90 5 => style.add_modifier |= Modifier::SLOW_BLINK,
91 7 => style.add_modifier |= Modifier::REVERSED,
92 num if (30..=37).contains(&num) => {
93 style.fg = Some(Color::Indexed((num - 30) as u8));
94 }
95 38 => match iter.next() {
96 Some(&[2]) => {
97 let (r, g, b) = match (iter.next(), iter.next(), iter.next()) {
99 (Some(r), Some(g), Some(b)) => (r[0] as u8, g[0] as u8, b[0] as u8),
100 _ => {
101 log_info!("ignore CSI {:?} m", params);
102 continue;
103 }
104 };
105
106 style.fg = Some(Color::Rgb(r, g, b));
107 }
108 Some(&[5]) => {
109 let color = match iter.next() {
111 Some(color) => color[0] as u8,
112 None => {
113 log_info!("ignore CSI {:?} m", params);
114 continue;
115 }
116 };
117
118 style.fg = Some(Color::Indexed(color));
119 }
120 _ => {
121 log_info!("error on parsing CSI {:?} m", params);
122 }
123 },
124 39 => style.fg = Some(Color::Black),
125 num if (40..=47).contains(&num) => {
126 style.bg = Some(Color::Indexed((num - 40) as u8));
127 }
128 48 => match iter.next() {
129 Some(&[2]) => {
130 let (r, g, b) = match (iter.next(), iter.next(), iter.next()) {
132 (Some(r), Some(g), Some(b)) => (r[0] as u8, g[0] as u8, b[0] as u8),
133 _ => {
134 log_info!("ignore CSI {:?} m", params);
135 continue;
136 }
137 };
138
139 style.bg = Some(Color::Rgb(r, g, b));
140 }
141 Some(&[5]) => {
142 let color = match iter.next() {
144 Some(color) => color[0] as u8,
145 None => {
146 log_info!("ignore CSI {:?} m", params);
147 continue;
148 }
149 };
150
151 style.bg = Some(Color::Indexed(color));
152 }
153 _ => {
154 log_info!("ignore CSI {:?} m", params);
155 }
156 },
157 49 => style.bg = Some(Color::Rgb(0, 0, 0)),
158 _ => {
159 log_info!("ignore CSI {:?} m", params);
160 }
161 }
162 }
163
164 self.style_change(style);
165 }
166
167 fn esc_dispatch(&mut self, _intermediates: &[u8], _ignore: bool, _byte: u8) {
168 self.partial_str.push('"');
170 self.partial_str.push('[');
171 }
172}
173
174impl ANSIParser {
175 fn save_str(&mut self) {
177 if self.partial_str.is_empty() {
178 return;
179 }
180
181 let string = mem::take(&mut self.partial_str);
182 let string_char_count = string.chars().count();
183 self.fragments.push((
184 self.last_style,
185 (
186 self.stripped_char_count as u32,
187 (self.stripped_char_count + string_char_count) as u32,
188 ),
189 ));
190 self.stripped_char_count += string_char_count;
191 self.stripped.push_str(&string);
192 }
193
194 fn style_change(&mut self, new_style: Style) {
196 if new_style == self.last_style {
197 return;
198 }
199
200 self.save_str();
201 self.last_style = new_style;
202 }
203
204 pub fn parse_ansi(&mut self, text: &str) -> AnsiString<'static> {
205 let mut statemachine = vte::Parser::new();
206
207 for byte in text.as_bytes() {
208 statemachine.advance(self, *byte);
209 }
210 self.save_str();
211
212 let stripped = mem::take(&mut self.stripped);
213 self.stripped_char_count = 0;
214 let fragments = mem::take(&mut self.fragments);
215 AnsiString::new_string(stripped, fragments)
216 }
217}
218
219#[derive(Clone, Debug)]
223pub struct AnsiString<'a> {
224 stripped: Cow<'a, str>,
225 fragments: Option<Vec<(Style, (u32, u32))>>,
227}
228
229impl<'a> AnsiString<'a> {
230 pub fn new_empty() -> Self {
231 Self {
232 stripped: Cow::borrowed(""),
233 fragments: None,
234 }
235 }
236
237 fn new_raw_string(string: String) -> Self {
238 Self {
239 stripped: Cow::owned(string),
240 fragments: None,
241 }
242 }
243
244 fn new_raw_str(str_ref: &'a str) -> Self {
245 Self {
246 stripped: Cow::borrowed(str_ref),
247 fragments: None,
248 }
249 }
250
251 pub fn new_str(stripped: &'a str, fragments: Vec<(Style, (u32, u32))>) -> Self {
253 let fragments_empty =
254 fragments.is_empty() || (fragments.len() == 1 && fragments[0].0 == Style::default());
255 Self {
256 stripped: Cow::borrowed(stripped),
257 fragments: if fragments_empty {
258 None
259 } else {
260 Some(fragments)
261 },
262 }
263 }
264
265 pub fn new_string(stripped: String, fragments: Vec<(Style, (u32, u32))>) -> Self {
267 let fragments_empty =
268 fragments.is_empty() || (fragments.len() == 1 && fragments[0].0 == Style::default());
269 Self {
270 stripped: Cow::owned(stripped),
271 fragments: if fragments_empty {
272 None
273 } else {
274 Some(fragments)
275 },
276 }
277 }
278
279 pub fn parse(raw: &'a str) -> AnsiString<'static> {
280 ANSIParser::default().parse_ansi(raw)
281 }
282
283 #[inline]
284 pub fn is_empty(&self) -> bool {
285 self.stripped.is_empty()
286 }
287
288 #[inline]
289 pub fn into_inner(self) -> std::borrow::Cow<'a, str> {
290 std::borrow::Cow::Owned(self.stripped.into_owned())
291 }
292
293 pub fn iter(&'a self) -> Box<dyn Iterator<Item = (char, Style)> + 'a> {
294 if self.fragments.is_none() {
295 return Box::new(self.stripped.chars().map(|c| (c, Style::default())));
296 }
297
298 Box::new(AnsiStringIterator::new(
299 &self.stripped,
300 self.fragments.as_ref().unwrap(),
301 ))
302 }
303
304 pub fn has_styles(&self) -> bool {
305 self.fragments.is_some()
306 }
307
308 #[inline]
309 pub fn stripped(&self) -> &str {
310 &self.stripped
311 }
312
313 pub fn override_styles(&mut self, styles: Vec<(Style, (u32, u32))>) {
314 if styles.is_empty() {
315 } else if self.fragments.is_none() {
317 self.fragments = Some(styles);
318 } else {
319 let current_fragments = self.fragments.take().expect("unreachable");
320 let new_fragments = merge_fragments(¤t_fragments, &styles);
321 self.fragments.replace(new_fragments);
322 }
323 }
324}
325
326impl<'a> From<&'a str> for AnsiString<'a> {
327 fn from(s: &'a str) -> AnsiString<'a> {
328 AnsiString::new_raw_str(s)
329 }
330}
331
332impl From<String> for AnsiString<'static> {
333 fn from(s: String) -> Self {
334 AnsiString::new_raw_string(s)
335 }
336}
337
338impl<'a> From<(&'a str, &'a [usize], Style)> for AnsiString<'a> {
340 fn from((text, indices, style): (&'a str, &'a [usize], Style)) -> Self {
341 let fragments = indices
342 .iter()
343 .map(|&idx| (style, (idx as u32, 1 + idx as u32)))
344 .collect();
345 AnsiString::new_str(text, fragments)
346 }
347}
348
349pub struct AnsiStringIterator<'a> {
351 fragments: &'a [(Style, (u32, u32))],
352 fragment_idx: usize,
353 chars_iter: std::iter::Enumerate<std::str::Chars<'a>>,
354}
355
356impl<'a> AnsiStringIterator<'a> {
357 pub fn new(stripped: &'a str, fragments: &'a [(Style, (u32, u32))]) -> Self {
358 Self {
359 fragments,
360 fragment_idx: 0,
361 chars_iter: stripped.chars().enumerate(),
362 }
363 }
364}
365
366impl<'a> Iterator for AnsiStringIterator<'a> {
367 type Item = (char, Style);
368
369 fn next(&mut self) -> Option<Self::Item> {
370 match self.chars_iter.next() {
371 Some((char_idx, char)) => {
372 loop {
374 if self.fragment_idx >= self.fragments.len() {
375 break;
376 }
377
378 let (_style, (_start, end)) = self.fragments[self.fragment_idx];
379 if char_idx < (end as usize) {
380 break;
381 } else {
382 self.fragment_idx += 1;
383 }
384 }
385
386 let (style, (start, end)) = if self.fragment_idx >= self.fragments.len() {
387 (Style::default(), (char_idx as u32, 1 + char_idx as u32))
388 } else {
389 self.fragments[self.fragment_idx]
390 };
391
392 if (start as usize) <= char_idx && char_idx < (end as usize) {
393 Some((char, style))
394 } else {
395 Some((char, Style::default()))
396 }
397 }
398 None => None,
399 }
400 }
401}
402
403fn merge_fragments(
404 old: &[(Style, (u32, u32))],
405 new: &[(Style, (u32, u32))],
406) -> Vec<(Style, (u32, u32))> {
407 let mut ret = vec![];
408 let mut i = 0;
409 let mut j = 0;
410 let mut os = 0;
411
412 while i < old.len() && j < new.len() {
413 let (oa, (o_start, oe)) = old[i];
414 let (na, (ns, ne)) = new[j];
415 os = max(os, o_start);
416
417 if ns <= os && ne >= oe {
418 i += 1; } else if ns <= os {
422 ret.push((na, (ns, ne)));
425 os = ne;
426 j += 1;
427 } else if ns >= oe {
428 ret.push((oa, (os, oe)));
431 i += 1;
432 } else {
433 ret.push((oa, (os, ns)));
436 os = ns;
437 }
438 }
439
440 if i < old.len() {
441 for &(oa, (s, e)) in old[i..].iter() {
442 ret.push((oa, (max(os, s), e)))
443 }
444 }
445 if j < new.len() {
446 ret.extend_from_slice(&new[j..]);
447 }
448
449 ret
450}
451
452#[cfg(test)]
453mod tests {
454 use super::*;
455
456 #[test]
457 fn test_ansi_iterator() {
458 let input = "\x1B[48;2;5;10;15m\x1B[38;2;70;130;180mhi\x1B[0m";
459 let ansistring = ANSIParser::default().parse_ansi(input);
460 let mut it = ansistring.iter();
461 let style = Style {
462 fg: Some(Color::Rgb(70, 130, 180)),
463 bg: Some(Color::Rgb(5, 10, 15)),
464 ..Style::default()
465 };
466
467 assert_eq!(Some(('h', style)), it.next());
468 assert_eq!(Some(('i', style)), it.next());
469 assert_eq!(None, it.next());
470 assert_eq!(ansistring.stripped(), "hi");
471 }
472
473 #[test]
474 fn test_highlight_indices() {
475 let text = "abc";
476 let indices: Vec<usize> = vec![1];
477 let style = Style {
478 fg: Some(Color::Rgb(70, 130, 180)),
479 bg: Some(Color::Rgb(5, 10, 15)),
480 ..Style::default()
481 };
482
483 let ansistring = AnsiString::from((text, &indices as &[usize], style));
484 let mut it = ansistring.iter();
485
486 assert_eq!(Some(('a', Style::default())), it.next());
487 assert_eq!(Some(('b', style)), it.next());
488 assert_eq!(Some(('c', Style::default())), it.next());
489 assert_eq!(None, it.next());
490 }
491
492 #[test]
493 fn test_normal_string() {
494 let input = "ab";
495 let ansistring = ANSIParser::default().parse_ansi(input);
496
497 assert!(!ansistring.has_styles());
498
499 let mut it = ansistring.iter();
500 assert_eq!(Some(('a', Style::default())), it.next());
501 assert_eq!(Some(('b', Style::default())), it.next());
502 assert_eq!(None, it.next());
503
504 assert_eq!(ansistring.stripped(), "ab");
505 }
506
507 #[test]
508 fn test_multiple_styleibutes() {
509 let input = "\x1B[1;31mhi";
510 let ansistring = ANSIParser::default().parse_ansi(input);
511 let mut it = ansistring.iter();
512 let style = Style {
513 fg: Some(Color::Black),
514 add_modifier: Modifier::BOLD,
515 ..Style::default()
516 };
517
518 assert_eq!(Some(('h', style)), it.next());
519 assert_eq!(Some(('i', style)), it.next());
520 assert_eq!(None, it.next());
521 assert_eq!(ansistring.stripped(), "hi");
522 }
523
524 #[test]
525 fn test_reset() {
526 let input = "\x1B[35mA\x1B[mB";
527 let ansistring = ANSIParser::default().parse_ansi(input);
528 assert_eq!(ansistring.fragments.as_ref().map(|x| x.len()).unwrap(), 2);
529 assert_eq!(ansistring.stripped(), "AB");
530 }
531
532 #[test]
533 fn test_multi_bytes() {
534 let input = "中`\x1B[0m\x1B[1m\x1B[31mXYZ\x1B[0ms`";
535 let ansistring = ANSIParser::default().parse_ansi(input);
536 let mut it = ansistring.iter();
537 let default_style = Style::default();
538 let annotated = Style {
539 fg: Some(Color::Black),
540 add_modifier: Modifier::BOLD,
541 ..default_style
542 };
543
544 assert_eq!(Some(('中', default_style)), it.next());
545 assert_eq!(Some(('`', default_style)), it.next());
546 assert_eq!(Some(('X', annotated)), it.next());
547 assert_eq!(Some(('Y', annotated)), it.next());
548 assert_eq!(Some(('Z', annotated)), it.next());
549 assert_eq!(Some(('s', default_style)), it.next());
550 assert_eq!(Some(('`', default_style)), it.next());
551 assert_eq!(None, it.next());
552 }
553
554 #[test]
555 fn test_merge_fragments() {
556 let ao = Style::default();
557 let an = Style::default().bg(Color::Blue);
558
559 assert_eq!(
560 merge_fragments(&[(ao, (0, 1)), (ao, (1, 2))], &[]),
561 vec![(ao, (0, 1)), (ao, (1, 2))]
562 );
563
564 assert_eq!(
565 merge_fragments(&[], &[(an, (0, 1)), (an, (1, 2))]),
566 vec![(an, (0, 1)), (an, (1, 2))]
567 );
568
569 assert_eq!(
570 merge_fragments(
571 &[(ao, (1, 3)), (ao, (5, 6)), (ao, (9, 10))],
572 &[(an, (0, 1))]
573 ),
574 vec![(an, (0, 1)), (ao, (1, 3)), (ao, (5, 6)), (ao, (9, 10))]
575 );
576
577 assert_eq!(
578 merge_fragments(
579 &[(ao, (1, 3)), (ao, (5, 7)), (ao, (9, 11))],
580 &[(an, (0, 2))]
581 ),
582 vec![(an, (0, 2)), (ao, (2, 3)), (ao, (5, 7)), (ao, (9, 11))]
583 );
584
585 assert_eq!(
586 merge_fragments(
587 &[(ao, (1, 3)), (ao, (5, 7)), (ao, (9, 11))],
588 &[(an, (0, 3))]
589 ),
590 vec![(an, (0, 3)), (ao, (5, 7)), (ao, (9, 11))]
591 );
592
593 assert_eq!(
594 merge_fragments(
595 &[(ao, (1, 3)), (ao, (5, 7)), (ao, (9, 11))],
596 &[(an, (0, 6)), (an, (6, 7))]
597 ),
598 vec![(an, (0, 6)), (an, (6, 7)), (ao, (9, 11))]
599 );
600
601 assert_eq!(
602 merge_fragments(
603 &[(ao, (1, 3)), (ao, (5, 7)), (ao, (9, 11))],
604 &[(an, (1, 2))]
605 ),
606 vec![(an, (1, 2)), (ao, (2, 3)), (ao, (5, 7)), (ao, (9, 11))]
607 );
608
609 assert_eq!(
610 merge_fragments(
611 &[(ao, (1, 3)), (ao, (5, 7)), (ao, (9, 11))],
612 &[(an, (1, 3))]
613 ),
614 vec![(an, (1, 3)), (ao, (5, 7)), (ao, (9, 11))]
615 );
616
617 assert_eq!(
618 merge_fragments(
619 &[(ao, (1, 3)), (ao, (5, 7)), (ao, (9, 11))],
620 &[(an, (1, 4))]
621 ),
622 vec![(an, (1, 4)), (ao, (5, 7)), (ao, (9, 11))]
623 );
624
625 assert_eq!(
626 merge_fragments(
627 &[(ao, (1, 3)), (ao, (5, 7)), (ao, (9, 11))],
628 &[(an, (2, 3))]
629 ),
630 vec![(ao, (1, 2)), (an, (2, 3)), (ao, (5, 7)), (ao, (9, 11))]
631 );
632
633 assert_eq!(
634 merge_fragments(
635 &[(ao, (1, 3)), (ao, (5, 7)), (ao, (9, 11))],
636 &[(an, (2, 4))]
637 ),
638 vec![(ao, (1, 2)), (an, (2, 4)), (ao, (5, 7)), (ao, (9, 11))]
639 );
640
641 assert_eq!(
642 merge_fragments(
643 &[(ao, (1, 3)), (ao, (5, 7)), (ao, (9, 11))],
644 &[(an, (2, 6))]
645 ),
646 vec![(ao, (1, 2)), (an, (2, 6)), (ao, (6, 7)), (ao, (9, 11))]
647 );
648 }
649
650 #[test]
651 fn test_multi_byte_359() {
652 let highlight = Style::default().add_modifier(Modifier::BOLD);
654 let ansistring = AnsiString::new_str("ああa", vec![(highlight, (2, 3))]);
655 let mut it = ansistring.iter();
656 assert_eq!(Some(('あ', Style::default())), it.next());
657 assert_eq!(Some(('あ', Style::default())), it.next());
658 assert_eq!(Some(('a', highlight)), it.next());
659 assert_eq!(None, it.next());
660 }
661}