1use std::time::{
2 Duration,
3 Instant,
4};
5
6use ropey::Rope;
7
8#[derive(Clone, Debug, PartialEq)]
9pub enum HistoryChange {
10 InsertChar {
11 idx: usize,
12 len: usize,
13 ch: char,
14 },
15 InsertText {
16 idx: usize,
17 len: usize,
18 text: String,
19 },
20 Remove {
21 idx: usize,
22 len: usize,
23 text: String,
24 },
25}
26
27#[derive(Clone, Debug, PartialEq)]
28pub struct HistoryTransaction {
29 pub timestamp: Instant,
30 pub changes: Vec<HistoryChange>,
31}
32
33#[derive(Clone, Debug)]
34pub struct EditorHistory {
35 pub transactions: Vec<HistoryTransaction>,
36 pub current_transaction: usize,
37 pub version: usize,
39 transaction_treshold_groping: Duration,
41}
42
43impl EditorHistory {
44 pub fn new(transaction_treshold_groping: Duration) -> Self {
45 Self {
46 transactions: Vec::default(),
47 current_transaction: 0,
48 version: 0,
49 transaction_treshold_groping,
50 }
51 }
52
53 pub fn push_change(&mut self, change: HistoryChange) {
54 if self.can_redo() {
55 self.transactions.drain(self.current_transaction..);
56 }
57
58 let last_transaction = self
59 .transactions
60 .get_mut(self.current_transaction.saturating_sub(1));
61 if let Some(last_transaction) = last_transaction
62 && last_transaction.timestamp.elapsed() <= self.transaction_treshold_groping
63 {
64 last_transaction.changes.push(change);
65 last_transaction.timestamp = Instant::now();
66 return;
67 }
68
69 self.transactions.push(HistoryTransaction {
70 timestamp: Instant::now(),
71 changes: vec![change],
72 });
73
74 self.current_transaction = self.transactions.len();
75 self.version += 1;
76 }
77
78 pub fn current_change(&self) -> usize {
79 self.current_transaction
80 }
81
82 pub fn any_pending_changes(&self) -> usize {
83 self.transactions.len() - self.current_transaction
84 }
85
86 pub fn can_undo(&self) -> bool {
87 self.current_transaction > 0
88 }
89
90 pub fn can_redo(&self) -> bool {
91 self.current_transaction < self.transactions.len()
92 }
93
94 pub fn undo(&mut self, rope: &mut Rope) -> Option<usize> {
95 if !self.can_undo() {
96 return None;
97 }
98
99 let last_transaction = self.transactions.get(self.current_transaction - 1);
100 if let Some(last_transaction) = last_transaction {
101 let mut idx_end = None;
102 for change in last_transaction.changes.iter().rev() {
103 idx_end.replace(match change {
104 HistoryChange::Remove { idx, text, len } => {
105 let start = rope.utf16_cu_to_char(*idx);
106 rope.insert(start, text);
107 *idx + len
108 }
109 HistoryChange::InsertChar { idx, len, .. } => {
110 let start = rope.utf16_cu_to_char(*idx);
111 let end = rope.utf16_cu_to_char(*idx + len);
112 rope.remove(start..end);
113 *idx
114 }
115 HistoryChange::InsertText { idx, len, .. } => {
116 let start = rope.utf16_cu_to_char(*idx);
117 let end = rope.utf16_cu_to_char(*idx + len);
118 rope.remove(start..end);
119 *idx
120 }
121 });
122 }
123
124 self.current_transaction -= 1;
125 self.version += 1;
126 idx_end
127 } else {
128 None
129 }
130 }
131
132 pub fn redo(&mut self, rope: &mut Rope) -> Option<usize> {
133 if !self.can_redo() {
134 return None;
135 }
136
137 let last_transaction = self.transactions.get(self.current_transaction);
138 if let Some(last_transaction) = last_transaction {
139 let mut idx_end = None;
140 for change in &last_transaction.changes {
141 idx_end.replace(match change {
142 HistoryChange::Remove { idx, len, .. } => {
143 let start = rope.utf16_cu_to_char(*idx);
144 let end = rope.utf16_cu_to_char(*idx + len);
145 rope.remove(start..end);
146 *idx
147 }
148 HistoryChange::InsertChar { idx, ch, len } => {
149 let start = rope.utf16_cu_to_char(*idx);
150 rope.insert_char(start, *ch);
151 *idx + len
152 }
153 HistoryChange::InsertText { idx, text, len } => {
154 let start = rope.utf16_cu_to_char(*idx);
155 rope.insert(start, text);
156 *idx + len
157 }
158 });
159 }
160 self.current_transaction += 1;
161 self.version += 1;
162 idx_end
163 } else {
164 None
165 }
166 }
167
168 pub fn clear_redos(&mut self) {
169 if self.can_redo() {
170 self.transactions.drain(self.current_transaction..);
171 }
172 }
173
174 pub fn clear(&mut self) {
175 self.transactions.clear();
176 self.current_transaction = 0;
177 self.version = 0;
178 }
179}
180
181#[cfg(test)]
182mod test {
183 use std::time::Duration;
184
185 use ropey::Rope;
186
187 use super::{
188 EditorHistory,
189 HistoryChange,
190 };
191
192 #[test]
193 fn test() {
194 let mut rope = Rope::new();
195 let mut history = EditorHistory::new(Duration::ZERO);
196
197 rope.insert(0, "Hello World");
199
200 assert!(!history.can_undo());
201 assert!(!history.can_redo());
202
203 rope.insert(11, "\n!!!!");
205 history.push_change(HistoryChange::InsertText {
206 idx: 11,
207 text: "\n!!!!".to_owned(),
208 len: "\n!!!!".len(),
209 });
210
211 assert!(history.can_undo());
212 assert!(!history.can_redo());
213 assert_eq!(rope.to_string(), "Hello World\n!!!!");
214
215 history.undo(&mut rope);
217
218 assert!(!history.can_undo());
219 assert!(history.can_redo());
220 assert_eq!(rope.to_string(), "Hello World");
221
222 rope.insert(11, "\n!!!!");
224 history.push_change(HistoryChange::InsertText {
225 idx: 11,
226 text: "\n!!!!".to_owned(),
227 len: "\n!!!!".len(),
228 });
229 rope.insert(16, "\n!!!!");
230 history.push_change(HistoryChange::InsertText {
231 idx: 16,
232 text: "\n!!!!".to_owned(),
233 len: "\n!!!!".len(),
234 });
235 rope.insert(21, "\n!!!!");
236 history.push_change(HistoryChange::InsertText {
237 idx: 21,
238 text: "\n!!!!".to_owned(),
239 len: "\n!!!!".len(),
240 });
241
242 assert_eq!(history.any_pending_changes(), 0);
243 assert!(history.can_undo());
244 assert!(!history.can_redo());
245 assert_eq!(rope.to_string(), "Hello World\n!!!!\n!!!!\n!!!!");
246
247 history.undo(&mut rope);
249 assert_eq!(history.any_pending_changes(), 1);
250 assert_eq!(rope.to_string(), "Hello World\n!!!!\n!!!!");
251 history.undo(&mut rope);
252 assert_eq!(history.any_pending_changes(), 2);
253 assert_eq!(rope.to_string(), "Hello World\n!!!!");
254 history.undo(&mut rope);
255 assert_eq!(history.any_pending_changes(), 3);
256 assert_eq!(rope.to_string(), "Hello World");
257
258 assert!(!history.can_undo());
259 assert!(history.can_redo());
260
261 history.redo(&mut rope);
263 assert_eq!(rope.to_string(), "Hello World\n!!!!");
264 history.redo(&mut rope);
265 assert_eq!(rope.to_string(), "Hello World\n!!!!\n!!!!");
266 history.redo(&mut rope);
267 assert_eq!(rope.to_string(), "Hello World\n!!!!\n!!!!\n!!!!");
268
269 assert_eq!(history.any_pending_changes(), 0);
270 assert!(history.can_undo());
271 assert!(!history.can_redo());
272
273 history.undo(&mut rope);
275 assert_eq!(rope.to_string(), "Hello World\n!!!!\n!!!!");
276 assert_eq!(history.any_pending_changes(), 1);
277 history.undo(&mut rope);
278 assert_eq!(rope.to_string(), "Hello World\n!!!!");
279 assert_eq!(history.any_pending_changes(), 2);
280
281 rope.insert_char(0, '.');
283 history.push_change(HistoryChange::InsertChar {
284 idx: 0,
285 ch: '.',
286 len: 1,
287 });
288 assert_eq!(history.any_pending_changes(), 0);
289 }
290}