Expand description
Undo/Redo command history framework.
This module provides infrastructure for reversible operations in FrankenTUI applications. It implements the Command Pattern with support for:
- Reversibility: Every command can be undone and redone
- Merging: Consecutive similar commands batch together (e.g., typing)
- Memory management: Commands report size for bounded history
- Batching: Multiple commands group into atomic operations
§Architecture
┌─────────────────────────────────────────────────────────────────┐
│ UndoStack │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Undo Stack │ │ Redo Stack │ │
│ │ ┌────────────┐ │ │ ┌────────────┐ │ │
│ │ │ CommandN │ │ undo() │ │ Command1 │ │ │
│ │ ├────────────┤ │ ──────► │ ├────────────┤ │ │
│ │ │ Command2 │ │ │ │ Command2 │ │ │
│ │ ├────────────┤ │ ◄────── │ ├────────────┤ │ │
│ │ │ Command1 │ │ redo() │ │ CommandN │ │ │
│ │ └────────────┘ │ │ └────────────┘ │ │
│ └──────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────────┘§Quick Start
ⓘ
use ftui_runtime::undo::{UndoableCmd, CommandMetadata, TextInsertCmd, WidgetId};
// Create a command
let cmd = TextInsertCmd::new(WidgetId::new(1), 0, "Hello")
.with_apply(|id, pos, text| {
// Apply the insertion
Ok(())
})
.with_remove(|id, pos, len| {
// Remove the insertion
Ok(())
});
// Execute the command
cmd.execute()?;
// Later, undo it
cmd.undo()?;§Module Structure
command: CoreUndoableCmdtrait and built-in commands
§Design Notes
§Why Commands Store Callbacks
Commands need to interact with widget state, but we can’t store references to widgets (lifetime issues). Instead, commands store callbacks that are set by the widget when the command is created. This allows:
- Commands to be stored in history (owned, not borrowed)
- Widgets to control how operations are applied
- Commands to work with any widget implementation
§Merge Strategy
Command merging reduces memory usage and makes undo more natural:
- Typing “hello” creates 5 insert commands
- Merged, they become 1 command that inserts “hello”
- Undo removes “hello” in one step (more intuitive)
Merge decisions use:
- Time window (500ms default)
- Word boundaries (optional)
- Size limits (prevent unbounded growth)
§Memory Budget
Every command reports its size via size_bytes(). The undo stack uses
this to enforce memory limits:
- Default: 10MB history
- Oldest commands evicted when limit exceeded
- Commands can estimate or measure their size
Re-exports§
pub use command::CommandBatch;pub use command::CommandError;pub use command::CommandMetadata;pub use command::CommandResult;pub use command::CommandSource;pub use command::MergeConfig;pub use command::TextDeleteCmd;pub use command::TextInsertCmd;pub use command::TextReplaceCmd;pub use command::UndoableCmd;pub use command::WidgetId;pub use history::HistoryConfig;pub use history::HistoryManager;pub use snapshot_store::SnapshotConfig;pub use snapshot_store::SnapshotStore;pub use transaction::Transaction;pub use transaction::TransactionScope;
Modules§
- command
- Undoable command infrastructure for the undo/redo system.
- history
- History stack for undo/redo operations.
- snapshot_
store - Persistent-snapshot undo/redo store.
- transaction
- Transaction support for grouping multiple commands atomically.