toss/src/styled.rs

195 lines
4.8 KiB
Rust

use std::iter::Peekable;
use std::slice;
use unicode_segmentation::{GraphemeIndices, Graphemes, UnicodeSegmentation};
use crate::Style;
#[derive(Debug, Default, Clone)]
pub struct Styled {
text: String,
/// List of `(style, until)` tuples. The style should be applied to all
/// chars in the range `prev_until..until`.
styles: Vec<(Style, usize)>,
}
impl Styled {
pub fn new<S: AsRef<str>>(text: S, style: Style) -> Self {
Self::default().then(text, style)
}
pub fn new_plain<S: AsRef<str>>(text: S) -> Self {
Self::default().then_plain(text)
}
pub fn then<S: AsRef<str>>(mut self, text: S, style: Style) -> Self {
let text = text.as_ref();
if !text.is_empty() {
self.text.push_str(text);
self.styles.push((style, self.text.len()));
}
self
}
pub fn then_plain<S: AsRef<str>>(self, text: S) -> Self {
self.then(text, Style::new())
}
pub fn and_then(mut self, mut other: Self) -> Self {
let delta = self.text.len();
for (_, until) in &mut other.styles {
*until += delta;
}
self.text.push_str(&other.text);
self.styles.extend(other.styles);
self
}
pub fn text(&self) -> &str {
&self.text
}
pub fn split_at(self, mid: usize) -> (Self, Self) {
let (left_text, right_text) = self.text.split_at(mid);
let mut left_styles = vec![];
let mut right_styles = vec![];
let mut from = 0;
for (style, until) in self.styles {
if from < mid {
left_styles.push((style, until.min(mid)));
}
if mid < until {
right_styles.push((style, until.saturating_sub(mid)));
}
from = until;
}
let left = Self {
text: left_text.to_string(),
styles: left_styles,
};
let right = Self {
text: right_text.to_string(),
styles: right_styles,
};
(left, right)
}
pub fn split_at_indices(self, indices: &[usize]) -> Vec<Self> {
let mut lines = vec![];
let mut rest = self;
let mut offset = 0;
for i in indices {
let (left, right) = rest.split_at(i - offset);
lines.push(left);
rest = right;
offset = *i;
}
lines.push(rest);
lines
}
pub fn trim_end(&mut self) {
self.text = self.text.trim_end().to_string();
let text_len = self.text.len();
let mut styles_len = 0;
for (_, until) in &mut self.styles {
styles_len += 1;
if *until >= text_len {
*until = text_len;
break;
}
}
while self.styles.len() > styles_len {
self.styles.pop();
}
}
}
//////////////////////////////
// Iterating over graphemes //
//////////////////////////////
pub struct StyledGraphemeIndices<'a> {
text: GraphemeIndices<'a>,
styles: Peekable<slice::Iter<'a, (Style, usize)>>,
}
impl<'a> Iterator for StyledGraphemeIndices<'a> {
type Item = (usize, Style, &'a str);
fn next(&mut self) -> Option<Self::Item> {
let (gi, grapheme) = self.text.next()?;
let (mut style, mut until) = **self.styles.peek().expect("styles cover entire text");
while gi >= until {
self.styles.next();
(style, until) = **self.styles.peek().expect("styles cover entire text");
}
Some((gi, style, grapheme))
}
}
impl Styled {
pub fn graphemes(&self) -> Graphemes<'_> {
self.text.graphemes(true)
}
pub fn grapheme_indices(&self) -> GraphemeIndices<'_> {
self.text.grapheme_indices(true)
}
pub fn styled_grapheme_indices(&self) -> StyledGraphemeIndices<'_> {
StyledGraphemeIndices {
text: self.grapheme_indices(),
styles: self.styles.iter().peekable(),
}
}
}
//////////////////////////
// Converting to Styled //
//////////////////////////
impl From<&str> for Styled {
fn from(text: &str) -> Self {
Self::new_plain(text)
}
}
impl From<String> for Styled {
fn from(text: String) -> Self {
Self::new_plain(text)
}
}
impl<S: AsRef<str>> From<(S,)> for Styled {
fn from((text,): (S,)) -> Self {
Self::new_plain(text)
}
}
impl<S: AsRef<str>> From<(S, Style)> for Styled {
fn from((text, style): (S, Style)) -> Self {
Self::new(text, style)
}
}
impl<S: AsRef<str>> From<&[(S, Style)]> for Styled {
fn from(segments: &[(S, Style)]) -> Self {
let mut result = Self::default();
for (text, style) in segments {
result = result.then(text, *style);
}
result
}
}