1 //! Work with spans of text.
2 //!
3 //! This module defines various structs describing a span of text from a
4 //! larger string.
5 use std::borrow::Cow;
6 use std::iter::FromIterator;
7 use unicode_width::UnicodeWidthStr;
8 
9 /// A string with associated spans.
10 ///
11 /// Each span has an associated attribute `T`.
12 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
13 pub struct SpannedString<T> {
14     source: String,
15     spans: Vec<IndexedSpan<T>>,
16 }
17 
18 /// The immutable, borrowed equivalent of `SpannedString`.
19 #[derive(Debug, PartialEq, Eq, Hash)]
20 pub struct SpannedStr<'a, T> {
21     source: &'a str,
22     spans: &'a [IndexedSpan<T>],
23 }
24 
25 /// Describes an object that appears like a `SpannedStr`.
26 pub trait SpannedText {
27     /// Type of span returned by `SpannedText::spans()`.
28     type S: AsRef<IndexedCow>;
29 
30     /// Returns the source text.
source(&self) -> &str31     fn source(&self) -> &str;
32 
33     /// Returns the spans for this text.
spans(&self) -> &[Self::S]34     fn spans(&self) -> &[Self::S];
35 
36     /// Returns a `SpannedText` by reference.
as_ref(&self) -> SpannedTextRef<'_, Self>37     fn as_ref(&self) -> SpannedTextRef<'_, Self> {
38         SpannedTextRef { r: self }
39     }
40 }
41 
42 /// A reference to another `SpannedText`.
43 pub struct SpannedTextRef<'a, C>
44 where
45     C: SpannedText + ?Sized,
46 {
47     r: &'a C,
48 }
49 
50 impl<T> Default for SpannedString<T> {
default() -> Self51     fn default() -> Self {
52         SpannedString::new()
53     }
54 }
55 
56 impl<'a, T> SpannedText for &'a SpannedString<T> {
57     type S = IndexedSpan<T>;
58 
source(&self) -> &str59     fn source(&self) -> &str {
60         &self.source
61     }
62 
spans(&self) -> &[IndexedSpan<T>]63     fn spans(&self) -> &[IndexedSpan<T>] {
64         &self.spans
65     }
66 }
67 
68 impl<'a, C> SpannedText for SpannedTextRef<'a, C>
69 where
70     C: 'a + SpannedText + ?Sized,
71 {
72     type S = C::S;
73 
source(&self) -> &str74     fn source(&self) -> &str {
75         self.r.source()
76     }
77 
spans(&self) -> &[C::S]78     fn spans(&self) -> &[C::S] {
79         self.r.spans()
80     }
81 }
82 
83 impl<'a, T> SpannedText for SpannedStr<'a, T>
84 where
85     T: 'a,
86 {
87     type S = IndexedSpan<T>;
88 
source(&self) -> &str89     fn source(&self) -> &str {
90         self.source
91     }
92 
spans(&self) -> &[IndexedSpan<T>]93     fn spans(&self) -> &[IndexedSpan<T>] {
94         self.spans
95     }
96 }
97 
98 impl<S, T> From<S> for SpannedString<T>
99 where
100     S: Into<String>,
101     T: Default,
102 {
from(value: S) -> Self103     fn from(value: S) -> Self {
104         Self::single_span(value.into(), T::default())
105     }
106 }
107 
108 impl<'a, T> SpannedStr<'a, T>
109 where
110     T: 'a,
111 {
112     /// Creates a new `SpannedStr` from the given references.
new(source: &'a str, spans: &'a [IndexedSpan<T>]) -> Self113     pub fn new(source: &'a str, spans: &'a [IndexedSpan<T>]) -> Self {
114         SpannedStr { source, spans }
115     }
116 
117     /// Gives access to the parsed styled spans.
spans<'b>( &'b self, ) -> impl DoubleEndedIterator<Item = Span<'a, T>> + ExactSizeIterator<Item = Span<'a, T>> + 'b where 'a: 'b,118     pub fn spans<'b>(
119         &'b self,
120     ) -> impl DoubleEndedIterator<Item = Span<'a, T>>
121            + ExactSizeIterator<Item = Span<'a, T>>
122            + 'b
123     where
124         'a: 'b,
125     {
126         let source = self.source;
127         self.spans.iter().map(move |span| span.resolve(source))
128     }
129 
130     /// Returns a reference to the indexed spans.
spans_raw(&self) -> &'a [IndexedSpan<T>]131     pub fn spans_raw(&self) -> &'a [IndexedSpan<T>] {
132         self.spans
133     }
134 
135     /// Returns a reference to the source (non-parsed) string.
source(&self) -> &'a str136     pub fn source(&self) -> &'a str {
137         self.source
138     }
139 
140     /// Returns `true` if `self` is empty.
141     ///
142     /// Can be caused by an empty source, or no span.
is_empty(&self) -> bool143     pub fn is_empty(&self) -> bool {
144         self.source.is_empty() || self.spans.is_empty()
145     }
146 }
147 
148 impl<'a, T> Clone for SpannedStr<'a, T> {
clone(&self) -> Self149     fn clone(&self) -> Self {
150         SpannedStr {
151             source: self.source,
152             spans: self.spans,
153         }
154     }
155 }
156 
157 impl SpannedString<()> {
158     /// Returns a simple spanned string without any attribute.
plain<S>(content: S) -> Self where S: Into<String>,159     pub fn plain<S>(content: S) -> Self
160     where
161         S: Into<String>,
162     {
163         Self::single_span(content, ())
164     }
165 }
166 
167 impl<T> SpannedString<T> {
168     /// Returns an empty `SpannedString`.
new() -> Self169     pub fn new() -> Self {
170         Self::with_spans(String::new(), Vec::new())
171     }
172 
173     /// Creates a new `SpannedString` manually.
174     ///
175     /// It is not recommended to use this directly.
176     /// Instead, look for methods like `Markdown::parse`.
with_spans<S>(source: S, spans: Vec<IndexedSpan<T>>) -> Self where S: Into<String>,177     pub fn with_spans<S>(source: S, spans: Vec<IndexedSpan<T>>) -> Self
178     where
179         S: Into<String>,
180     {
181         let source = source.into();
182 
183         // Make sure the spans are within bounds.
184         // This should disapear when compiled in release mode.
185         for span in &spans {
186             if let IndexedCow::Borrowed { end, .. } = span.content {
187                 assert!(end <= source.len());
188             }
189         }
190 
191         SpannedString { source, spans }
192     }
193 
194     /// Compacts the source to only include the spans content.
compact(&mut self)195     pub fn compact(&mut self) {
196         // Prepare the new source
197         let mut source = String::new();
198 
199         for span in &mut self.spans {
200             // Only include what we need.
201             let start = source.len();
202             source.push_str(span.content.resolve(&self.source));
203             let end = source.len();
204 
205             // All spans now borrow the source.
206             span.content = IndexedCow::Borrowed { start, end };
207         }
208 
209         self.source = source;
210     }
211 
212     /// Shrink the source to discard any unused suffix.
trim_end(&mut self)213     pub fn trim_end(&mut self) {
214         if let Some(max) = self
215             .spans
216             .iter()
217             .filter_map(|s| s.content.as_borrowed())
218             .map(|(_start, end)| end)
219             .max()
220         {
221             self.source.truncate(max);
222         }
223     }
224 
225     /// Shrink the source to discard any unused prefix.
trim_start(&mut self)226     pub fn trim_start(&mut self) {
227         if let Some(min) = self
228             .spans
229             .iter()
230             .filter_map(|s| s.content.as_borrowed())
231             .map(|(start, _end)| start)
232             .min()
233         {
234             self.source.drain(..min);
235             for span in &mut self.spans {
236                 span.content.rev_offset(min);
237             }
238         }
239     }
240 
241     /// Shrink the source to discard any unused prefix or suffix.
trim(&mut self)242     pub fn trim(&mut self) {
243         self.trim_end();
244         self.trim_start();
245     }
246 
247     /// Returns a new SpannedString with a single span.
single_span<S>(source: S, attr: T) -> Self where S: Into<String>,248     pub fn single_span<S>(source: S, attr: T) -> Self
249     where
250         S: Into<String>,
251     {
252         let source = source.into();
253 
254         let spans = vec![IndexedSpan::simple_borrowed(&source, attr)];
255 
256         Self::with_spans(source, spans)
257     }
258 
259     /// Appends the given `StyledString` to `self`.
append<S>(&mut self, other: S) where S: Into<Self>,260     pub fn append<S>(&mut self, other: S)
261     where
262         S: Into<Self>,
263     {
264         let other = other.into();
265         self.append_raw(&other.source, other.spans);
266     }
267 
268     /// Appends `content` and its corresponding spans to the end.
269     ///
270     /// It is not recommended to use this directly;
271     /// instead, look at the `append` method.
append_raw(&mut self, source: &str, spans: Vec<IndexedSpan<T>>)272     pub fn append_raw(&mut self, source: &str, spans: Vec<IndexedSpan<T>>) {
273         let offset = self.source.len();
274         let mut spans = spans;
275 
276         for span in &mut spans {
277             span.content.offset(offset);
278         }
279 
280         self.source.push_str(source);
281         self.spans.append(&mut spans);
282     }
283 
284     /// Remove the given range of spans from the styled string.
285     ///
286     /// You may want to follow this with either `compact()`,
287     /// `trim_start()` or `trim_end()`.
remove_spans<R>(&mut self, range: R) where R: std::ops::RangeBounds<usize>,288     pub fn remove_spans<R>(&mut self, range: R)
289     where
290         R: std::ops::RangeBounds<usize>,
291     {
292         self.spans.drain(range);
293     }
294 
295     /// Iterates on the resolved spans.
spans( &self, ) -> impl DoubleEndedIterator<Item = Span<'_, T>> + ExactSizeIterator<Item = Span<'_, T>>296     pub fn spans(
297         &self,
298     ) -> impl DoubleEndedIterator<Item = Span<'_, T>>
299            + ExactSizeIterator<Item = Span<'_, T>> {
300         let source = &self.source;
301         self.spans.iter().map(move |span| span.resolve(source))
302     }
303 
304     /// Iterates on the resolved spans, with mutable access to the attributes.
spans_attr_mut(&mut self) -> impl Iterator<Item = SpanMut<'_, T>>305     pub fn spans_attr_mut(&mut self) -> impl Iterator<Item = SpanMut<'_, T>> {
306         let source = &self.source;
307         self.spans
308             .iter_mut()
309             .map(move |span| span.resolve_mut(source))
310     }
311 
312     /// Returns a reference to the indexed spans.
spans_raw(&self) -> &[IndexedSpan<T>]313     pub fn spans_raw(&self) -> &[IndexedSpan<T>] {
314         &self.spans
315     }
316 
317     /// Returns a mutable iterator on the spans of this string.
318     ///
319     /// This can be used to modify the style of each span.
spans_raw_attr_mut( &mut self, ) -> impl DoubleEndedIterator<Item = IndexedSpanRefMut<'_, T>> + ExactSizeIterator<Item = IndexedSpanRefMut<'_, T>>320     pub fn spans_raw_attr_mut(
321         &mut self,
322     ) -> impl DoubleEndedIterator<Item = IndexedSpanRefMut<'_, T>>
323            + ExactSizeIterator<Item = IndexedSpanRefMut<'_, T>> {
324         self.spans.iter_mut().map(IndexedSpan::as_ref_mut)
325     }
326 
327     /// Returns a reference to the source string.
328     ///
329     /// This is the non-parsed string.
source(&self) -> &str330     pub fn source(&self) -> &str {
331         &self.source
332     }
333 
334     /// Returns `true` if self is empty.
is_empty(&self) -> bool335     pub fn is_empty(&self) -> bool {
336         self.source.is_empty() || self.spans.is_empty()
337     }
338 
339     /// Returns the width taken by this string.
340     ///
341     /// This is the sum of the width of each span.
width(&self) -> usize342     pub fn width(&self) -> usize {
343         self.spans().map(|s| s.width).sum()
344     }
345 }
346 
347 impl<T> FromIterator<SpannedString<T>> for SpannedString<T> {
from_iter<I: IntoIterator<Item = SpannedString<T>>>( iter: I, ) -> SpannedString<T>348     fn from_iter<I: IntoIterator<Item = SpannedString<T>>>(
349         iter: I,
350     ) -> SpannedString<T> {
351         let mut iter = iter.into_iter();
352         if let Some(first) = iter.next() {
353             iter.fold(first, |mut acc, s| {
354                 acc.append(s);
355                 acc
356             })
357         } else {
358             SpannedString::new()
359         }
360     }
361 }
362 
363 impl<'a, T> From<&'a SpannedString<T>> for SpannedStr<'a, T> {
from(other: &'a SpannedString<T>) -> Self364     fn from(other: &'a SpannedString<T>) -> Self {
365         SpannedStr::new(&other.source, &other.spans)
366     }
367 }
368 
369 /// A reference to an IndexedSpan allowing modification of the attribute.
370 #[derive(Debug, PartialEq, Eq, Hash)]
371 pub struct IndexedSpanRefMut<'a, T> {
372     /// Points to the content of the span.
373     pub content: &'a IndexedCow,
374 
375     /// Mutable reference to the attribute of the span.
376     pub attr: &'a mut T,
377 
378     /// Width of the span.
379     pub width: usize,
380 }
381 
382 /// An indexed span with an associated attribute.
383 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
384 pub struct IndexedSpan<T> {
385     /// Content of the span.
386     pub content: IndexedCow,
387 
388     /// Attribute applied to the span.
389     pub attr: T,
390 
391     /// Width of the text for this span.
392     pub width: usize,
393 }
394 
395 impl<T> AsRef<IndexedCow> for IndexedSpan<T> {
as_ref(&self) -> &IndexedCow396     fn as_ref(&self) -> &IndexedCow {
397         &self.content
398     }
399 }
400 
401 /// A resolved span borrowing its source string, with mutable access to the
402 /// attribute.
403 #[derive(Debug, PartialEq, Eq, Hash)]
404 pub struct SpanMut<'a, T> {
405     /// Content of this span.
406     pub content: &'a str,
407 
408     /// Attribute associated to this span.
409     pub attr: &'a mut T,
410 
411     /// Width of the text for this span.
412     pub width: usize,
413 }
414 
415 /// A resolved span borrowing its source string.
416 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
417 pub struct Span<'a, T> {
418     /// Content of this span.
419     pub content: &'a str,
420 
421     /// Attribute associated to this span.
422     pub attr: &'a T,
423 
424     /// Width of the text for this span.
425     pub width: usize,
426 }
427 
428 impl<T> IndexedSpan<T> {
429     /// Resolve the span to a string slice and an attribute.
resolve<'a>(&'a self, source: &'a str) -> Span<'a, T> where T: 'a,430     pub fn resolve<'a>(&'a self, source: &'a str) -> Span<'a, T>
431     where
432         T: 'a,
433     {
434         Span {
435             content: self.content.resolve(source),
436             attr: &self.attr,
437             width: self.width,
438         }
439     }
440 
441     /// Resolve the span to a string slice and a mutable attribute.
resolve_mut<'a>(&'a mut self, source: &'a str) -> SpanMut<'a, T> where T: 'a,442     pub fn resolve_mut<'a>(&'a mut self, source: &'a str) -> SpanMut<'a, T>
443     where
444         T: 'a,
445     {
446         SpanMut {
447             content: self.content.resolve(source),
448             attr: &mut self.attr,
449             width: self.width,
450         }
451     }
452 
453     /// Returns a reference struct to only access mutation of the attribute.
as_ref_mut(&mut self) -> IndexedSpanRefMut<'_, T>454     pub fn as_ref_mut(&mut self) -> IndexedSpanRefMut<'_, T> {
455         IndexedSpanRefMut {
456             content: &self.content,
457             attr: &mut self.attr,
458             width: self.width,
459         }
460     }
461 
462     /// Returns `true` if `self` is an empty span.
is_empty(&self) -> bool463     pub fn is_empty(&self) -> bool {
464         self.content.is_empty()
465     }
466 
467     /// Returns a single indexed span around the entire text.
simple_borrowed(content: &str, attr: T) -> Self468     pub fn simple_borrowed(content: &str, attr: T) -> Self {
469         IndexedSpan {
470             content: IndexedCow::Borrowed {
471                 start: 0,
472                 end: content.len(),
473             },
474             attr,
475             width: content.width(),
476         }
477     }
478 
479     /// Returns a single owned indexed span around the entire text.
simple_owned(content: String, attr: T) -> Self480     pub fn simple_owned(content: String, attr: T) -> Self {
481         let width = content.width();
482         IndexedSpan {
483             content: IndexedCow::Owned(content),
484             attr,
485             width,
486         }
487     }
488 }
489 
490 /// A span of text that can be either owned, or indexed in another String.
491 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
492 pub enum IndexedCow {
493     /// Indexes content in a separate string.
494     Borrowed {
495         /// Byte offset of the beginning of the span (inclusive)
496         start: usize,
497 
498         /// Byte offset of the end of the span (exclusive)
499         end: usize,
500     },
501 
502     /// Owns its content.
503     Owned(String),
504 }
505 
506 impl IndexedCow {
507     /// Resolve the span to a string slice.
resolve<'a>(&'a self, source: &'a str) -> &'a str508     pub fn resolve<'a>(&'a self, source: &'a str) -> &'a str {
509         match *self {
510             IndexedCow::Borrowed { start, end } => &source[start..end],
511             IndexedCow::Owned(ref content) => content,
512         }
513     }
514 
515     /// Return the `(start, end)` indexes if `self` is `IndexedCow::Borrowed`.
as_borrowed(&self) -> Option<(usize, usize)>516     pub fn as_borrowed(&self) -> Option<(usize, usize)> {
517         if let IndexedCow::Borrowed { start, end } = *self {
518             Some((start, end))
519         } else {
520             None
521         }
522     }
523 
524     /// Returns the embedded text content if `self` is `IndexedCow::Owned`.
as_owned(&self) -> Option<&str>525     pub fn as_owned(&self) -> Option<&str> {
526         if let IndexedCow::Owned(ref content) = *self {
527             Some(content)
528         } else {
529             None
530         }
531     }
532 
533     /// Returns an indexed view of the given item.
534     ///
535     /// **Note**: it is assumed `cow`, if borrowed, is a substring of `source`.
from_cow(cow: Cow<'_, str>, source: &str) -> Self536     pub fn from_cow(cow: Cow<'_, str>, source: &str) -> Self {
537         match cow {
538             Cow::Owned(value) => IndexedCow::Owned(value),
539             Cow::Borrowed(value) => {
540                 let source_pos = source.as_ptr() as usize;
541                 let value_pos = value.as_ptr() as usize;
542 
543                 // Make sure `value` is indeed a substring of `source`
544                 assert!(value_pos >= source_pos);
545                 assert!(value_pos + value.len() <= source_pos + source.len());
546 
547                 let start = value_pos - source_pos;
548                 let end = start + value.len();
549 
550                 IndexedCow::Borrowed { start, end }
551             }
552         }
553     }
554 
555     /// Returns `true` if this represents an empty span.
is_empty(&self) -> bool556     pub fn is_empty(&self) -> bool {
557         match *self {
558             IndexedCow::Borrowed { start, end } => start == end,
559             IndexedCow::Owned(ref content) => content.is_empty(),
560         }
561     }
562 
563     /// If `self` is borrowed, offset its indices by the given value.
564     ///
565     /// Useful to update spans when concatenating sources. This span will now
566     /// point to text `offset` further in the source.
offset(&mut self, offset: usize)567     pub fn offset(&mut self, offset: usize) {
568         if let IndexedCow::Borrowed {
569             ref mut start,
570             ref mut end,
571         } = *self
572         {
573             *start += offset;
574             *end += offset;
575         }
576     }
577 
578     /// If `self` is borrowed, offset its indices back by the given value.
579     ///
580     /// Useful to update spans when removing a prefix from the source.
581     /// This span will now point to text `offset` closer to the start of the source.
582     ///
583     /// This span may become empty as a result.
rev_offset(&mut self, offset: usize)584     pub fn rev_offset(&mut self, offset: usize) {
585         if let IndexedCow::Borrowed {
586             ref mut start,
587             ref mut end,
588         } = *self
589         {
590             *start = start.saturating_sub(offset);
591             *end = end.saturating_sub(offset);
592         }
593     }
594 }
595