use std::borrow::Cow;
use std::iter::FromIterator;
use unicode_width::UnicodeWidthStr;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SpannedString<T> {
source: String,
spans: Vec<IndexedSpan<T>>,
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct SpannedStr<'a, T> {
source: &'a str,
spans: &'a [IndexedSpan<T>],
}
pub trait SpannedText {
type S: AsRef<IndexedCow>;
fn source(&self) -> &str;
fn spans(&self) -> &[Self::S];
fn as_ref(&self) -> SpannedTextRef<'_, Self> {
SpannedTextRef { r: self }
}
}
pub struct SpannedTextRef<'a, C>
where
C: SpannedText + ?Sized,
{
r: &'a C,
}
impl<T> Default for SpannedString<T> {
fn default() -> Self {
SpannedString::new()
}
}
impl<'a, T> SpannedText for &'a SpannedString<T> {
type S = IndexedSpan<T>;
fn source(&self) -> &str {
&self.source
}
fn spans(&self) -> &[IndexedSpan<T>] {
&self.spans
}
}
impl<'a, C> SpannedText for SpannedTextRef<'a, C>
where
C: 'a + SpannedText + ?Sized,
{
type S = C::S;
fn source(&self) -> &str {
self.r.source()
}
fn spans(&self) -> &[C::S] {
self.r.spans()
}
}
impl<'a, T> SpannedText for SpannedStr<'a, T>
where
T: 'a,
{
type S = IndexedSpan<T>;
fn source(&self) -> &str {
self.source
}
fn spans(&self) -> &[IndexedSpan<T>] {
self.spans
}
}
impl<S, T> From<S> for SpannedString<T>
where
S: Into<String>,
T: Default,
{
fn from(value: S) -> Self {
Self::single_span(value.into(), T::default())
}
}
impl<'a, T> SpannedStr<'a, T>
where
T: 'a,
{
pub fn new(source: &'a str, spans: &'a [IndexedSpan<T>]) -> Self {
SpannedStr { source, spans }
}
pub fn spans<'b>(
&'b self,
) -> impl DoubleEndedIterator<Item = Span<'a, T>>
+ ExactSizeIterator<Item = Span<'a, T>>
+ 'b
where
'a: 'b,
{
let source = self.source;
self.spans.iter().map(move |span| span.resolve(source))
}
pub fn spans_raw(&self) -> &'a [IndexedSpan<T>] {
self.spans
}
pub fn source(&self) -> &'a str {
self.source
}
pub fn is_empty(&self) -> bool {
self.source.is_empty() || self.spans.is_empty()
}
}
impl<'a, T> Clone for SpannedStr<'a, T> {
fn clone(&self) -> Self {
SpannedStr {
source: self.source,
spans: self.spans,
}
}
}
impl SpannedString<()> {
pub fn plain<S>(content: S) -> Self
where
S: Into<String>,
{
Self::single_span(content, ())
}
}
impl<T> SpannedString<T> {
pub fn new() -> Self {
Self::with_spans(String::new(), Vec::new())
}
pub fn with_spans<S>(source: S, spans: Vec<IndexedSpan<T>>) -> Self
where
S: Into<String>,
{
let source = source.into();
for span in &spans {
if let IndexedCow::Borrowed { end, .. } = span.content {
assert!(end <= source.len());
}
}
SpannedString { source, spans }
}
pub fn compact(&mut self) {
let mut source = String::new();
for span in &mut self.spans {
let start = source.len();
source.push_str(span.content.resolve(&self.source));
let end = source.len();
span.content = IndexedCow::Borrowed { start, end };
}
self.source = source;
}
pub fn trim_end(&mut self) {
if let Some(max) = self
.spans
.iter()
.filter_map(|s| s.content.as_borrowed())
.map(|(_start, end)| end)
.max()
{
self.source.truncate(max);
}
}
pub fn trim_start(&mut self) {
if let Some(min) = self
.spans
.iter()
.filter_map(|s| s.content.as_borrowed())
.map(|(start, _end)| start)
.min()
{
self.source.drain(..min);
for span in &mut self.spans {
span.content.rev_offset(min);
}
}
}
pub fn trim(&mut self) {
self.trim_end();
self.trim_start();
}
pub fn single_span<S>(source: S, attr: T) -> Self
where
S: Into<String>,
{
let source = source.into();
let spans = vec![IndexedSpan::simple_borrowed(&source, attr)];
Self::with_spans(source, spans)
}
pub fn append<S>(&mut self, other: S)
where
S: Into<Self>,
{
let other = other.into();
self.append_raw(&other.source, other.spans);
}
pub fn append_raw(&mut self, source: &str, spans: Vec<IndexedSpan<T>>) {
let offset = self.source.len();
let mut spans = spans;
for span in &mut spans {
span.content.offset(offset);
}
self.source.push_str(source);
self.spans.append(&mut spans);
}
pub fn remove_spans<R>(&mut self, range: R)
where
R: std::ops::RangeBounds<usize>,
{
self.spans.drain(range);
}
pub fn spans(
&self,
) -> impl DoubleEndedIterator<Item = Span<'_, T>>
+ ExactSizeIterator<Item = Span<'_, T>> {
let source = &self.source;
self.spans.iter().map(move |span| span.resolve(source))
}
pub fn spans_attr_mut(&mut self) -> impl Iterator<Item = SpanMut<'_, T>> {
let source = &self.source;
self.spans
.iter_mut()
.map(move |span| span.resolve_mut(source))
}
pub fn spans_raw(&self) -> &[IndexedSpan<T>] {
&self.spans
}
pub fn spans_raw_attr_mut(
&mut self,
) -> impl DoubleEndedIterator<Item = IndexedSpanRefMut<'_, T>>
+ ExactSizeIterator<Item = IndexedSpanRefMut<'_, T>> {
self.spans.iter_mut().map(IndexedSpan::as_ref_mut)
}
pub fn source(&self) -> &str {
&self.source
}
pub fn is_empty(&self) -> bool {
self.source.is_empty() || self.spans.is_empty()
}
pub fn width(&self) -> usize {
self.spans().map(|s| s.width).sum()
}
}
impl<T> FromIterator<SpannedString<T>> for SpannedString<T> {
fn from_iter<I: IntoIterator<Item = SpannedString<T>>>(
iter: I,
) -> SpannedString<T> {
let mut iter = iter.into_iter();
if let Some(first) = iter.next() {
iter.fold(first, |mut acc, s| {
acc.append(s);
acc
})
} else {
SpannedString::new()
}
}
}
impl<'a, T> From<&'a SpannedString<T>> for SpannedStr<'a, T> {
fn from(other: &'a SpannedString<T>) -> Self {
SpannedStr::new(&other.source, &other.spans)
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct IndexedSpanRefMut<'a, T> {
pub content: &'a IndexedCow,
pub attr: &'a mut T,
pub width: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct IndexedSpan<T> {
pub content: IndexedCow,
pub attr: T,
pub width: usize,
}
impl<T> AsRef<IndexedCow> for IndexedSpan<T> {
fn as_ref(&self) -> &IndexedCow {
&self.content
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct SpanMut<'a, T> {
pub content: &'a str,
pub attr: &'a mut T,
pub width: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Span<'a, T> {
pub content: &'a str,
pub attr: &'a T,
pub width: usize,
}
impl<T> IndexedSpan<T> {
pub fn resolve<'a>(&'a self, source: &'a str) -> Span<'a, T>
where
T: 'a,
{
Span {
content: self.content.resolve(source),
attr: &self.attr,
width: self.width,
}
}
pub fn resolve_mut<'a>(&'a mut self, source: &'a str) -> SpanMut<'a, T>
where
T: 'a,
{
SpanMut {
content: self.content.resolve(source),
attr: &mut self.attr,
width: self.width,
}
}
pub fn as_ref_mut(&mut self) -> IndexedSpanRefMut<'_, T> {
IndexedSpanRefMut {
content: &self.content,
attr: &mut self.attr,
width: self.width,
}
}
pub fn is_empty(&self) -> bool {
self.content.is_empty()
}
pub fn simple_borrowed(content: &str, attr: T) -> Self {
IndexedSpan {
content: IndexedCow::Borrowed {
start: 0,
end: content.len(),
},
attr,
width: content.width(),
}
}
pub fn simple_owned(content: String, attr: T) -> Self {
let width = content.width();
IndexedSpan {
content: IndexedCow::Owned(content),
attr,
width,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum IndexedCow {
Borrowed {
start: usize,
end: usize,
},
Owned(String),
}
impl IndexedCow {
pub fn resolve<'a>(&'a self, source: &'a str) -> &'a str {
match *self {
IndexedCow::Borrowed { start, end } => &source[start..end],
IndexedCow::Owned(ref content) => content,
}
}
pub fn as_borrowed(&self) -> Option<(usize, usize)> {
if let IndexedCow::Borrowed { start, end } = *self {
Some((start, end))
} else {
None
}
}
pub fn as_owned(&self) -> Option<&str> {
if let IndexedCow::Owned(ref content) = *self {
Some(content)
} else {
None
}
}
pub fn from_cow(cow: Cow<'_, str>, source: &str) -> Self {
match cow {
Cow::Owned(value) => IndexedCow::Owned(value),
Cow::Borrowed(value) => {
let source_pos = source.as_ptr() as usize;
let value_pos = value.as_ptr() as usize;
assert!(value_pos >= source_pos);
assert!(value_pos + value.len() <= source_pos + source.len());
let start = value_pos - source_pos;
let end = start + value.len();
IndexedCow::Borrowed { start, end }
}
}
}
pub fn is_empty(&self) -> bool {
match *self {
IndexedCow::Borrowed { start, end } => start == end,
IndexedCow::Owned(ref content) => content.is_empty(),
}
}
pub fn offset(&mut self, offset: usize) {
if let IndexedCow::Borrowed {
ref mut start,
ref mut end,
} = *self
{
*start += offset;
*end += offset;
}
}
pub fn rev_offset(&mut self, offset: usize) {
if let IndexedCow::Borrowed {
ref mut start,
ref mut end,
} = *self
{
*start = start.saturating_sub(offset);
*end = end.saturating_sub(offset);
}
}
}