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::BTreeSet;
use std::fmt;

use mathtex_editor_core::{Document, Editor, MAX_HOST_TOKEN};

use crate::UndoStack;

/// Why a host box token cannot be used or minted.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TokenError {
    /// The token is above [`MAX_HOST_TOKEN`], TeX cannot read it back from `\hostbox{N}`.
    TooLarge(u32),
    /// Every token up to [`MAX_HOST_TOKEN`] has been handed out.
    Exhausted,
}

impl fmt::Display for TokenError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            TokenError::TooLarge(t) => write!(f, "host box token {t} is above {MAX_HOST_TOKEN}"),
            TokenError::Exhausted => write!(f, "every host box token up to {MAX_HOST_TOKEN} is taken"),
        }
    }
}

impl std::error::Error for TokenError {}

/// Hands out host box tokens that were never minted or reserved before, from 1 up to [`MAX_HOST_TOKEN`].
#[derive(Debug, Clone)]
pub struct TokenRegistry {
    next: u32,
    /// Reserved tokens at or above `next`, the ones below it can never be minted again anyway.
    reserved: BTreeSet<u32>,
}

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

impl TokenRegistry {
    /// A registry that has handed out nothing.
    pub fn new() -> Self {
        Self { next: 1, reserved: BTreeSet::new() }
    }

    /// A fresh token.
    pub fn mint(&mut self) -> Result<u32, TokenError> {
        while self.next <= MAX_HOST_TOKEN {
            let token = self.next;
            self.next += 1;
            if !self.reserved.remove(&token) {
                return Ok(token);
            }
        }
        Err(TokenError::Exhausted)
    }

    /// Mark a token the host chose itself as taken so it is never minted.
    pub fn reserve(&mut self, token: u32) -> Result<(), TokenError> {
        if token > MAX_HOST_TOKEN {
            return Err(TokenError::TooLarge(token));
        }
        if token >= self.next {
            self.reserved.insert(token);
        }
        Ok(())
    }

    /// Reserve every token in a document, tokens above the limit are left to [`Document::validate`].
    pub fn reserve_document(&mut self, doc: &Document) {
        for token in doc.host_tokens() {
            let _ = self.reserve(token);
        }
    }

    /// Give every host box in `doc` its own fresh token and return `(old, new)` per box in document order.
    pub fn remint(&mut self, doc: &mut Document) -> Result<Vec<(u32, u32)>, TokenError> {
        let mut pairs = Vec::new();
        let mut failed = None;
        doc.map_host_tokens(|old| match self.mint() {
            Ok(new) => {
                pairs.push((old, new));
                new
            }
            Err(e) => {
                failed = Some(e);
                old
            }
        });
        match failed {
            Some(e) => Err(e),
            None => Ok(pairs),
        }
    }

    /// Tokens the document, the history, or the clipboard still reference, any other token's content is dead.
    pub fn live(editor: &Editor, history: &UndoStack, clipboard: Option<&Document>) -> BTreeSet<u32> {
        let mut live = editor.document().host_tokens();
        for snapshot in history.snapshots() {
            live.extend(snapshot.document.host_tokens());
        }
        if let Some(doc) = clipboard {
            live.extend(doc.host_tokens());
        }
        live
    }
}