use std::collections::VecDeque;
use super::Cell;
pub struct Scrollback {
rows: VecDeque<Vec<Cell>>,
max_lines: usize,
}
impl Scrollback {
pub fn new() -> Self {
Self::new_scrollback(10_000)
}
pub fn new_scrollback(max_lines: usize) -> Self {
Self {
rows: VecDeque::with_capacity(max_lines.min(1000)),
max_lines,
}
}
pub fn push(&mut self, row: &[Cell]) {
if self.max_lines == 0 {
return;
}
let cloned: Vec<Cell> = row.to_vec();
self.push_owned(cloned);
}
pub fn push_owned(&mut self, row: Vec<Cell>) {
let _ = self.push_owned_recycling(row);
}
pub fn push_owned_recycling(&mut self, row: Vec<Cell>) -> Option<Vec<Cell>> {
if self.max_lines == 0 {
return Some(row);
}
let recycled = (self.rows.len() >= self.max_lines)
.then(|| self.rows.pop_front())
.flatten();
self.rows.push_back(row);
recycled
}
pub fn len(&self) -> usize {
self.rows.len()
}
pub fn set_max_lines(&mut self, max_lines: usize) {
self.max_lines = max_lines;
while self.rows.len() > self.max_lines {
self.rows.pop_front();
}
}
pub fn row_cells(&self, index: usize) -> Option<&[Cell]> {
self.rows.get(index).map(|r| r.as_slice())
}
pub fn row_text(&self, index: usize, cols: usize) -> String {
if let Some(row) = self.rows.get(index) {
let mut s = String::with_capacity(cols);
for cell in row {
cell.push_text(&mut s);
}
s
} else {
" ".repeat(cols)
}
}
pub fn clear(&mut self) {
self.rows.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
fn row(text: &str) -> Vec<Cell> {
text.chars().map(Cell::new).collect()
}
#[test]
fn set_max_lines_trims_oldest_rows() {
let mut scrollback = Scrollback::new_scrollback(4);
for value in ["one", "two", "three", "four"] {
scrollback.push_owned(row(value));
}
scrollback.set_max_lines(2);
assert_eq!(scrollback.len(), 2);
assert_eq!(scrollback.row_text(0, 0), "three");
assert_eq!(scrollback.row_text(1, 0), "four");
}
#[test]
fn set_max_lines_zero_drops_all_rows_and_recycles_future_rows() {
let mut scrollback = Scrollback::new_scrollback(2);
scrollback.push_owned(row("one"));
scrollback.set_max_lines(0);
let recycled = scrollback.push_owned_recycling(row("two"));
assert_eq!(scrollback.len(), 0);
assert!(recycled.is_some());
}
}