Skip to content

Commit

Permalink
Add StyledString::{simplify, canonicalize, canonical}
Browse files Browse the repository at this point in the history
  • Loading branch information
gyscos committed Jun 20, 2024
1 parent e37530f commit f3c2aa9
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 3 deletions.
42 changes: 39 additions & 3 deletions cursive-core/src/utils/markup/cursup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,36 @@ struct Candidate {
brace: usize,
}

#[derive(Debug)]
#[derive(Debug, PartialEq, Eq)]
enum Event {
StartSkip,
Start(Style),
End,
StartSkip,
Resume,
}

impl PartialOrd for Event {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}

impl Ord for Event {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match (self, other) {
(Event::StartSkip, Event::Start(_) | Event::End | Event::Resume)
| (Event::Start(_), Event::End | Event::Resume)
| (Event::End, Event::Resume) => std::cmp::Ordering::Less,

(Event::Start(_) | Event::End | Event::Resume, Event::StartSkip)
| (Event::End | Event::Resume, Event::Start(_))
| (Event::Resume, Event::End) => std::cmp::Ordering::Greater,

_ => std::cmp::Ordering::Equal,
}
}
}

/// Parse spans for the given text.
pub fn parse_spans(input: &str) -> Vec<StyledIndexedSpan> {
let mut candidates = Vec::<Candidate>::new();
Expand All @@ -63,6 +85,8 @@ pub fn parse_spans(input: &str) -> Vec<StyledIndexedSpan> {
events.push((candidate.brace + 1, Event::Start(style)));
events.push((i, Event::End));
events.push((i + 1, Event::Resume));

state = State::Plain;
}
(State::Plain, _) => (),

Expand All @@ -89,7 +113,7 @@ pub fn parse_spans(input: &str) -> Vec<StyledIndexedSpan> {
}
}

events.sort_by_key(|(i, _)| *i);
events.sort();

let mut spans = Vec::new();
let mut style_stack = vec![Style::default()];
Expand Down Expand Up @@ -255,6 +279,18 @@ mod tests {
);
}

#[test]
fn simple() {
let parsed = parse("/{simple}").canonical();
assert_eq!(parsed, StyledString::plain("simple"));
}

#[test]
fn escape() {
let parsed = parse("/{/}red{foo}").canonical();
assert_eq!(parsed, StyledString::plain("/red{foo}"));
}

#[test]
fn hex_color() {
let parsed = parse("/#ff0000{red}");
Expand Down
70 changes: 70 additions & 0 deletions cursive-core/src/utils/span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,34 @@ impl<T> SpannedString<T> {
SpannedString { source, spans }
}

/// Compacts and simplifies this string, resulting in a canonical form.
///
/// If two styled strings represent the same styled text, they should have equal canonical
/// forms.
///
/// (The PartialEq implementation for StyledStrings requires both the source and spans to be
/// equals, so non-visible changes such as text in the source between spans could cause
/// StyledStrings to evaluate as non-equal.)
pub fn canonicalize(&mut self)
where
T: PartialEq,
{
self.compact();
self.simplify();
}

/// Returns the canonical form of this styled string.
pub fn canonical(mut self) -> Self
where
T: PartialEq,
{
self.canonicalize();
self
}

/// Compacts the source to only include the spans content.
///
/// This does not change the number of spans, but changes the source.
pub fn compact(&mut self) {
// Prepare the new source
let mut source = String::new();
Expand All @@ -247,6 +274,36 @@ impl<T> SpannedString<T> {
self.source = source;
}

/// Attemps to reduce the number of spans by merging consecutive similar ones.
pub fn simplify(&mut self)
where
T: PartialEq,
{
// Now, merge consecutive similar spans.
let mut i = 0;
while i + 1 < self.spans.len() {
let left = &self.spans[i];
let right = &self.spans[i + 1];
if left.attr != right.attr {
i += 1;
continue;
}

let (_, left_end) = left.content.as_borrowed().unwrap();
let (right_start, right_end) = right.content.as_borrowed().unwrap();
let right_width = right.width;

if left_end != right_start {
i += 1;
continue;
}

*self.spans[i].content.as_borrowed_mut().unwrap().1 = right_end;
self.spans[i].width += right_width;
self.spans.remove(i + 1);
}
}

/// Shrink the source to discard any unused suffix.
pub fn trim_end(&mut self) {
if let Some(max) = self
Expand Down Expand Up @@ -558,6 +615,19 @@ impl IndexedCow {
}
}

/// Return the `(start, end)` indexes if `self` is `IndexedCow::Borrowed`.
pub fn as_borrowed_mut(&mut self) -> Option<(&mut usize, &mut usize)> {
if let IndexedCow::Borrowed {
ref mut start,
ref mut end,
} = *self
{
Some((start, end))
} else {
None
}
}

/// Returns the embedded text content if `self` is `IndexedCow::Owned`.
pub fn as_owned(&self) -> Option<&str> {
if let IndexedCow::Owned(ref content) = *self {
Expand Down

0 comments on commit f3c2aa9

Please sign in to comment.