use super::super::{MAX_BLOCKS, MAX_TOTAL_TEXT_BYTES, Transcript};
#[allow(dead_code)]
impl Transcript {
pub(crate) fn mutate<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
let baseline = (self.scroll_up != 0)
.then(|| self.scrolled_baseline())
.flatten();
let result = f(self);
let measured = baseline.map(|(width, before)| {
self.invalidate_cache();
let mid = self.lines(width).len();
(width, before, mid)
});
let evicted = self.enforce_bounds();
self.after_scrolled_mutation(measured, evicted);
result
}
pub(super) fn enforce_bounds(&mut self) -> bool {
let mut bytes: usize = self.blocks.iter().map(|b| b.text.len()).sum();
let mut drop = 0;
while self.blocks.len().saturating_sub(drop) > MAX_BLOCKS
|| (bytes > MAX_TOTAL_TEXT_BYTES && drop < self.blocks.len())
{
bytes = bytes.saturating_sub(self.blocks[drop].text.len());
drop += 1;
}
if drop > 0 {
self.blocks.drain(..drop);
return true;
}
false
}
fn freeze_scrolled_view(&mut self, before: usize, mid: usize) {
if self.scroll_up == 0 {
return;
}
if mid >= before {
let growth = mid - before;
self.scroll_up = self.scroll_up.saturating_add(growth);
self.unseen_rows = self.unseen_rows.saturating_add(growth);
} else {
let shrink = before - mid;
self.scroll_up = self.scroll_up.saturating_sub(shrink);
self.unseen_rows = self.unseen_rows.saturating_sub(shrink).min(self.scroll_up);
}
if self.scroll_up == 0 {
self.unseen_rows = 0;
}
}
fn scrolled_baseline(&self) -> Option<(usize, usize)> {
self.cache
.borrow()
.as_ref()
.map(|(width, rows)| (*width, rows.len()))
}
fn after_scrolled_mutation(&mut self, measured: Option<(usize, usize, usize)>, evicted: bool) {
match measured {
Some((width, before, mid)) => {
if evicted {
self.invalidate_cache();
}
let after = self.lines(width).len();
debug_assert!(after <= mid, "bounds enforcement only removes rows");
self.freeze_scrolled_view(before, mid);
}
None => self.invalidate_cache(),
}
}
}
#[cfg(test)]
#[path = "mutation_tests.rs"]
mod mutation_tests;
#[cfg(test)]
#[path = "shrink_anchor_tests.rs"]
mod shrink_anchor_tests;