diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-02-07 20:00:21 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-02-07 20:00:21 +0100 |
| commit | 68503b9a07b00bce3f4d377bcfe945452de815ea (patch) | |
| tree | 3719ef491b993c59b619ca215963000f4847e78f /src/eval/collapse.rs | |
| parent | 9730e785a885a4ab5fcc52ce705298654f82f9c2 (diff) | |
Redesigned template layout
Diffstat (limited to 'src/eval/collapse.rs')
| -rw-r--r-- | src/eval/collapse.rs | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/src/eval/collapse.rs b/src/eval/collapse.rs new file mode 100644 index 00000000..0e91cae6 --- /dev/null +++ b/src/eval/collapse.rs @@ -0,0 +1,109 @@ +use super::{StyleChain, StyleVec, StyleVecBuilder}; + +/// A wrapper around a [`StyleVecBuilder`] that allows to collapse items. +pub struct CollapsingBuilder<'a, T> { + builder: StyleVecBuilder<'a, T>, + staged: Vec<(T, StyleChain<'a>, bool)>, + last: Last, +} + +/// What the last non-ignorant item was. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +enum Last { + Weak, + Destructive, + Supportive, +} + +impl<'a, T: Merge> CollapsingBuilder<'a, T> { + /// Create a new style-vec builder. + pub fn new() -> Self { + Self { + builder: StyleVecBuilder::new(), + staged: vec![], + last: Last::Destructive, + } + } + + /// Can only exist when there is at least one supportive item to its left + /// and to its right, with no destructive items or weak items in between to + /// its left and no destructive items in between to its right. There may be + /// ignorant items in between in both directions. + pub fn weak(&mut self, item: T, styles: StyleChain<'a>) { + if self.last == Last::Supportive { + self.staged.push((item, styles, true)); + self.last = Last::Weak; + } + } + + /// Forces nearby weak items to collapse. + pub fn destructive(&mut self, item: T, styles: StyleChain<'a>) { + self.flush(false); + self.push(item, styles); + self.last = Last::Destructive; + } + + /// Allows nearby weak items to exist. + pub fn supportive(&mut self, item: T, styles: StyleChain<'a>) { + self.flush(true); + self.push(item, styles); + self.last = Last::Supportive; + } + + /// Has no influence on other items. + pub fn ignorant(&mut self, item: T, styles: StyleChain<'a>) { + self.staged.push((item, styles, false)); + } + + /// Return the finish style vec and the common prefix chain. + pub fn finish(mut self) -> (StyleVec<T>, StyleChain<'a>) { + self.flush(false); + self.builder.finish() + } + + /// Push the staged items, filtering out weak items if `supportive` is false. + fn flush(&mut self, supportive: bool) { + for (item, styles, weak) in self.staged.drain(..) { + if !weak || supportive { + push_merging(&mut self.builder, item, styles); + } + } + } + + /// Push a new item into the style vector. + fn push(&mut self, item: T, styles: StyleChain<'a>) { + push_merging(&mut self.builder, item, styles); + } +} + +/// Push an item into a style-vec builder, trying to merging it with the +/// previous item. +fn push_merging<'a, T: Merge>( + builder: &mut StyleVecBuilder<'a, T>, + item: T, + styles: StyleChain<'a>, +) { + if let Some((prev_item, prev_styles)) = builder.last_mut() { + if styles == prev_styles { + if prev_item.merge(&item) { + return; + } + } + } + + builder.push(item, styles); +} + +impl<'a, T: Merge> Default for CollapsingBuilder<'a, T> { + fn default() -> Self { + Self::new() + } +} + +/// Defines if and how to merge two adjacent items in a [`CollapsingBuilder`]. +pub trait Merge { + /// Try to merge the items, returning whether they were merged. + /// + /// Defaults to not merging. + fn merge(&mut self, next: &Self) -> bool; +} |
