1use std::cmp;
2use sum_tree::{Bias, SumTree};
3
4use crate::UndoOperation;
5
6#[derive(Copy, Clone, Debug)]
7struct UndoMapEntry {
8 key: UndoMapKey,
9 undo_count: u32,
10}
11
12impl sum_tree::Item for UndoMapEntry {
13 type Summary = UndoMapKey;
14
15 fn summary(&self) -> Self::Summary {
16 self.key
17 }
18}
19
20impl sum_tree::KeyedItem for UndoMapEntry {
21 type Key = UndoMapKey;
22
23 fn key(&self) -> Self::Key {
24 self.key
25 }
26}
27
28#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
29struct UndoMapKey {
30 edit_id: clock::Local,
31 undo_id: clock::Local,
32}
33
34impl sum_tree::Summary for UndoMapKey {
35 type Context = ();
36
37 fn add_summary(&mut self, summary: &Self, _: &Self::Context) {
38 *self = cmp::max(*self, *summary);
39 }
40}
41
42#[derive(Clone, Default)]
43pub struct UndoMap(SumTree<UndoMapEntry>);
44
45impl UndoMap {
46 pub fn insert(&mut self, undo: &UndoOperation) {
47 let edits = undo
48 .counts
49 .iter()
50 .map(|(edit_id, count)| {
51 sum_tree::Edit::Insert(UndoMapEntry {
52 key: UndoMapKey {
53 edit_id: *edit_id,
54 undo_id: undo.id,
55 },
56 undo_count: *count,
57 })
58 })
59 .collect::<Vec<_>>();
60 self.0.edit(edits, &());
61 }
62
63 pub fn is_undone(&self, edit_id: clock::Local) -> bool {
64 self.undo_count(edit_id) % 2 == 1
65 }
66
67 pub fn was_undone(&self, edit_id: clock::Local, version: &clock::Global) -> bool {
68 let mut cursor = self.0.cursor::<UndoMapKey>();
69 cursor.seek(
70 &UndoMapKey {
71 edit_id,
72 undo_id: Default::default(),
73 },
74 Bias::Left,
75 &(),
76 );
77
78 let mut undo_count = 0;
79 for entry in cursor {
80 if entry.key.edit_id != edit_id {
81 break;
82 }
83
84 if version.observed(entry.key.undo_id) {
85 undo_count = cmp::max(undo_count, entry.undo_count);
86 }
87 }
88
89 undo_count % 2 == 1
90 }
91
92 pub fn undo_count(&self, edit_id: clock::Local) -> u32 {
93 let mut cursor = self.0.cursor::<UndoMapKey>();
94 cursor.seek(
95 &UndoMapKey {
96 edit_id,
97 undo_id: Default::default(),
98 },
99 Bias::Left,
100 &(),
101 );
102
103 let mut undo_count = 0;
104 for entry in cursor {
105 if entry.key.edit_id != edit_id {
106 break;
107 }
108
109 undo_count = cmp::max(undo_count, entry.undo_count);
110 }
111 undo_count
112 }
113}