use {
crate::mm::{Edit, Position},
std::time::{Duration, Instant},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum EditOrigin {
Client(usize),
#[default]
System,
}
#[derive(Debug, Clone)]
pub struct UndoNode {
edits: Vec<Edit>,
cursor_before: Position,
cursor_after: Position,
timestamp: Instant,
parent: Option<usize>,
children: Vec<usize>,
seq_num: u64,
origin: EditOrigin,
}
impl UndoNode {
#[must_use]
pub fn edits(&self) -> &[Edit] {
&self.edits
}
#[must_use]
pub const fn cursor_before(&self) -> Position {
self.cursor_before
}
#[must_use]
pub const fn cursor_after(&self) -> Position {
self.cursor_after
}
#[must_use]
pub const fn timestamp(&self) -> Instant {
self.timestamp
}
#[must_use]
pub const fn seq_num(&self) -> u64 {
self.seq_num
}
#[must_use]
pub const fn is_root(&self) -> bool {
self.parent.is_none()
}
#[must_use]
pub const fn branch_count(&self) -> usize {
self.children.len()
}
#[must_use]
pub const fn parent(&self) -> Option<usize> {
self.parent
}
#[must_use]
pub fn children(&self) -> &[usize] {
&self.children
}
#[must_use]
pub const fn origin(&self) -> EditOrigin {
self.origin
}
}
#[derive(Debug, Clone)]
pub struct UndoResult {
pub edits: Vec<Edit>,
pub cursor: Position,
}
#[derive(Debug, Clone)]
pub struct UndoTree {
nodes: Vec<UndoNode>,
current: usize,
seq_counter: u64,
max_nodes: usize,
active_branches: Vec<usize>,
}
impl Default for UndoTree {
fn default() -> Self {
Self::new()
}
}
impl UndoTree {
pub const DEFAULT_MAX_NODES: usize = 10000;
#[must_use]
pub fn new() -> Self {
Self::with_max_nodes(Self::DEFAULT_MAX_NODES)
}
#[must_use]
pub fn with_max_nodes(max_nodes: usize) -> Self {
let root = UndoNode {
edits: Vec::new(),
cursor_before: Position::default(),
cursor_after: Position::default(),
timestamp: Instant::now(),
parent: None,
children: Vec::new(),
seq_num: 0,
origin: EditOrigin::System,
};
Self {
nodes: vec![root],
current: 0,
seq_counter: 0,
max_nodes: max_nodes.max(1), active_branches: vec![0],
}
}
pub fn push(&mut self, edits: Vec<Edit>, cursor_before: Position, cursor_after: Position) {
self.push_with_origin(edits, cursor_before, cursor_after, EditOrigin::System);
}
pub fn push_with_origin(
&mut self,
edits: Vec<Edit>,
cursor_before: Position,
cursor_after: Position,
origin: EditOrigin,
) {
if edits.is_empty() {
return;
}
self.seq_counter += 1;
let new_node = UndoNode {
edits,
cursor_before,
cursor_after,
timestamp: Instant::now(),
parent: Some(self.current),
children: Vec::new(),
seq_num: self.seq_counter,
origin,
};
let new_idx = self.nodes.len();
self.nodes.push(new_node);
self.nodes[self.current].children.push(new_idx);
let branch_idx = self.nodes[self.current].children.len() - 1;
while self.active_branches.len() <= self.current {
self.active_branches.push(0);
}
self.active_branches[self.current] = branch_idx;
while self.active_branches.len() <= new_idx {
self.active_branches.push(0);
}
self.current = new_idx;
self.prune_if_needed();
}
pub fn undo(&mut self) -> Option<UndoResult> {
let current_node = &self.nodes[self.current];
let parent_idx = current_node.parent?;
let result = UndoResult {
edits: current_node.edits.iter().rev().map(Edit::inverse).collect(),
cursor: current_node.cursor_before,
};
self.current = parent_idx;
Some(result)
}
pub fn redo(&mut self) -> Option<UndoResult> {
let current_node = &self.nodes[self.current];
if current_node.children.is_empty() {
return None;
}
let branch_idx = self
.active_branches
.get(self.current)
.copied()
.unwrap_or(0)
.min(current_node.children.len() - 1);
let child_idx = current_node.children[branch_idx];
let child_node = &self.nodes[child_idx];
let result = UndoResult {
edits: child_node.edits.clone(),
cursor: child_node.cursor_after,
};
self.current = child_idx;
Some(result)
}
#[cfg_attr(coverage_nightly, coverage(off))]
pub fn redo_branch(&mut self, branch_idx: usize) -> Option<UndoResult> {
let current_node = &self.nodes[self.current];
if branch_idx >= current_node.children.len() {
return None;
}
if self.current < self.active_branches.len() {
self.active_branches[self.current] = branch_idx;
}
let child_idx = current_node.children[branch_idx];
let child_node = &self.nodes[child_idx];
let result = UndoResult {
edits: child_node.edits.clone(),
cursor: child_node.cursor_after,
};
self.current = child_idx;
Some(result)
}
#[must_use]
pub fn branches(&self) -> &[usize] {
&self.nodes[self.current].children
}
pub fn switch_branch(&mut self, branch_idx: usize) -> bool {
let current_node = &self.nodes[self.current];
if branch_idx >= current_node.children.len() {
return false;
}
while self.active_branches.len() <= self.current {
self.active_branches.push(0);
}
self.active_branches[self.current] = branch_idx;
true
}
#[must_use]
pub fn can_undo(&self) -> bool {
self.nodes[self.current].parent.is_some()
}
#[must_use]
pub fn can_redo(&self) -> bool {
!self.nodes[self.current].children.is_empty()
}
#[must_use]
pub fn current_node(&self) -> &UndoNode {
&self.nodes[self.current]
}
#[must_use]
pub const fn current_index(&self) -> usize {
self.current
}
#[must_use]
pub const fn node_count(&self) -> usize {
self.nodes.len()
}
#[must_use]
pub fn node(&self, index: usize) -> Option<&UndoNode> {
self.nodes.get(index)
}
#[must_use]
pub const fn node_indices(&self) -> std::ops::Range<usize> {
0..self.nodes.len()
}
#[must_use]
pub fn active_branch_at(&self, node_index: usize) -> Option<usize> {
self.active_branches.get(node_index).copied()
}
#[must_use]
pub const fn max_nodes(&self) -> usize {
self.max_nodes
}
pub fn set_max_nodes(&mut self, max: usize) {
self.max_nodes = max.max(1);
self.prune_if_needed();
}
#[must_use]
pub const fn seq_counter(&self) -> u64 {
self.seq_counter
}
#[must_use]
pub fn edits_since(&self, from_idx: usize) -> Option<Vec<&Edit>> {
if from_idx >= self.nodes.len() {
return None;
}
if from_idx == self.current {
return Some(Vec::new());
}
let mut path_indices = Vec::new();
let mut idx = self.current;
loop {
if idx == from_idx {
break;
}
path_indices.push(idx);
match self.nodes[idx].parent {
Some(parent) => idx = parent,
None => return None, }
}
path_indices.reverse();
let mut edits = Vec::new();
for node_idx in path_indices {
for edit in &self.nodes[node_idx].edits {
if !edit.is_empty() {
edits.push(edit);
}
}
}
Some(edits)
}
#[cfg_attr(coverage_nightly, coverage(off))]
fn prune_if_needed(&mut self) {
if self.nodes.len() <= self.max_nodes {
return;
}
let mut protected = vec![false; self.nodes.len()];
let mut idx = self.current;
loop {
protected[idx] = true;
match self.nodes[idx].parent {
Some(parent) => idx = parent,
None => break,
}
}
let to_remove = self.nodes.len() - self.max_nodes;
let mut removed = 0;
let mut remove_indices = Vec::new();
for (i, node) in self.nodes.iter().enumerate() {
if removed >= to_remove {
break;
}
if !protected[i] && node.children.is_empty() {
remove_indices.push(i);
removed += 1;
}
}
for &idx in remove_indices.iter().rev() {
self.remove_node(idx);
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
fn remove_node(&mut self, idx: usize) {
if idx == 0 || idx == self.current {
return; }
if let Some(parent_idx) = self.nodes[idx].parent {
self.nodes[parent_idx].children.retain(|&c| c != idx);
}
for node in &mut self.nodes {
if let Some(ref mut parent) = node.parent
&& *parent > idx
{
*parent -= 1;
}
for child in &mut node.children {
if *child > idx {
*child -= 1;
}
}
}
if self.current > idx {
self.current -= 1;
}
for branch in &mut self.active_branches {
if *branch > idx {
*branch -= 1;
}
}
self.nodes.remove(idx);
while self.active_branches.len() > self.nodes.len() {
self.active_branches.pop();
}
}
pub fn clear(&mut self) {
let root_cursor = self.nodes[0].cursor_after;
*self = Self::with_max_nodes(self.max_nodes);
self.nodes[0].cursor_after = root_cursor;
}
#[must_use]
#[allow(clippy::type_complexity)] pub fn from_serializable(
nodes_data: Vec<(
Vec<Edit>, // edits
Position, // cursor_before
Position, // cursor_after
Duration, // relative_time from root
Option<usize>, // parent
Vec<usize>, // children
u64, // seq_num
EditOrigin, // origin
)>,
current: usize,
seq_counter: u64,
max_nodes: usize,
active_branches: Vec<usize>,
) -> Self {
assert!(!nodes_data.is_empty(), "UndoTree must have at least a root node");
let now = Instant::now();
let nodes: Vec<UndoNode> = nodes_data
.into_iter()
.map(
|(
edits,
cursor_before,
cursor_after,
relative_time,
parent,
children,
seq_num,
origin,
)| {
UndoNode {
edits,
cursor_before,
cursor_after,
timestamp: now + relative_time,
parent,
children,
seq_num,
origin,
}
},
)
.collect();
Self {
nodes,
current,
seq_counter,
max_nodes: max_nodes.max(1), active_branches,
}
}
}