mathtex-editor-session 0.3.0

Batteries included session for mathtex-editor: keymap, typesetting, undo, clipboard and host box tokens over the explicit core
Documentation
use std::collections::VecDeque;

use mathtex_editor_core::Snapshot;

/// Undo steps a [`crate::Session`] keeps unless the host sets another limit.
pub const DEFAULT_UNDO_LIMIT: usize = 200;

/// Bounded undo and redo history of editor snapshots, where any new step clears the redo side.
#[derive(Debug, Clone)]
pub struct UndoStack {
    undo: VecDeque<Snapshot>,
    redo: Vec<Snapshot>,
    limit: usize,
}

impl Default for UndoStack {
    fn default() -> Self {
        Self::new(DEFAULT_UNDO_LIMIT)
    }
}

impl UndoStack {
    /// An empty history keeping at most `limit` undo steps, 0 keeps none.
    pub fn new(limit: usize) -> Self {
        Self { undo: VecDeque::new(), redo: Vec::new(), limit }
    }

    /// The most undo steps kept.
    pub fn limit(&self) -> usize {
        self.limit
    }

    /// Change the limit, dropping the oldest steps beyond it.
    pub fn set_limit(&mut self, limit: usize) {
        self.limit = limit;
        self.trim();
    }

    /// Record the state before an edit and clear the redo side.
    pub fn record(&mut self, before: Snapshot) {
        self.redo.clear();
        self.undo.push_back(before);
        self.trim();
    }

    /// Step back: returns the state to restore and keeps `current` for redo.
    pub fn undo(&mut self, current: Snapshot) -> Option<Snapshot> {
        let target = self.undo.pop_back()?;
        self.redo.push(current);
        Some(target)
    }

    /// Step forward again: returns the state to restore and keeps `current` for undo.
    pub fn redo(&mut self, current: Snapshot) -> Option<Snapshot> {
        let target = self.redo.pop()?;
        self.undo.push_back(current);
        self.trim();
        Some(target)
    }

    /// Whether there is a step to undo.
    pub fn can_undo(&self) -> bool {
        !self.undo.is_empty()
    }

    /// Whether there is a step to redo.
    pub fn can_redo(&self) -> bool {
        !self.redo.is_empty()
    }

    /// Drop every step.
    pub fn clear(&mut self) {
        self.undo.clear();
        self.redo.clear();
    }

    /// Every kept snapshot, undo steps oldest first and then redo steps.
    pub fn snapshots(&self) -> impl Iterator<Item = &Snapshot> {
        self.undo.iter().chain(&self.redo)
    }

    fn trim(&mut self) {
        while self.undo.len() > self.limit {
            self.undo.pop_front();
        }
    }
}