1mod search_tree;
2mod span;
3use super::{
4 BoundedWidth, Expandable, HasWidth, Joinable, Paintable, Pushable, RawText, Replaceable,
5 Sliceable, Width,
6};
7
8use regex::{Captures, Regex, Replacer};
9use search_tree::SearchTree;
10pub use span::Span;
11use std::borrow::{Borrow, Cow};
12use std::fmt;
13use std::iter::{once, repeat, FromIterator};
14use std::ops::{Deref, RangeBounds};
15#[derive(Clone, Debug)]
19pub struct Spans<T> {
20 content: String,
21 spans: SearchTree<T>,
23}
24
25impl<T> Default for Spans<T> {
26 fn default() -> Self {
27 Self {
28 content: String::new(),
29 spans: Default::default(),
30 }
31 }
32}
33
34impl<T: PartialEq> Eq for Spans<T> {}
35
36impl<T: PartialEq> PartialEq for Spans<T> {
37 fn eq(&self, other: &Spans<T>) -> bool {
38 self.content == other.content && self.spans == other.spans
39 }
40}
41
42impl<T> Spans<T> {
43 #[allow(clippy::type_complexity)]
44 fn segments(
45 &self,
46 ) -> Box<dyn Iterator<Item = ((&usize, Cow<'_, T>), Option<(&usize, Cow<'_, T>)>)> + '_>
47 where
48 T: Clone + Default,
49 {
50 if self.spans.contains_key(0) {
51 Box::new(
52 self.spans
53 .iter()
54 .map(|(key, val)| (key, Cow::Borrowed(val)))
55 .zip(
56 self.spans
57 .iter()
58 .map(|(key, val)| (key, Cow::Borrowed(val)))
59 .map(Some)
60 .skip(1)
61 .chain(repeat(None)),
62 ),
63 )
64 } else {
65 Box::new(
66 once((&0, Cow::Owned(Default::default())))
67 .chain(
68 self.spans
69 .iter()
70 .map(|(key, val)| (key, Cow::Borrowed(val))),
71 )
72 .zip(
73 self.spans
74 .iter()
75 .map(|(key, val)| (key, Cow::Borrowed(val)))
76 .map(Some)
77 .chain(repeat(None)),
78 ),
79 )
80 }
81 }
82 pub fn spans(&self) -> impl Iterator<Item = Span<'_, T>>
84 where
85 T: Clone + Default,
86 {
87 self.segments()
88 .filter_map(move |((first_key, style), second)| {
89 let second_key = if let Some((second_key, _)) = second {
90 *second_key
91 } else {
92 self.content.len()
93 };
94 #[allow(clippy::manual_map)]
95 if let Some(ref s) = self.content.get(*first_key..second_key) {
96 Some(Span::new(style, Cow::Borrowed(s)))
97 } else {
98 None
101 }
102 })
103 }
104 fn trim(&mut self) {
105 self.spans.trim(self.content.len().saturating_sub(1));
106 }
107}
108
109impl<T: Clone + PartialEq> Pushable<Spans<T>> for Spans<T> {
110 fn push(&mut self, other: &Spans<T>) {
111 self.spans
114 .copy_with_shift(&other.spans, .., self.content.len())
115 .unwrap();
116 self.content.push_str(&other.content);
117 self.trim();
118 }
119}
120
121impl<'a, T: Clone + PartialEq> Pushable<Span<'a, T>> for Spans<T> {
122 fn push(&mut self, other: &Span<'a, T>) {
123 self.spans
124 .insert(self.content.len(), other.style().clone().into_owned());
125 self.content.push_str(other.raw_ref());
126 self.spans.dedup();
127 self.trim();
128 }
129}
130
131impl<T> Pushable<&str> for Spans<T> {
132 fn push(&mut self, other: &&str) {
133 self.content.push_str(other);
134 }
135}
136
137impl<T> Pushable<str> for Spans<T> {
138 fn push(&mut self, other: &str) {
139 self.content.push_str(other);
140 }
141}
142
143impl<T: Default + Clone + PartialEq> Expandable for Spans<T> {
144 fn expand(&self, capture: &Captures) -> Self {
145 let mut result: Spans<T> = Default::default();
146 for span in self.spans() {
147 let mut dst = String::new();
148 capture.expand(span.raw_ref(), &mut dst);
149 let new_span = Span::<T>::borrowed(&span.style(), &dst);
150 result.push(&new_span);
151 }
152 result
153 }
154}
155
156impl<'a, T: Clone + PartialEq> Replaceable<&'a str> for Spans<T> {
159 fn replace(&self, from: &str, replacer: &'a str) -> Self {
160 let mut result = Spans {
161 content: String::new(),
162 spans: SearchTree::new(),
163 };
164
165 let mut last_end = 0;
166 for (start, part) in self.content.match_indices(from) {
167 if let Some(spans) = self.slice(last_end..start) {
168 result.push(&spans);
169 if let Some(mut r) = self.slice(start..start + part.len()) {
170 r.content = String::from(replacer);
171 result.push(&r);
172 }
173 }
174 last_end = start + part.len();
175 }
176 if let Some(spans) = self.slice(last_end..) {
177 result.push(&spans);
178 }
179 result.trim();
180 result
181 }
182 fn replace_regex(&self, searcher: &Regex, replacer: &'a str) -> Self {
183 let mut last_end = 0;
184 let mut result = Spans {
185 content: String::new(),
186 spans: SearchTree::new(),
187 };
188 let captures = searcher.captures_iter(&self.content);
189 for capture in captures {
190 let mat = capture
191 .get(0)
192 .expect("Captures are always supposed to have one match");
193 if let Some(spans) = self.slice(last_end..mat.start()) {
194 result.push(&spans);
195 if let Some(mut r) = self.slice(mat.start()..mat.end()) {
196 let mut new = String::new();
197 String::from(replacer).replace_append(&capture, &mut new);
198 r.content = new;
199 result.push(&r);
200 }
201 last_end = mat.end();
202 }
203 }
204 if let Some(spans) = self.slice(last_end..) {
205 result.push(&spans);
206 }
207 result.trim();
208 result
209 }
210}
211
212impl<'a, T: Clone> Sliceable for Spans<T> {
213 fn slice<R>(&self, range: R) -> Option<Self>
214 where
215 R: RangeBounds<usize> + Clone,
216 {
217 let string = self.content.deref().slice(range.clone());
218 if self.spans.is_empty() {
219 if let Some(string) = string {
220 return Some(Spans {
221 content: string.to_string(),
222 spans: SearchTree::new(),
223 });
224 }
225 }
226 let spans = self.spans.slice(range);
227 if let (Some(string), Some(spans)) = (string, spans) {
228 Some(Spans {
229 content: string.to_string(),
230 spans,
231 })
232 } else {
233 None
234 }
235 }
236}
237
238impl<'a, T, U> FromIterator<U> for Spans<T>
239where
240 T: Clone + PartialEq + 'a,
241 U: Borrow<Spans<T>> + 'a,
242{
243 fn from_iter<I>(iter: I) -> Spans<T>
244 where
245 I: IntoIterator<Item = U>,
246 {
247 let mut result: Spans<T> = Default::default();
248 for span in iter {
249 result.push(span.borrow());
250 }
251 result.spans.dedup();
252 result
253 }
254}
255
256impl<'a, T> FromIterator<Span<'a, T>> for Spans<T>
257where
258 T: Clone + PartialEq + 'a,
259{
260 fn from_iter<I>(iter: I) -> Spans<T>
261 where
262 I: IntoIterator<Item = Span<'a, T>>,
263 {
264 let mut result: Spans<T> = Default::default();
265 for span in iter {
266 result.push(&span);
267 }
268 result.spans.dedup();
269 result
270 }
271}
272
273impl<T> RawText for Spans<T> {
274 fn raw(&self) -> String {
275 self.content.clone()
276 }
277 fn raw_ref<'a>(&self) -> &str {
278 &self.content
279 }
280}
281
282impl<T> From<&str> for Spans<T>
283where
284 T: Clone + Default + PartialEq,
285{
286 fn from(other: &str) -> Spans<T> {
287 let mut spans: SearchTree<_> = Default::default();
288 spans.insert(0, Default::default());
289 Spans {
290 content: String::from(other),
291 spans,
292 }
293 }
294}
295
296impl<'a, T: Paintable + Clone + Default> fmt::Display for Spans<T> {
297 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
298 T::paint_many(self.spans().map(|span| (span.style().clone(), span.raw()))).fmt(fmt)
299 }
300}
301
302impl<T> BoundedWidth for Spans<T> {
303 fn bounded_width(&self) -> usize {
304 self.content.bounded_width()
305 }
306}
307
308impl<T> HasWidth for Spans<T> {
309 fn width(&self) -> Width {
310 Width::Bounded(self.bounded_width())
311 }
312}
313
314impl<T: PartialEq + Clone> Joinable<Spans<T>> for Spans<T> {
315 type Output = Spans<T>;
316 fn join(&self, other: &Spans<T>) -> Self::Output {
317 let mut result: Spans<T> = Default::default();
318 result.push(self);
319 result.push(other);
320 result.trim();
321 result
322 }
323}
324
325impl<T: PartialEq + Clone> Joinable<Span<'_, T>> for Spans<T> {
326 type Output = Spans<T>;
327 fn join(&self, other: &Span<'_, T>) -> Self::Output {
328 let mut result: Spans<T> = Default::default();
329 result.push(self);
330 result.push(other);
331 result
332 }
333}
334
335#[cfg(test)]
336mod test {
337 use super::*;
338 use crate::text::{Sliceable, Split, Splitable, WidthSliceable};
339 use ansi_term::{ANSIString, ANSIStrings, Color, Style};
340 fn strings_to_spans(strings: &[ANSIString<'_>]) -> Spans<Style> {
341 strings.iter().map(Span::<Style>::from).collect()
342 }
343 fn string_to_spans(string: &ANSIString<'_>) -> Spans<Style> {
344 let span = Span::<Style>::from(string);
345 let mut spans: Spans<Style> = Default::default();
346 spans.push(&span);
347 spans
348 }
349 #[test]
350 fn test_slice_width_easy() {
351 let text = strings_to_spans(&[Color::Green.paint("foo")]);
352 let actual = text.slice_width(..2).unwrap();
353 let expected = strings_to_spans(&[Color::Green.paint("fo")]);
354 assert_eq!(expected, actual);
355 }
356 #[test]
357 fn test_slice_width_left_hard() {
358 let text = strings_to_spans(&[Color::Green.paint("👱👱👱")]);
359 let actual = text.slice_width(..3).unwrap();
360 let expected = strings_to_spans(&[Color::Green.paint("👱")]);
361 assert_eq!(expected, actual);
362 let actual = text.slice_width(..4).unwrap();
363 let expected = strings_to_spans(&[Color::Green.paint("👱👱")]);
364 assert_eq!(expected, actual);
365 }
366 #[test]
367 fn test_finite_width() {
368 let text = strings_to_spans(&[Color::Green.paint("foo")]);
369 let expected = 3;
370 let actual = text.bounded_width();
371 assert_eq!(expected, actual);
372 }
373 #[test]
374 fn build_span() {
375 let from = Color::Green.paint("foo");
376 let to = string_to_spans(&from);
377 let expected = format!("{}", from);
378 let actual = format!("{}", to);
379 assert_eq!(expected, actual);
380 }
381 #[test]
382 fn build_spans() {
383 let texts = [
384 Color::Red.paint("a"),
385 Color::Blue.paint("b"),
386 Color::Blue.paint("⛇"),
387 ];
388 let text = strings_to_spans(&texts);
389 let string = ANSIStrings(&texts);
390 let strings = text.spans().map(ANSIString::from).collect::<Vec<_>>();
391 let output = ANSIStrings(&strings);
392 let expected = format!("{}", string);
393 let actual = format!("{}", output);
394 assert_eq!(expected, actual);
395 }
396 #[test]
397 fn simple_replace() {
398 let text = strings_to_spans(&[Color::Red.paint("foo")]);
399 let actual = text.replace("foo", "bar");
400 let expected = strings_to_spans(&[Color::Red.paint("bar")]);
401 assert_eq!(expected, actual);
402 }
403 #[test]
404 fn replace_in_span() {
405 let text = strings_to_spans(&[Color::Red.paint("Bob "), Color::Blue.paint("Dylan")]);
406 let new_text = text.replace("Bob", "Robert");
407 let target_text =
408 strings_to_spans(&[Color::Red.paint("Robert "), Color::Blue.paint("Dylan")]);
409 assert_eq!(new_text, target_text);
410 }
411 #[test]
412 fn replace_chars() {
413 let text = strings_to_spans(&[
414 Color::Blue.paint("what"),
415 Color::Red.paint("//\\/;,!"),
416 Color::Blue.paint("the fudge"),
417 ]);
418 let new_text = text.replace("/", "/");
419 let target_text = strings_to_spans(&[
420 Color::Blue.paint("what"),
421 Color::Red.paint("//\\/;,!"),
422 Color::Blue.paint("the fudge"),
423 ]);
424 assert_eq!(new_text, target_text);
425 }
426 #[test]
427 fn replace_across_span_simple_2() {
428 let text = strings_to_spans(&[
429 Color::Red.paint("Here is some f"),
430 Color::Blue.paint("oo foo fo"),
431 Color::Green.paint("o"),
432 ]);
433 let new_text = text.replace("foo", "bar");
434 let target_text = strings_to_spans(&[
435 Color::Red.paint("Here is some b"),
436 Color::Blue.paint("ar bar ba"),
437 Color::Green.paint("r"),
438 ]);
439 assert_eq!(new_text, target_text);
440 }
441 #[test]
442 fn simple_regex_replace() {
443 let text = strings_to_spans(&[Color::Red.paint("foooo")]);
444 let new_text = text.replace_regex(&Regex::new("fo+").unwrap(), "bar");
445 let target_text = strings_to_spans(&[Color::Red.paint("bar")]);
446
447 assert_eq!(new_text, target_text);
448 }
449 #[test]
450 fn replace_regex_across_span_simple_trival() {
451 let text = strings_to_spans(&[Color::Red.paint("Here lies "), Color::Blue.paint("Beavis")]);
452 let new_text = text.replace_regex(
453 &Regex::new(r"(Here lies) Beavis").unwrap(),
454 "Here lies Butthead",
455 );
456 let target_text = strings_to_spans(&[
457 Color::Red.paint("Here lies "),
458 Color::Blue.paint("Butthead"),
459 ]);
460 assert_eq!(new_text, target_text);
461 }
462 #[test]
463 fn replace_regex_across_span_simple_backref() {
464 let text = strings_to_spans(&[Color::Red.paint("Here lies "), Color::Blue.paint("Beavis")]);
465 let new_text =
466 text.replace_regex(&Regex::new(r"(Here lies) Beavis").unwrap(), "$1 Butthead");
467 let target_text = strings_to_spans(&[
468 Color::Red.paint("Here lies "),
469 Color::Blue.paint("Butthead"),
470 ]);
471 assert_eq!(new_text, target_text);
472 }
473 #[test]
474 fn replace_regex_across_span_simple_2_backref() {
475 let text = strings_to_spans(&[
476 Color::Red.paint("Here is some f"),
477 Color::Blue.paint("ooo fuuu f"),
478 Color::Green.paint("aaa"),
479 ]);
480 let new_text = text.replace_regex(&Regex::new("f(([aeiou])+)").unwrap(), "b${2}r");
481 let target_text = strings_to_spans(&[
482 Color::Red.paint("Here is some b"),
483 Color::Blue.paint("or bur b"),
484 Color::Green.paint("ar"),
485 ]);
486 println!("expected: {}", target_text);
487 println!("actual: {}", new_text);
488 assert_eq!(new_text, target_text);
489 }
490 #[test]
491 fn replace_regex_across_span_simple_2_trivial() {
492 let text = strings_to_spans(&[
493 Color::Red.paint("Here is some f"),
494 Color::Blue.paint("ooo fuuu f"),
495 Color::Green.paint("aaa"),
496 ]);
497 let new_text = text.replace_regex(&Regex::new("f(([aeiou])+)").unwrap(), "bar");
498 let target_text = strings_to_spans(&[
499 Color::Red.paint("Here is some b"),
500 Color::Blue.paint("ar bar b"),
501 Color::Green.paint("ar"),
502 ]);
503 println!("expected: {}", target_text);
504 println!("actual: {}", new_text);
505 assert_eq!(new_text, target_text);
506 }
507 #[test]
508 fn replace_regex_empty() {
509 let text = strings_to_spans(&[
510 Color::Red.paint("Here is some f"),
511 Color::Blue.paint("ooo fuuu f"),
512 Color::Green.paint("aaa"),
513 ]);
514 let new_text = text.replace_regex(&Regex::new("quux").unwrap(), "bar");
515 assert_eq!(new_text, text);
516 }
517 #[test]
518 fn replace_regex_empty_fancy() {
519 let text = strings_to_spans(&[
520 Color::Red.paint("Here is some f"),
521 Color::Blue.paint("ooo fuuu f"),
522 Color::Green.paint("aaa"),
523 ]);
524 let new_text = text.replace_regex(&Regex::new("([zyx])").unwrap(), "missing $1 letters");
525 assert_eq!(new_text, text);
526 }
527 #[test]
528 fn replace_regex_styled_easy() {
529 let text = strings_to_spans(&[
530 Color::Red.paint("Foo"),
531 Color::Blue.paint("Bar"),
532 Color::Green.paint("Baz"),
533 ]);
534 let replacement = strings_to_spans(&[Color::Cyan.paint("Quux")]);
535 let actual = text.replace_regex(&Regex::new("Bar").unwrap(), &replacement);
536 let expected = strings_to_spans(&[
537 Color::Red.paint("Foo"),
538 Color::Cyan.paint("Quux"),
539 Color::Green.paint("Baz"),
540 ]);
541 assert_eq!(expected, actual);
542 }
543 #[test]
544 fn replace_regex_styled_complex() {
545 let text = strings_to_spans(&[
546 Color::Red.paint("555"),
547 Color::Black.paint("."),
548 Color::Blue.paint("444"),
549 Color::White.paint("."),
550 Color::Green.paint("3333"),
551 ]);
552 let replacement = strings_to_spans(&[
553 Color::Red.paint("$1"),
554 Color::Black.paint("-"),
555 Color::Green.paint("$2"),
556 Color::Black.paint("-"),
557 Color::Blue.paint("$3"),
558 ]);
559 let regex = Regex::new(r"(\d{3})[.-](\d{3})[.-](\d{4})").unwrap();
560 for name in regex.capture_names() {
561 println!("name: {:#?}", name);
562 }
563 println!("location: {:#?}", regex.capture_locations());
564 println!("len: {:#?}", regex.capture_locations().len());
565 let actual = text.replace_regex(®ex, &replacement);
566 let expected = strings_to_spans(&[
567 Color::Red.paint("555"),
568 Color::Black.paint("-"),
569 Color::Green.paint("444"),
570 Color::Black.paint("-"),
571 Color::Blue.paint("3333"),
572 ]);
573 println!("expected: {}", expected);
574 println!("actual: {}", actual);
575 assert_eq!(expected, actual);
576 }
577 #[test]
578 fn span() {
579 let texts = [
580 Color::Red.paint("Here is some f"),
581 Color::Blue.paint("ooo fuuu f"),
582 Color::Green.paint("aaa"),
583 ];
584 let text = strings_to_spans(&texts);
585 let span = text.spans().next().unwrap();
586 let expected = format!("{}", texts[0]);
587 let actual = format!("{}", span);
588 assert_eq!(expected, actual);
589 }
590 #[test]
591 fn raw() {
592 let text = strings_to_spans(&[
593 Color::Red.paint("Here is some f"),
594 Color::Blue.paint("ooo fuuu f"),
595 Color::Green.paint("aaa"),
596 ]);
597
598 let expected = String::from("Here is some fooo fuuu faaa");
599 let actual = text.raw();
600 assert_eq!(expected, actual);
601 }
602 #[test]
603 fn slice_start() {
604 let text = strings_to_spans(&[Color::Red.paint("01234"), Color::Blue.paint("56789")]);
605 let actual = text.slice(0..8).unwrap();
606 let expected = strings_to_spans(&[Color::Red.paint("01234"), Color::Blue.paint("567")]);
607
608 assert_eq!(expected, actual);
609 }
610 #[test]
611 fn slice_middle() {
612 let text = strings_to_spans(&[
613 Color::Red.paint("012"),
614 Color::Blue.paint("345"),
615 Color::Green.paint("678"),
616 ]);
617 let actual = text.slice(2..8).unwrap();
618 let expected = strings_to_spans(&[
619 Color::Red.paint("2"),
620 Color::Blue.paint("345"),
621 Color::Green.paint("67"),
622 ]);
623
624 assert_eq!(expected, actual);
625 }
626 #[test]
627 fn slice_end() {
628 let text = strings_to_spans(&[
629 Color::Red.paint("012"),
630 Color::Blue.paint("345"),
631 Color::Green.paint("678"),
632 ]);
633 let actual = text.slice(2..).unwrap();
634 let expected = strings_to_spans(&[
635 Color::Red.paint("2"),
636 Color::Blue.paint("345"),
637 Color::Green.paint("678"),
638 ]);
639
640 assert_eq!(expected, actual);
641 }
642 #[test]
643 fn slice_full() {
644 let text = strings_to_spans(&[
645 Color::Red.paint("012"),
646 Color::Blue.paint("345"),
647 Color::Green.paint("678"),
648 ]);
649 let actual = text.slice(..).unwrap();
650 let expected = strings_to_spans(&[
651 Color::Red.paint("012"),
652 Color::Blue.paint("345"),
653 Color::Green.paint("678"),
654 ]);
655
656 assert_eq!(expected, actual);
657 }
658 #[test]
659 fn split_outer() {
660 let texts = vec![
661 Color::Black.paint("::"),
662 Color::Red.paint("Some"),
663 Color::Blue.paint("::"),
664 Color::Green.paint("Random"),
665 Color::Cyan.paint("::"),
666 Color::White.paint("Place"),
667 Color::Yellow.paint("::"),
668 ];
669 let spans = strings_to_spans(&texts);
670 let actual = spans.split("::").collect::<Vec<_>>();
671 let expected = vec![
672 Split {
673 segment: None,
674 delim: Some(string_to_spans(&texts[0])),
675 },
676 Split {
677 segment: Some(string_to_spans(&texts[1])),
678 delim: Some(string_to_spans(&texts[2])),
679 },
680 Split {
681 segment: Some(string_to_spans(&texts[3])),
682 delim: Some(string_to_spans(&texts[4])),
683 },
684 Split {
685 segment: Some(string_to_spans(&texts[5])),
686 delim: Some(string_to_spans(&texts[6])),
687 },
688 ];
689 assert_eq!(expected, actual);
690 }
691 #[test]
692 fn split_inner() {
693 let texts = vec![
694 Color::Red.paint("Some"),
695 Color::Blue.paint("::"),
696 Color::Green.paint("Random"),
697 Color::Cyan.paint("::"),
698 Color::White.paint("Place"),
699 ];
700 let spans = strings_to_spans(&texts);
701 let actual = spans.split("::").collect::<Vec<_>>();
702 let expected = vec![
703 Split {
704 segment: Some(string_to_spans(&texts[0])),
705 delim: Some(string_to_spans(&texts[1])),
706 },
707 Split {
708 segment: Some(string_to_spans(&texts[2])),
709 delim: Some(string_to_spans(&texts[3])),
710 },
711 Split {
712 segment: Some(string_to_spans(&texts[4])),
713 delim: None,
714 },
715 ];
716 assert_eq!(expected, actual);
717 }
718}