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