use std::cmp; use sum_tree::{Bias, SumTree}; use crate::UndoOperation; #[derive(Copy, Clone, Debug)] struct UndoMapEntry { key: UndoMapKey, undo_count: u32, } impl sum_tree::Item for UndoMapEntry { type Summary = UndoMapKey; fn summary(&self) -> Self::Summary { self.key } } impl sum_tree::KeyedItem for UndoMapEntry { type Key = UndoMapKey; fn key(&self) -> Self::Key { self.key } } #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] struct UndoMapKey { edit_id: clock::Local, undo_id: clock::Local, } impl sum_tree::Summary for UndoMapKey { type Context = (); fn add_summary(&mut self, summary: &Self, _: &Self::Context) { *self = cmp::max(*self, *summary); } } #[derive(Clone, Default)] pub struct UndoMap(SumTree); impl UndoMap { pub fn insert(&mut self, undo: &UndoOperation) { let edits = undo .counts .iter() .map(|(edit_id, count)| { sum_tree::Edit::Insert(UndoMapEntry { key: UndoMapKey { edit_id: *edit_id, undo_id: undo.id, }, undo_count: *count, }) }) .collect::>(); self.0.edit(edits, &()); } pub fn is_undone(&self, edit_id: clock::Local) -> bool { self.undo_count(edit_id) % 2 == 1 } pub fn was_undone(&self, edit_id: clock::Local, version: &clock::Global) -> bool { let mut cursor = self.0.cursor::(); cursor.seek( &UndoMapKey { edit_id, undo_id: Default::default(), }, Bias::Left, &(), ); let mut undo_count = 0; for entry in cursor { if entry.key.edit_id != edit_id { break; } if version.observed(entry.key.undo_id) { undo_count = cmp::max(undo_count, entry.undo_count); } } undo_count % 2 == 1 } pub fn undo_count(&self, edit_id: clock::Local) -> u32 { let mut cursor = self.0.cursor::(); cursor.seek( &UndoMapKey { edit_id, undo_id: Default::default(), }, Bias::Left, &(), ); let mut undo_count = 0; for entry in cursor { if entry.key.edit_id != edit_id { break; } undo_count = cmp::max(undo_count, entry.undo_count); } undo_count } }