1use std::borrow::Cow;
6use std::iter::FromIterator;
7use unicode_width::UnicodeWidthStr;
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash)]
13pub struct SpannedString<T> {
14 source: String,
15 spans: Vec<IndexedSpan<T>>,
16}
17
18#[derive(Debug, PartialEq, Eq, Hash)]
20pub struct SpannedStr<'a, T> {
21 source: &'a str,
22 spans: &'a [IndexedSpan<T>],
23}
24
25pub trait SpannedText {
31 type S: AsRef<IndexedCow>;
35
36 fn source(&self) -> &str;
38
39 fn spans(&self) -> &[Self::S];
41
42 fn as_ref(&self) -> SpannedTextRef<'_, Self> {
44 SpannedTextRef { r: self }
45 }
46}
47
48pub struct SpannedTextRef<'a, C>
50where
51 C: SpannedText + ?Sized,
52{
53 r: &'a C,
54}
55
56impl<T> Default for SpannedString<T> {
57 fn default() -> Self {
58 SpannedString::new()
59 }
60}
61
62impl<'a, T> SpannedText for &'a SpannedString<T> {
63 type S = IndexedSpan<T>;
64
65 fn source(&self) -> &str {
66 &self.source
67 }
68
69 fn spans(&self) -> &[IndexedSpan<T>] {
70 &self.spans
71 }
72}
73
74impl<'a, C> SpannedText for SpannedTextRef<'a, C>
75where
76 C: 'a + SpannedText + ?Sized,
77{
78 type S = C::S;
79
80 fn source(&self) -> &str {
81 self.r.source()
82 }
83
84 fn spans(&self) -> &[C::S] {
85 self.r.spans()
86 }
87}
88
89impl<'a, T> SpannedText for SpannedStr<'a, T>
90where
91 T: 'a,
92{
93 type S = IndexedSpan<T>;
94
95 fn source(&self) -> &str {
96 self.source
97 }
98
99 fn spans(&self) -> &[IndexedSpan<T>] {
100 self.spans
101 }
102}
103
104impl<S, T> From<S> for SpannedString<T>
105where
106 S: Into<String>,
107 T: Default,
108{
109 fn from(value: S) -> Self {
110 Self::single_span(value.into(), T::default())
111 }
112}
113
114impl<'a, T> SpannedStr<'a, T>
115where
116 T: 'a,
117{
118 pub const fn new(source: &'a str, spans: &'a [IndexedSpan<T>]) -> Self {
120 SpannedStr { source, spans }
121 }
122
123 pub const fn empty() -> Self {
125 Self::new("", &[])
126 }
127
128 pub fn spans<'b>(
130 &'b self,
131 ) -> impl DoubleEndedIterator<Item = Span<'a, T>> + ExactSizeIterator<Item = Span<'a, T>> + 'b
132 where
133 'a: 'b,
134 {
135 let source = self.source;
136 self.spans.iter().map(move |span| span.resolve(source))
137 }
138
139 pub const fn spans_raw(&self) -> &'a [IndexedSpan<T>] {
141 self.spans
142 }
143
144 pub const fn source(&self) -> &'a str {
146 self.source
147 }
148
149 pub const fn is_empty(&self) -> bool {
153 self.source.is_empty() || self.spans.is_empty()
154 }
155
156 pub fn width(&self) -> usize {
160 self.spans().map(|s| s.width).sum()
161 }
162
163 pub fn from_spanned_text<'b, S>(text: &'b S) -> Self
165 where
166 S: SpannedText<S = IndexedSpan<T>>,
167 'b: 'a,
168 {
169 Self {
170 source: text.source(),
171 spans: text.spans(),
172 }
173 }
174}
175
176impl<'a, T> Clone for SpannedStr<'a, T> {
177 fn clone(&self) -> Self {
178 SpannedStr {
179 source: self.source,
180 spans: self.spans,
181 }
182 }
183}
184
185impl SpannedString<()> {
186 pub fn plain<S>(content: S) -> Self
188 where
189 S: Into<String>,
190 {
191 Self::single_span(content, ())
192 }
193}
194
195impl<T> SpannedString<T> {
196 pub fn new() -> Self {
198 Self::with_spans(String::new(), Vec::new())
199 }
200
201 pub fn concatenate<I>(spans: I) -> Self
205 where
206 I: IntoIterator<Item = Self>,
207 {
208 spans.into_iter().collect()
209 }
210
211 pub fn with_spans<S>(source: S, spans: Vec<IndexedSpan<T>>) -> Self
216 where
217 S: Into<String>,
218 {
219 let source = source.into();
220
221 for span in &spans {
224 if let IndexedCow::Borrowed { end, .. } = span.content {
225 assert!(end <= source.len());
226 }
227 }
228
229 SpannedString { source, spans }
230 }
231
232 pub fn canonicalize(&mut self)
241 where
242 T: PartialEq,
243 {
244 self.compact();
245 self.simplify();
246 }
247
248 pub fn canonical(mut self) -> Self
250 where
251 T: PartialEq,
252 {
253 self.canonicalize();
254 self
255 }
256
257 pub fn compact(&mut self) {
261 let mut source = String::new();
263
264 for span in &mut self.spans {
265 let start = source.len();
267 source.push_str(span.content.resolve(&self.source));
268 let end = source.len();
269
270 span.content = IndexedCow::Borrowed { start, end };
272 }
273
274 self.source = source;
275 }
276
277 pub fn simplify(&mut self)
279 where
280 T: PartialEq,
281 {
282 let mut i = 0;
284 while i + 1 < self.spans.len() {
285 let left = &self.spans[i];
286 let right = &self.spans[i + 1];
287 if left.attr != right.attr {
288 i += 1;
289 continue;
290 }
291
292 let (_, left_end) = left.content.as_borrowed().unwrap();
293 let (right_start, right_end) = right.content.as_borrowed().unwrap();
294 let right_width = right.width;
295
296 if left_end != right_start {
297 i += 1;
298 continue;
299 }
300
301 *self.spans[i].content.as_borrowed_mut().unwrap().1 = right_end;
302 self.spans[i].width += right_width;
303 self.spans.remove(i + 1);
304 }
305 }
306
307 pub fn trim_end(&mut self) {
309 if let Some(max) = self
310 .spans
311 .iter()
312 .filter_map(|s| s.content.as_borrowed())
313 .map(|(_start, end)| end)
314 .max()
315 {
316 self.source.truncate(max);
317 }
318 }
319
320 pub fn trim_start(&mut self) {
322 if let Some(min) = self
323 .spans
324 .iter()
325 .filter_map(|s| s.content.as_borrowed())
326 .map(|(start, _end)| start)
327 .min()
328 {
329 self.source.drain(..min);
330 for span in &mut self.spans {
331 span.content.rev_offset(min);
332 }
333 }
334 }
335
336 pub fn trim(&mut self) {
338 self.trim_end();
339 self.trim_start();
340 }
341
342 pub fn single_span<S>(source: S, attr: T) -> Self
344 where
345 S: Into<String>,
346 {
347 let source = source.into();
348
349 let spans = vec![IndexedSpan::simple_borrowed(&source, attr)];
350
351 Self::with_spans(source, spans)
352 }
353
354 pub fn append<S>(&mut self, other: S)
356 where
357 S: Into<Self>,
358 {
359 let other = other.into();
360 self.append_raw(&other.source, other.spans);
361 }
362
363 pub fn append_raw(&mut self, source: &str, spans: Vec<IndexedSpan<T>>) {
368 let offset = self.source.len();
369 let mut spans = spans;
370
371 for span in &mut spans {
372 span.content.offset(offset);
373 }
374
375 self.source.push_str(source);
376 self.spans.append(&mut spans);
377 }
378
379 pub fn remove_spans<R>(&mut self, range: R)
384 where
385 R: std::ops::RangeBounds<usize>,
386 {
387 self.spans.drain(range);
388 }
389
390 pub fn spans(
392 &self,
393 ) -> impl DoubleEndedIterator<Item = Span<'_, T>> + ExactSizeIterator<Item = Span<'_, T>> {
394 let source = &self.source;
395 self.spans.iter().map(move |span| span.resolve(source))
396 }
397
398 pub fn spans_attr_mut(&mut self) -> impl Iterator<Item = SpanMut<'_, T>> {
400 let source = &self.source;
401 self.spans
402 .iter_mut()
403 .map(move |span| span.resolve_mut(source))
404 }
405
406 pub fn spans_raw(&self) -> &[IndexedSpan<T>] {
408 &self.spans
409 }
410
411 pub fn spans_raw_attr_mut(
415 &mut self,
416 ) -> impl DoubleEndedIterator<Item = IndexedSpanRefMut<'_, T>>
417 + ExactSizeIterator<Item = IndexedSpanRefMut<'_, T>> {
418 self.spans.iter_mut().map(IndexedSpan::as_ref_mut)
419 }
420
421 pub fn source(&self) -> &str {
425 &self.source
426 }
427
428 pub fn into_source(self) -> String {
430 self.source
431 }
432
433 pub fn is_empty(&self) -> bool {
435 self.source.is_empty() || self.spans.is_empty()
436 }
437
438 pub fn width(&self) -> usize {
442 self.spans().map(|s| s.width).sum()
443 }
444}
445
446impl<T> FromIterator<SpannedString<T>> for SpannedString<T> {
447 fn from_iter<I: IntoIterator<Item = SpannedString<T>>>(iter: I) -> SpannedString<T> {
448 let mut iter = iter.into_iter();
451 if let Some(first) = iter.next() {
452 iter.fold(first, |mut acc, s| {
453 acc.append(s);
454 acc
455 })
456 } else {
457 SpannedString::new()
458 }
459 }
460}
461
462impl<'a, T> From<&'a SpannedString<T>> for SpannedStr<'a, T> {
463 fn from(other: &'a SpannedString<T>) -> Self {
464 SpannedStr::new(&other.source, &other.spans)
465 }
466}
467
468#[derive(Debug, PartialEq, Eq, Hash)]
470pub struct IndexedSpanRefMut<'a, T> {
471 pub content: &'a IndexedCow,
473
474 pub attr: &'a mut T,
476
477 pub width: usize,
479}
480
481#[derive(Debug, Clone, PartialEq, Eq, Hash)]
483pub struct IndexedSpan<T> {
484 pub content: IndexedCow,
486
487 pub attr: T,
489
490 pub width: usize,
492}
493
494impl<T> AsRef<IndexedCow> for IndexedSpan<T> {
495 fn as_ref(&self) -> &IndexedCow {
496 &self.content
497 }
498}
499
500#[derive(Debug, PartialEq, Eq, Hash)]
503pub struct SpanMut<'a, T> {
504 pub content: &'a str,
506
507 pub attr: &'a mut T,
509
510 pub width: usize,
512}
513
514#[derive(Debug, Clone, PartialEq, Eq, Hash)]
516pub struct Span<'a, T> {
517 pub content: &'a str,
519
520 pub attr: &'a T,
522
523 pub width: usize,
525}
526
527impl<T> IndexedSpan<T> {
528 pub fn resolve<'a>(&'a self, source: &'a str) -> Span<'a, T>
530 where
531 T: 'a,
532 {
533 Span {
534 content: self.content.resolve(source),
535 attr: &self.attr,
536 width: self.width,
537 }
538 }
539
540 pub fn resolve_mut<'a>(&'a mut self, source: &'a str) -> SpanMut<'a, T>
542 where
543 T: 'a,
544 {
545 SpanMut {
546 content: self.content.resolve(source),
547 attr: &mut self.attr,
548 width: self.width,
549 }
550 }
551
552 pub fn as_ref_mut(&mut self) -> IndexedSpanRefMut<'_, T> {
554 IndexedSpanRefMut {
555 content: &self.content,
556 attr: &mut self.attr,
557 width: self.width,
558 }
559 }
560
561 pub fn is_empty(&self) -> bool {
563 self.content.is_empty()
564 }
565
566 pub fn simple_borrowed(content: &str, attr: T) -> Self {
568 IndexedSpan {
569 content: IndexedCow::Borrowed {
570 start: 0,
571 end: content.len(),
572 },
573 attr,
574 width: content.width(),
575 }
576 }
577
578 pub fn simple_owned(content: String, attr: T) -> Self {
580 let width = content.width();
581 IndexedSpan {
582 content: IndexedCow::Owned(content),
583 attr,
584 width,
585 }
586 }
587}
588
589#[derive(Debug, Clone, PartialEq, Eq, Hash)]
591pub enum IndexedCow {
592 Borrowed {
594 start: usize,
596
597 end: usize,
599 },
600
601 Owned(String),
603}
604
605impl IndexedCow {
606 pub fn resolve<'a>(&'a self, source: &'a str) -> &'a str {
608 match *self {
609 IndexedCow::Borrowed { start, end } => &source[start..end],
610 IndexedCow::Owned(ref content) => content,
611 }
612 }
613
614 pub fn subcow(&self, range: std::ops::Range<usize>) -> Self {
618 match *self {
619 IndexedCow::Borrowed { start, end } => {
620 if start + range.end > end {
621 panic!("Attempting to get a subcow larger than itself!");
622 }
623 IndexedCow::Borrowed {
624 start: start + range.start,
625 end: start + range.end,
626 }
627 }
628 IndexedCow::Owned(ref content) => IndexedCow::Owned(content[range].into()),
629 }
630 }
631
632 pub fn as_borrowed(&self) -> Option<(usize, usize)> {
634 if let IndexedCow::Borrowed { start, end } = *self {
635 Some((start, end))
636 } else {
637 None
638 }
639 }
640
641 pub fn as_borrowed_mut(&mut self) -> Option<(&mut usize, &mut usize)> {
643 if let IndexedCow::Borrowed {
644 ref mut start,
645 ref mut end,
646 } = *self
647 {
648 Some((start, end))
649 } else {
650 None
651 }
652 }
653
654 pub fn as_owned(&self) -> Option<&str> {
656 if let IndexedCow::Owned(ref content) = *self {
657 Some(content)
658 } else {
659 None
660 }
661 }
662
663 pub fn from_str(value: &str, source: &str) -> Self {
667 let source_pos = source.as_ptr() as usize;
668 let value_pos = value.as_ptr() as usize;
669
670 assert!(value_pos >= source_pos);
672 assert!(value_pos + value.len() <= source_pos + source.len());
673
674 let start = value_pos - source_pos;
675 let end = start + value.len();
676
677 IndexedCow::Borrowed { start, end }
678 }
679
680 pub fn from_cow(cow: Cow<'_, str>, source: &str) -> Self {
684 match cow {
685 Cow::Owned(value) => IndexedCow::Owned(value),
686 Cow::Borrowed(value) => IndexedCow::from_str(value, source),
687 }
688 }
689
690 pub fn is_empty(&self) -> bool {
692 match *self {
693 IndexedCow::Borrowed { start, end } => start == end,
694 IndexedCow::Owned(ref content) => content.is_empty(),
695 }
696 }
697
698 pub fn offset(&mut self, offset: usize) {
703 if let IndexedCow::Borrowed {
704 ref mut start,
705 ref mut end,
706 } = *self
707 {
708 *start += offset;
709 *end += offset;
710 }
711 }
712
713 pub fn rev_offset(&mut self, offset: usize) {
720 if let IndexedCow::Borrowed {
721 ref mut start,
722 ref mut end,
723 } = *self
724 {
725 *start = start.saturating_sub(offset);
726 *end = end.saturating_sub(offset);
727 }
728 }
729}
730
731#[cfg(test)]
732mod tests {
733 use super::*;
734 use crate::style::Style;
735
736 #[test]
737 fn test_spanned_str_width() {
738 let spans = vec![
739 IndexedSpan {
740 content: IndexedCow::Borrowed { start: 0, end: 5 },
741 attr: Style::default(),
742 width: 5,
743 },
744 IndexedSpan {
745 content: IndexedCow::Borrowed { start: 6, end: 11 },
746 attr: Style::default(),
747 width: 5,
748 },
749 ];
750 let spanned_str = SpannedStr::new("Hello World", &spans);
751 assert_eq!(spanned_str.width(), 10);
752 }
753}