1use crate::model::event::BufferId;
18
19#[derive(Clone, Debug, PartialEq)]
21pub struct PositionEntry {
22 pub buffer_id: BufferId,
24
25 pub position: usize,
27
28 pub anchor: Option<usize>,
30}
31
32impl PositionEntry {
33 pub fn new(buffer_id: BufferId, position: usize, anchor: Option<usize>) -> Self {
35 Self {
36 buffer_id,
37 position,
38 anchor,
39 }
40 }
41}
42
43#[derive(Clone, Debug)]
45struct PendingMovement {
46 start_entry: PositionEntry,
48}
49
50const LARGE_JUMP_THRESHOLD: usize = 50;
53
54pub struct PositionHistory {
63 entries: Vec<PositionEntry>,
65
66 current_index: Option<usize>,
69
70 max_entries: usize,
72
73 pending_movement: Option<PendingMovement>,
76}
77
78impl PositionHistory {
79 pub fn new() -> Self {
81 Self::with_capacity(100)
82 }
83
84 pub fn with_capacity(max_entries: usize) -> Self {
86 Self {
87 entries: Vec::new(),
88 current_index: None,
89 max_entries,
90 pending_movement: None,
91 }
92 }
93
94 pub fn record_movement(&mut self, buffer_id: BufferId, position: usize, anchor: Option<usize>) {
102 let entry = PositionEntry::new(buffer_id, position, anchor);
103
104 if let Some(pending) = &mut self.pending_movement {
105 if pending.start_entry.buffer_id == buffer_id {
107 let distance = position.abs_diff(pending.start_entry.position);
109
110 if distance <= LARGE_JUMP_THRESHOLD {
112 return;
114 }
115 }
116
117 self.commit_pending_movement();
119 }
120
121 self.pending_movement = Some(PendingMovement { start_entry: entry });
123 }
124
125 pub fn commit_pending_movement(&mut self) {
131 if let Some(pending) = self.pending_movement.take() {
132 self.push(pending.start_entry);
136 }
137 }
138
139 pub fn push(&mut self, entry: PositionEntry) {
149 if let Some(current_idx) = self.current_index {
152 self.entries.truncate(current_idx + 1);
153 }
154
155 if let Some(current_idx) = self.current_index {
157 if current_idx < self.entries.len() && self.entries[current_idx] == entry {
158 return;
159 }
160 }
161
162 self.entries.push(entry);
164
165 if self.entries.len() > self.max_entries {
167 self.entries.remove(0);
168 }
169
170 self.current_index = Some(self.entries.len() - 1);
172 }
173
174 pub fn back(&mut self) -> Option<&PositionEntry> {
179 self.commit_pending_movement();
181
182 if self.entries.is_empty() {
183 return None;
184 }
185
186 match self.current_index {
187 None => None,
188 Some(0) => None, Some(idx) => {
190 self.current_index = Some(idx - 1);
191 Some(&self.entries[idx - 1])
192 }
193 }
194 }
195
196 pub fn forward(&mut self) -> Option<&PositionEntry> {
200 if self.entries.is_empty() {
201 return None;
202 }
203
204 match self.current_index {
205 None => None,
206 Some(idx) if idx >= self.entries.len() - 1 => None, Some(idx) => {
208 self.current_index = Some(idx + 1);
209 Some(&self.entries[idx + 1])
210 }
211 }
212 }
213
214 pub fn can_go_back(&self) -> bool {
216 match self.current_index {
217 Some(idx) => idx > 0,
218 None => false,
219 }
220 }
221
222 pub fn can_go_forward(&self) -> bool {
224 match self.current_index {
225 Some(idx) => idx < self.entries.len() - 1,
226 None => false,
227 }
228 }
229
230 pub fn current(&self) -> Option<&PositionEntry> {
232 self.current_index.and_then(|idx| self.entries.get(idx))
233 }
234
235 pub fn clear(&mut self) {
237 self.entries.clear();
238 self.current_index = None;
239 }
240
241 pub fn len(&self) -> usize {
243 self.entries.len()
244 }
245
246 pub fn is_empty(&self) -> bool {
248 self.entries.is_empty()
249 }
250
251 pub fn current_index(&self) -> Option<usize> {
253 self.current_index
254 }
255}
256
257impl Default for PositionHistory {
258 fn default() -> Self {
259 Self::new()
260 }
261}
262
263#[cfg(test)]
264mod tests {
265 use super::*;
266
267 fn make_entry(buffer_id: usize, position: usize) -> PositionEntry {
268 PositionEntry::new(BufferId(buffer_id), position, None)
269 }
270
271 #[test]
272 fn test_new_history_is_empty() {
273 let history = PositionHistory::new();
274 assert!(history.is_empty());
275 assert_eq!(history.len(), 0);
276 assert!(!history.can_go_back());
277 assert!(!history.can_go_forward());
278 }
279
280 #[test]
281 fn test_push_single_entry() {
282 let mut history = PositionHistory::new();
283 let entry = make_entry(1, 10);
284
285 history.push(entry.clone());
286
287 assert_eq!(history.len(), 1);
288 assert_eq!(history.current(), Some(&entry));
289 assert!(!history.can_go_back());
290 assert!(!history.can_go_forward());
291 }
292
293 #[test]
294 fn test_push_multiple_entries() {
295 let mut history = PositionHistory::new();
296 let entry1 = make_entry(1, 10);
297 let entry2 = make_entry(1, 20);
298 let entry3 = make_entry(2, 5);
299
300 history.push(entry1.clone());
301 history.push(entry2.clone());
302 history.push(entry3.clone());
303
304 assert_eq!(history.len(), 3);
305 assert_eq!(history.current(), Some(&entry3));
306 assert!(history.can_go_back());
307 assert!(!history.can_go_forward());
308 }
309
310 #[test]
311 fn test_back_navigation() {
312 let mut history = PositionHistory::new();
313 let entry1 = make_entry(1, 10);
314 let entry2 = make_entry(1, 20);
315 let entry3 = make_entry(2, 5);
316
317 history.push(entry1.clone());
318 history.push(entry2.clone());
319 history.push(entry3.clone());
320
321 let back1 = history.back();
323 assert_eq!(back1, Some(&entry2));
324 assert_eq!(history.current(), Some(&entry2));
325 assert!(history.can_go_back());
326 assert!(history.can_go_forward());
327
328 let back2 = history.back();
330 assert_eq!(back2, Some(&entry1));
331 assert_eq!(history.current(), Some(&entry1));
332 assert!(!history.can_go_back());
333 assert!(history.can_go_forward());
334
335 let back3 = history.back();
337 assert_eq!(back3, None);
338 assert_eq!(history.current(), Some(&entry1));
339 }
340
341 #[test]
342 fn test_forward_navigation() {
343 let mut history = PositionHistory::new();
344 let entry1 = make_entry(1, 10);
345 let entry2 = make_entry(1, 20);
346 let entry3 = make_entry(2, 5);
347
348 history.push(entry1.clone());
349 history.push(entry2.clone());
350 history.push(entry3.clone());
351
352 history.back();
354 history.back();
355 assert_eq!(history.current(), Some(&entry1));
356
357 let fwd1 = history.forward();
359 assert_eq!(fwd1, Some(&entry2));
360 assert_eq!(history.current(), Some(&entry2));
361
362 let fwd2 = history.forward();
364 assert_eq!(fwd2, Some(&entry3));
365 assert_eq!(history.current(), Some(&entry3));
366
367 let fwd3 = history.forward();
369 assert_eq!(fwd3, None);
370 assert_eq!(history.current(), Some(&entry3));
371 }
372
373 #[test]
374 fn test_push_truncates_forward_history() {
375 let mut history = PositionHistory::new();
376 let entry1 = make_entry(1, 10);
377 let entry2 = make_entry(1, 20);
378 let entry3 = make_entry(2, 5);
379 let entry4 = make_entry(2, 15);
380
381 history.push(entry1.clone());
382 history.push(entry2.clone());
383 history.push(entry3.clone());
384
385 history.back();
387 history.back();
388 assert_eq!(history.current(), Some(&entry1));
389
390 history.push(entry4.clone());
392
393 assert_eq!(history.len(), 2);
394 assert_eq!(history.current(), Some(&entry4));
395 assert!(history.can_go_back());
396 assert!(!history.can_go_forward());
397
398 let back = history.back();
400 assert_eq!(back, Some(&entry1));
401 }
402
403 #[test]
404 fn test_duplicate_consecutive_entries_not_added() {
405 let mut history = PositionHistory::new();
406 let entry1 = make_entry(1, 10);
407
408 history.push(entry1.clone());
409 history.push(entry1.clone());
410 history.push(entry1.clone());
411
412 assert_eq!(history.len(), 1);
413 }
414
415 #[test]
416 fn test_max_entries_limit() {
417 let mut history = PositionHistory::with_capacity(3);
418
419 for i in 0..5 {
420 history.push(make_entry(1, i * 10));
421 }
422
423 assert_eq!(history.len(), 3);
424 assert_eq!(history.current(), Some(&make_entry(1, 40)));
426
427 history.back();
428 assert_eq!(history.current(), Some(&make_entry(1, 30)));
429
430 history.back();
431 assert_eq!(history.current(), Some(&make_entry(1, 20)));
432 }
433
434 #[test]
435 fn test_clear() {
436 let mut history = PositionHistory::new();
437
438 history.push(make_entry(1, 10));
439 history.push(make_entry(1, 20));
440
441 assert_eq!(history.len(), 2);
442
443 history.clear();
444
445 assert!(history.is_empty());
446 assert_eq!(history.len(), 0);
447 assert_eq!(history.current(), None);
448 assert!(!history.can_go_back());
449 assert!(!history.can_go_forward());
450 }
451}