mathtex-editor-core 0.3.0

Headless core of the mathtex structural math editor: model, operations, navigation, selection, IR matching
Documentation
//! Commands the host sends to `Editor::exec` and the outcome it gets back.

use crate::doc::Document;
use crate::model::{FracStyle, Mark, MatrixEnv, ScriptSlot, Symbol, UnderOverSpec, Variant};
use crate::path::CaretPath;

/// A movement direction.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Dir {
    /// Left.
    Left,
    /// Right.
    Right,
    /// Up.
    Up,
    /// Down.
    Down,
}

/// A side relative to a matrix row or column, or the side a host box is approached from.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Side {
    /// Before the target.
    Before,
    /// After the target.
    After,
}

/// A direction in which the caret leaves the formula.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ExitDir {
    /// Left.
    Left,
    /// Right.
    Right,
    /// Up.
    Up,
    /// Down.
    Down,
}

/// An edge of the whole formula.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Edge {
    /// Before the first item.
    Start,
    /// After the last item.
    End,
}

/// How horizontal motion treats host boxes.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub enum HostBoxPolicy {
    /// Step over a host box like any atom.
    #[default]
    Skip,
    /// Stop in front of a host box and report it in [`Outcome::entered_host_box`].
    Enter,
}

/// A host box the caret would have crossed under [`HostBoxPolicy::Enter`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HostBoxEntry {
    /// The box's token.
    pub token: u32,
    /// `Before` when approached moving right, `After` when approached moving left.
    pub side: Side,
}

/// An editor command.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Command {
    /// Move the caret.
    Move(Dir),
    /// Move to the start of the caret's sequence.
    MoveLineStart,
    /// Move to the end of the caret's sequence.
    MoveLineEnd,
    /// Move to the next empty slot.
    Tab,
    /// Move to the previous empty slot.
    ShiftTab,
    /// Place the caret, usually at a `Editor::hit_test` result.
    MoveTo(CaretPath),
    /// Extend the selection by one step.
    Extend(Dir),
    /// Extend the selection to a caret, promoting across structures.
    ExtendTo(CaretPath),
    /// Select the whole formula.
    SelectAll,
    /// Collapse the selection, or close the menu.
    Collapse,
    /// Insert a symbol atom, replacing the selection.
    InsertAtom(Symbol),
    /// Insert an opaque host box carrying a host minted token, refused above [`crate::MAX_HOST_TOKEN`].
    InsertHostBox(u32),
    /// Insert each character as an atom, spaces are kept only in text slots.
    InsertText(String),
    /// Insert a fraction, wrapping the selection into the numerator.
    InsertFraction(FracStyle),
    /// Attach a script to the item left of the caret, or wrap the selection as the base.
    InsertScript(ScriptSlot),
    /// Insert a big operator with empty limits, replacing the selection.
    InsertBigOp(Symbol),
    /// Insert a radical, wrapping the selection into the radicand.
    InsertSqrt,
    /// Insert stretchy delimiters, wrapping the selection into the body.
    InsertDelimiters {
        /// Opening delimiter.
        open: char,
        /// Closing delimiter.
        close: char,
    },
    /// Insert an accent, wrapping the selection into its base.
    InsertAccent(Mark),
    /// Insert an under or over construct, wrapping the selection into its base.
    InsertUnderOver(UnderOverSpec),
    /// Insert a styled run, wrapping the selection into it.
    InsertStyled(Variant),
    /// Insert a matrix, replacing the selection.
    InsertMatrix {
        /// Environment.
        env: MatrixEnv,
        /// Row count, at least one.
        rows: usize,
        /// Column count, at least one.
        cols: usize,
    },
    /// Paste a document at the caret, replacing the selection, refused when it fails validation.
    InsertDocument(Document),
    /// Delete backward, selecting or opening the menu next to a structure first.
    DeleteBackward,
    /// Delete forward, selecting or opening the menu next to a structure first.
    DeleteForward,
    /// Insert a matrix row next to the caret's row.
    MatrixInsertRow(Side),
    /// Delete the caret's matrix row.
    MatrixDeleteRow,
    /// Insert a matrix column next to the caret's column.
    MatrixInsertCol(Side),
    /// Delete the caret's matrix column.
    MatrixDeleteCol,
    /// Commit the highlighted menu row, or collapse when no menu is open.
    Confirm,
    /// Commit a visible menu row by index.
    MenuSelect(usize),
    /// When the atoms left of the caret spell `typed`, delete them and run `with`.
    ReplaceTyped {
        /// Characters to match, compared through [`Symbol::from_char`].
        typed: String,
        /// Commands to run after deleting the match.
        with: Vec<Command>,
    },
    /// Leave the innermost matching delimiters when at their end, else insert the character.
    CloseDelimiter(char),
}

/// What a command did.
#[must_use]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Outcome {
    /// The document changed and the revision advanced.
    pub changed: bool,
    /// The caret or selection moved.
    pub moved: bool,
    /// The caret tried to leave the formula in this direction.
    pub exit: Option<ExitDir>,
    /// Deleting in an empty formula asks the host to end math mode.
    pub close: bool,
    /// Motion stopped in front of a host box under [`HostBoxPolicy::Enter`].
    pub entered_host_box: Option<HostBoxEntry>,
    /// The revision after the command.
    pub revision: u64,
}