1use std::{collections::VecDeque, sync::Arc};
2
3use crate::sync::{AtomicU64, Mutex};
4use either::Either;
5use rustc_hash::{FxHashMap, FxHashSet};
6use loro_common::{
7 ContainerID, Counter, CounterSpan, HasIdSpan, IdSpan, LoroError, LoroResult, LoroValue, PeerID,
8};
9use tracing::{debug_span, info_span, instrument};
10
11use crate::{
12 change::{get_sys_timestamp, Timestamp},
13 cursor::{AbsolutePosition, Cursor},
14 delta::TreeExternalDiff,
15 event::{Diff, EventTriggerKind},
16 version::Frontiers,
17 ContainerDiff, DiffEvent, DocDiff, LoroDoc, Subscription,
18};
19
20#[derive(Debug, Clone, Default)]
24pub struct DiffBatch {
25 pub cid_to_events: FxHashMap<ContainerID, Diff>,
26 pub order: Vec<ContainerID>,
27}
28
29impl DiffBatch {
30 pub fn new(diff: Vec<DocDiff>) -> Self {
31 let mut map: FxHashMap<ContainerID, Diff> = Default::default();
32 let mut order: Vec<ContainerID> = Vec::with_capacity(diff.len());
33 for d in diff.into_iter() {
34 for item in d.diff.into_iter() {
35 let old = map.insert(item.id.clone(), item.diff);
36 assert!(old.is_none());
37 order.push(item.id.clone());
38 }
39 }
40
41 Self {
42 cid_to_events: map,
43 order,
44 }
45 }
46
47 pub fn compose(&mut self, other: &Self) {
48 if other.cid_to_events.is_empty() {
49 return;
50 }
51
52 for (id, diff) in other.iter() {
53 if let Some(this_diff) = self.cid_to_events.get_mut(id) {
54 this_diff.compose_ref(diff);
55 } else {
56 self.cid_to_events.insert(id.clone(), diff.clone());
57 self.order.push(id.clone());
58 }
59 }
60 }
61
62 pub fn transform(&mut self, other: &Self, left_priority: bool) {
63 if other.cid_to_events.is_empty() || self.cid_to_events.is_empty() {
64 return;
65 }
66
67 for (idx, diff) in self.cid_to_events.iter_mut() {
68 if let Some(b_diff) = other.cid_to_events.get(idx) {
69 diff.transform(b_diff, left_priority);
70 }
71 }
72 }
73
74 pub fn clear(&mut self) {
75 self.cid_to_events.clear();
76 self.order.clear();
77 }
78
79 pub fn iter(&self) -> impl Iterator<Item = (&ContainerID, &Diff)> + '_ {
80 self.order
81 .iter()
82 .map(|cid| (cid, self.cid_to_events.get(cid).unwrap()))
83 }
84
85 #[allow(clippy::should_implement_trait)]
86 pub fn into_iter(self) -> impl Iterator<Item = (ContainerID, Diff)> {
87 let mut cid_to_events = self.cid_to_events;
88 self.order.into_iter().map(move |cid| {
89 let d = cid_to_events.remove(&cid).unwrap();
90 (cid, d)
91 })
92 }
93}
94
95fn transform_cursor(
96 cursor_with_pos: &mut CursorWithPos,
97 remote_diff: &DiffBatch,
98 doc: &LoroDoc,
99 container_remap: &FxHashMap<ContainerID, ContainerID>,
100) {
101 let mut container_changed = false;
102 let mut cid = &cursor_with_pos.cursor.container;
103 while let Some(new_cid) = container_remap.get(cid) {
104 cid = new_cid;
105 container_changed = true;
106 }
107
108 if cursor_with_pos.cursor.id.is_none() {
109 if container_changed {
111 cursor_with_pos.cursor.container = cid.clone();
112 }
113 return;
114 }
115
116 if let Some(diff) = remote_diff.cid_to_events.get(cid) {
117 let new_pos = diff.transform_cursor(cursor_with_pos.pos.pos, false);
118 cursor_with_pos.pos.pos = new_pos;
119 };
120
121 let new_pos = cursor_with_pos.pos.pos;
122 match doc.get_handler(cid.clone()).unwrap() {
123 crate::handler::Handler::Text(h) => {
124 let Some(new_cursor) = h.get_cursor_internal(new_pos, cursor_with_pos.pos.side, false)
125 else {
126 return;
127 };
128
129 cursor_with_pos.cursor = new_cursor;
130 }
131 crate::handler::Handler::List(h) => {
132 let Some(new_cursor) = h.get_cursor(new_pos, cursor_with_pos.pos.side) else {
133 return;
134 };
135
136 cursor_with_pos.cursor = new_cursor;
137 }
138 crate::handler::Handler::MovableList(h) => {
139 let Some(new_cursor) = h.get_cursor(new_pos, cursor_with_pos.pos.side) else {
140 return;
141 };
142
143 cursor_with_pos.cursor = new_cursor;
144 }
145 crate::handler::Handler::Map(_) => {}
146 crate::handler::Handler::Tree(_) => {}
147 crate::handler::Handler::Unknown(_) => {}
148 #[cfg(feature = "counter")]
149 crate::handler::Handler::Counter(_) => {}
150 }
151}
152
153pub struct UndoManager {
160 peer: Arc<AtomicU64>,
161 container_remap: Arc<Mutex<FxHashMap<ContainerID, ContainerID>>>,
162 inner: Arc<Mutex<UndoManagerInner>>,
163 _peer_id_change_sub: Subscription,
164 _undo_sub: Subscription,
165 doc: LoroDoc,
166}
167
168impl std::fmt::Debug for UndoManager {
169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170 f.debug_struct("UndoManager")
171 .field("peer", &self.peer)
172 .field("container_remap", &self.container_remap)
173 .field("inner", &self.inner)
174 .finish()
175 }
176}
177
178#[derive(Debug, Clone, Copy, PartialEq, Eq)]
179pub enum UndoOrRedo {
180 Undo,
181 Redo,
182}
183
184impl UndoOrRedo {
185 fn opposite(&self) -> UndoOrRedo {
186 match self {
187 Self::Undo => Self::Redo,
188 Self::Redo => Self::Undo,
189 }
190 }
191}
192
193pub type OnPush = Box<
196 dyn for<'a> Fn(UndoOrRedo, CounterSpan, Option<DiffEvent<'a>>) -> UndoItemMeta + Send + Sync,
197>;
198pub type OnPop = Box<dyn Fn(UndoOrRedo, CounterSpan, UndoItemMeta) + Send + Sync>;
199
200struct UndoManagerInner {
201 next_counter: Option<Counter>,
202 undo_stack: Stack,
203 redo_stack: Stack,
204 processing_undo: bool,
205 last_undo_time: i64,
206 merge_interval_in_ms: i64,
207 max_stack_size: usize,
208 exclude_origin_prefixes: Vec<Box<str>>,
209 last_popped_selection: Option<Vec<CursorWithPos>>,
210 on_push: Option<OnPush>,
211 on_pop: Option<OnPop>,
212 group: Option<UndoGroup>,
213}
214
215impl std::fmt::Debug for UndoManagerInner {
216 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217 f.debug_struct("UndoManagerInner")
218 .field("latest_counter", &self.next_counter)
219 .field("undo_stack", &self.undo_stack)
220 .field("redo_stack", &self.redo_stack)
221 .field("processing_undo", &self.processing_undo)
222 .field("last_undo_time", &self.last_undo_time)
223 .field("merge_interval", &self.merge_interval_in_ms)
224 .field("max_stack_size", &self.max_stack_size)
225 .field("exclude_origin_prefixes", &self.exclude_origin_prefixes)
226 .field("group", &self.group)
227 .finish()
228 }
229}
230
231#[derive(Debug, Clone, Default)]
232struct UndoGroup {
233 start_counter: Counter,
234 affected_cids: FxHashSet<ContainerID>,
235}
236
237impl UndoGroup {
238 pub fn new(start_counter: Counter) -> Self {
239 Self {
240 start_counter,
241 affected_cids: Default::default(),
242 }
243 }
244}
245
246#[derive(Debug)]
247struct Stack {
248 stack: VecDeque<(VecDeque<StackItem>, Arc<Mutex<DiffBatch>>)>,
249 size: usize,
250}
251
252#[derive(Debug, Clone)]
253struct StackItem {
254 span: CounterSpan,
255 meta: UndoItemMeta,
256}
257
258#[derive(Debug, Default, Clone)]
263pub struct UndoItemMeta {
264 pub value: LoroValue,
265 pub cursors: Vec<CursorWithPos>,
266}
267
268#[derive(Debug, Clone)]
269pub struct CursorWithPos {
270 pub cursor: Cursor,
271 pub pos: AbsolutePosition,
272}
273
274impl UndoItemMeta {
275 pub fn new() -> Self {
276 Self {
277 value: LoroValue::Null,
278 cursors: Default::default(),
279 }
280 }
281
282 pub fn add_cursor(&mut self, cursor: &Cursor) {
287 self.cursors.push(CursorWithPos {
288 cursor: cursor.clone(),
289 pos: AbsolutePosition {
290 pos: cursor.origin_pos,
291 side: cursor.side,
292 },
293 });
294 }
295
296 pub fn set_value(&mut self, value: LoroValue) {
297 self.value = value;
298 }
299}
300
301impl Stack {
302 pub fn new() -> Self {
303 let mut stack = VecDeque::new();
304 stack.push_back((VecDeque::new(), Arc::new(Mutex::new(Default::default()))));
305 Stack { stack, size: 0 }
306 }
307
308 fn peek_top_meta(&self) -> Option<UndoItemMeta> {
312 if self.is_empty() {
313 return None;
314 }
315
316 for (items, _) in self.stack.iter().rev() {
317 if let Some(item) = items.back() {
318 return Some(item.meta.clone());
319 }
320 }
321
322 None
323 }
324
325 pub fn pop(&mut self) -> Option<(StackItem, Arc<Mutex<DiffBatch>>)> {
326 while self.stack.back().unwrap().0.is_empty() && self.stack.len() > 1 {
327 let (_, diff) = self.stack.pop_back().unwrap();
328 let diff = diff.lock().unwrap();
329 if !diff.cid_to_events.is_empty() {
330 self.stack
331 .back_mut()
332 .unwrap()
333 .1
334 .lock()
335 .unwrap()
336 .compose(&diff);
337 }
338 }
339
340 if self.stack.len() == 1 && self.stack.back().unwrap().0.is_empty() {
341 self.stack.back_mut().unwrap().1.lock().unwrap().clear();
343 return None;
344 }
345
346 self.size -= 1;
347 let last = self.stack.back_mut().unwrap();
348 last.0.pop_back().map(|x| (x, last.1.clone()))
349 }
353
354 pub fn push(&mut self, span: CounterSpan, meta: UndoItemMeta) {
355 self.push_with_merge(span, meta, false, None)
356 }
357
358 pub fn push_with_merge(
359 &mut self,
360 span: CounterSpan,
361 meta: UndoItemMeta,
362 can_merge: bool,
363 group: Option<&UndoGroup>,
364 ) {
365 let last = self.stack.back_mut().unwrap();
366 let last_remote_diff = last.1.lock().unwrap();
367
368 let is_disjoint_group = group.is_some_and(|g| {
370 g.affected_cids.iter().all(|cid| {
371 last_remote_diff
372 .cid_to_events
373 .get(cid)
374 .is_none_or(|diff| diff.is_empty())
375 })
376 });
377
378 let should_create_new_entry =
380 !last_remote_diff.cid_to_events.is_empty() && !is_disjoint_group;
381
382 if should_create_new_entry {
383 drop(last_remote_diff);
385 let mut v = VecDeque::new();
386 v.push_back(StackItem { span, meta });
387 self.stack
388 .push_back((v, Arc::new(Mutex::new(DiffBatch::default()))));
389 self.size += 1;
390 return;
391 }
392
393 if can_merge {
395 if let Some(last_span) = last.0.back_mut() {
396 if last_span.span.end == span.start {
397 last_span.span.end = span.end;
399 return;
400 }
401 }
402 }
403
404 self.size += 1;
406 last.0.push_back(StackItem { span, meta });
407 }
408
409 pub fn compose_remote_event(&mut self, diff: &[&ContainerDiff]) {
410 if self.is_empty() {
411 return;
412 }
413
414 let remote_diff = &mut self.stack.back_mut().unwrap().1;
415 let mut remote_diff = remote_diff.lock().unwrap();
416 for e in diff {
417 if let Some(d) = remote_diff.cid_to_events.get_mut(&e.id) {
418 d.compose_ref(&e.diff);
419 } else {
420 remote_diff
421 .cid_to_events
422 .insert(e.id.clone(), e.diff.clone());
423 remote_diff.order.push(e.id.clone());
424 }
425 }
426 }
427
428 pub fn transform_based_on_this_delta(&mut self, diff: &DiffBatch) {
429 if self.is_empty() {
430 return;
431 }
432 let remote_diff = &mut self.stack.back_mut().unwrap().1;
433 remote_diff.lock().unwrap().transform(diff, false);
434 }
435
436 pub fn clear(&mut self) {
437 self.stack = VecDeque::new();
438 self.stack.push_back((VecDeque::new(), Default::default()));
439 self.size = 0;
440 }
441
442 pub fn is_empty(&self) -> bool {
443 self.size == 0
444 }
445
446 pub fn len(&self) -> usize {
447 self.size
448 }
449
450 fn pop_front(&mut self) {
451 if self.is_empty() {
452 return;
453 }
454
455 self.size -= 1;
456 let first = self.stack.front_mut().unwrap();
457 let f = first.0.pop_front();
458 assert!(f.is_some());
459 if first.0.is_empty() {
460 self.stack.pop_front();
461 }
462 }
463}
464
465impl Default for Stack {
466 fn default() -> Self {
467 Stack::new()
468 }
469}
470
471impl UndoManagerInner {
472 fn new(last_counter: Counter) -> Self {
473 Self {
474 next_counter: Some(last_counter),
475 undo_stack: Default::default(),
476 redo_stack: Default::default(),
477 processing_undo: false,
478 merge_interval_in_ms: 0,
479 last_undo_time: 0,
480 max_stack_size: usize::MAX,
481 exclude_origin_prefixes: vec![],
482 last_popped_selection: None,
483 on_pop: None,
484 on_push: None,
485 group: None,
486 }
487 }
488
489 fn is_disjoint_with_group(&self, diff: &[&ContainerDiff]) -> bool {
492 let Some(group) = &self.group else {
493 return false;
494 };
495
496 diff.iter().all(|d| !group.affected_cids.contains(&d.id))
497 }
498
499 fn record_checkpoint(&mut self, latest_counter: Counter, event: Option<DiffEvent>) {
500 let previous_counter = self.next_counter;
501
502 if Some(latest_counter) == self.next_counter {
503 return;
504 }
505
506 if self.next_counter.is_none() {
507 self.next_counter = Some(latest_counter);
508 return;
509 }
510
511 if let Some(group) = &mut self.group {
512 event.iter().for_each(|e| {
513 e.events.iter().for_each(|e| {
514 group.affected_cids.insert(e.id.clone());
515 })
516 });
517 }
518
519 let now = get_sys_timestamp() as Timestamp;
520 let span = CounterSpan::new(self.next_counter.unwrap(), latest_counter);
521 let meta = self
522 .on_push
523 .as_ref()
524 .map(|x| x(UndoOrRedo::Undo, span, event))
525 .unwrap_or_default();
526
527 let in_merge_interval = now - self.last_undo_time < self.merge_interval_in_ms;
529
530 let group_should_merge = self.group.is_some()
533 && match (
534 previous_counter,
535 self.group.as_ref().map(|g| g.start_counter),
536 ) {
537 (Some(previous), Some(active)) => previous != active,
538 _ => true,
539 };
540
541 let should_merge = !self.undo_stack.is_empty() && (in_merge_interval || group_should_merge);
542
543 if should_merge {
544 self.undo_stack
545 .push_with_merge(span, meta, true, self.group.as_ref());
546 } else {
547 self.last_undo_time = now;
548 self.undo_stack.push(span, meta);
549 }
550
551 self.next_counter = Some(latest_counter);
552 self.redo_stack.clear();
553 while self.undo_stack.len() > self.max_stack_size {
554 self.undo_stack.pop_front();
555 }
556 }
557}
558
559fn get_counter_end(doc: &LoroDoc, peer: PeerID) -> Counter {
560 doc.oplog()
561 .lock()
562 .unwrap()
563 .vv()
564 .get(&peer)
565 .cloned()
566 .unwrap_or(0)
567}
568
569impl UndoManager {
570 pub fn new(doc: &LoroDoc) -> Self {
571 let peer = Arc::new(AtomicU64::new(doc.peer_id()));
572 let peer_clone = peer.clone();
573 let peer_clone2 = peer.clone();
574 let inner = Arc::new(Mutex::new(UndoManagerInner::new(get_counter_end(
575 doc,
576 doc.peer_id(),
577 ))));
578 let inner_clone = inner.clone();
579 let inner_clone2 = inner.clone();
580 let remap_containers = Arc::new(Mutex::new(FxHashMap::default()));
581 let remap_containers_clone = remap_containers.clone();
582 let undo_sub = doc.subscribe_root(Arc::new(move |event| match event.event_meta.by {
583 EventTriggerKind::Local => {
584 let Ok(mut inner) = inner_clone.lock() else {
587 return;
588 };
589 if inner.processing_undo {
590 return;
591 }
592 if let Some(id) = event
593 .event_meta
594 .to
595 .iter()
596 .find(|x| x.peer == peer_clone.load(std::sync::atomic::Ordering::Relaxed))
597 {
598 if inner
599 .exclude_origin_prefixes
600 .iter()
601 .any(|x| event.event_meta.origin.starts_with(&**x))
602 {
603 inner.undo_stack.compose_remote_event(event.events);
607 inner.redo_stack.compose_remote_event(event.events);
608 inner.next_counter = Some(id.counter + 1);
609 } else {
610 inner.record_checkpoint(id.counter + 1, Some(event));
611 }
612 }
613 }
614 EventTriggerKind::Import => {
615 let mut inner = inner_clone.lock().unwrap();
616
617 for e in event.events {
618 if let Diff::Tree(tree) = &e.diff {
619 for item in &tree.diff {
620 let target = item.target;
621 if let TreeExternalDiff::Create { .. } = &item.action {
622 remap_containers_clone
625 .lock()
626 .unwrap()
627 .remove(&target.associated_meta_container());
628 }
629 }
630 }
631 }
632
633 let is_import_disjoint = inner.is_disjoint_with_group(event.events);
634
635 inner.undo_stack.compose_remote_event(event.events);
636 inner.redo_stack.compose_remote_event(event.events);
637
638 if !is_import_disjoint {
641 inner.group = None;
642 }
643 }
644 EventTriggerKind::Checkout => {
645 let mut inner = inner_clone.lock().unwrap();
646 inner.undo_stack.clear();
647 inner.redo_stack.clear();
648 inner.next_counter = None;
649 }
650 }));
651
652 let sub = doc.subscribe_peer_id_change(Box::new(move |id| {
653 let mut inner = inner_clone2.lock().unwrap();
654 inner.undo_stack.clear();
655 inner.redo_stack.clear();
656 inner.next_counter = Some(id.counter);
657 peer_clone2.store(id.peer, std::sync::atomic::Ordering::Relaxed);
658 true
659 }));
660
661 UndoManager {
662 peer,
663 container_remap: remap_containers,
664 inner,
665 _peer_id_change_sub: sub,
666 _undo_sub: undo_sub,
667 doc: doc.clone(),
668 }
669 }
670
671 pub fn group_start(&mut self) -> LoroResult<()> {
672 let mut inner = self.inner.lock().unwrap();
673
674 if inner.group.is_some() {
675 return Err(LoroError::UndoGroupAlreadyStarted);
676 }
677
678 inner.group = Some(UndoGroup::new(inner.next_counter.unwrap()));
679
680 Ok(())
681 }
682
683 pub fn group_end(&mut self) {
684 self.inner.lock().unwrap().group = None;
685 }
686
687 pub fn peer(&self) -> PeerID {
688 self.peer.load(std::sync::atomic::Ordering::Relaxed)
689 }
690
691 pub fn set_merge_interval(&mut self, interval: i64) {
692 self.inner.lock().unwrap().merge_interval_in_ms = interval;
693 }
694
695 pub fn set_max_undo_steps(&mut self, size: usize) {
696 self.inner.lock().unwrap().max_stack_size = size;
697 }
698
699 pub fn add_exclude_origin_prefix(&mut self, prefix: &str) {
700 self.inner
701 .lock()
702 .unwrap()
703 .exclude_origin_prefixes
704 .push(prefix.into());
705 }
706
707 pub fn record_new_checkpoint(&mut self) -> LoroResult<()> {
708 self.doc.with_barrier(|| {});
711 let counter = get_counter_end(&self.doc, self.peer());
712 self.inner.lock().unwrap().record_checkpoint(counter, None);
713 Ok(())
714 }
715
716 #[instrument(skip_all)]
717 pub fn undo(&mut self) -> LoroResult<bool> {
718 self.perform(
719 |x| &mut x.undo_stack,
720 |x| &mut x.redo_stack,
721 UndoOrRedo::Undo,
722 )
723 }
724
725 #[instrument(skip_all)]
726 pub fn redo(&mut self) -> LoroResult<bool> {
727 self.perform(
728 |x| &mut x.redo_stack,
729 |x| &mut x.undo_stack,
730 UndoOrRedo::Redo,
731 )
732 }
733
734 fn perform(
735 &mut self,
736 get_stack: impl Fn(&mut UndoManagerInner) -> &mut Stack,
737 get_opposite: impl Fn(&mut UndoManagerInner) -> &mut Stack,
738 kind: UndoOrRedo,
739 ) -> LoroResult<bool> {
740 let doc = &self.doc.clone();
741 self.record_new_checkpoint()?;
795 let end_counter = get_counter_end(doc, self.peer());
796 let mut top = {
797 let mut inner = self.inner.lock().unwrap();
798 inner.processing_undo = true;
799 get_stack(&mut inner).pop()
800 };
801
802 let mut executed = false;
803 while let Some((mut span, remote_diff)) = top {
804 let mut next_push_selection = None;
805 {
806 let inner = self.inner.clone();
807 let remote_change_clone = remote_diff.lock().unwrap().clone();
809 let commit = doc.undo_internal(
810 IdSpan {
811 peer: self.peer(),
812 counter: span.span,
813 },
814 &mut self.container_remap.lock().unwrap(),
815 Some(&remote_change_clone),
816 &mut |diff| {
817 info_span!("transform remote diff").in_scope(|| {
818 let mut inner = inner.lock().unwrap();
819 get_stack(&mut inner).transform_based_on_this_delta(diff);
821 });
822 },
823 )?;
824 drop(commit);
825 let mut inner = self.inner.lock().unwrap();
826 if let Some(x) = inner.on_pop.as_ref() {
827 for cursor in span.meta.cursors.iter_mut() {
828 transform_cursor(
832 cursor,
833 &remote_diff.lock().unwrap(),
834 doc,
835 &self.container_remap.lock().unwrap(),
836 );
837 }
838
839 x(kind, span.span, span.meta.clone());
840 let take = inner.last_popped_selection.take();
841 next_push_selection = take;
842 inner.last_popped_selection = Some(span.meta.cursors);
843 }
844 }
845 let new_counter = get_counter_end(doc, self.peer());
846 if end_counter != new_counter {
847 let mut inner = self.inner.lock().unwrap();
848 let mut meta = inner
849 .on_push
850 .as_ref()
851 .map(|x| {
852 x(
853 kind.opposite(),
854 CounterSpan::new(end_counter, new_counter),
855 None,
856 )
857 })
858 .unwrap_or_default();
859
860 if matches!(kind, UndoOrRedo::Undo) && get_opposite(&mut inner).is_empty() {
861 } else if let Some(inner) = next_push_selection.take() {
863 meta.cursors = inner;
865 }
866
867 get_opposite(&mut inner).push(CounterSpan::new(end_counter, new_counter), meta);
868 inner.next_counter = Some(new_counter);
869 executed = true;
870 break;
871 } else {
872 top = get_stack(&mut self.inner.lock().unwrap()).pop();
874 continue;
875 }
876 }
877
878 self.inner.lock().unwrap().processing_undo = false;
879 Ok(executed)
880 }
881
882 pub fn can_undo(&self) -> bool {
883 !self.inner.lock().unwrap().undo_stack.is_empty()
884 }
885
886 pub fn can_redo(&self) -> bool {
887 !self.inner.lock().unwrap().redo_stack.is_empty()
888 }
889
890 pub fn undo_count(&self) -> usize {
891 self.inner.lock().unwrap().undo_stack.len()
892 }
893
894 pub fn redo_count(&self) -> usize {
895 self.inner.lock().unwrap().redo_stack.len()
896 }
897
898 pub fn top_undo_meta(&self) -> Option<UndoItemMeta> {
900 self.inner.lock().unwrap().undo_stack.peek_top_meta()
901 }
902
903 pub fn top_redo_meta(&self) -> Option<UndoItemMeta> {
905 self.inner.lock().unwrap().redo_stack.peek_top_meta()
906 }
907
908 pub fn top_undo_value(&self) -> Option<LoroValue> {
910 self.top_undo_meta().map(|m| m.value)
911 }
912
913 pub fn top_redo_value(&self) -> Option<LoroValue> {
915 self.top_redo_meta().map(|m| m.value)
916 }
917
918 pub fn set_on_push(&self, on_push: Option<OnPush>) {
919 self.inner.lock().unwrap().on_push = on_push;
920 }
921
922 pub fn set_on_pop(&self, on_pop: Option<OnPop>) {
923 self.inner.lock().unwrap().on_pop = on_pop;
924 }
925
926 pub fn clear(&self) {
927 self.inner.lock().unwrap().undo_stack.clear();
928 self.inner.lock().unwrap().redo_stack.clear();
929 }
930}
931
932pub(crate) fn undo(
946 spans: Vec<(IdSpan, Frontiers)>,
947 last_frontiers_or_last_bi: Either<&Frontiers, &DiffBatch>,
948 calc_diff: impl Fn(&Frontiers, &Frontiers) -> DiffBatch,
949 on_last_event_a: &mut dyn FnMut(&DiffBatch),
950) -> DiffBatch {
951 let mut last_ci: Option<DiffBatch> = None;
969 for i in 0..spans.len() {
970 debug_span!("Undo", ?i, "Undo span {:?}", &spans[i]).in_scope(|| {
971 let (this_id_span, this_deps) = &spans[i];
972 let mut event_a_i = debug_span!("1. Calc event A_i").in_scope(|| {
976 calc_diff(&this_id_span.id_last().into(), this_deps)
978 });
979
980 let stack_diff_batch;
986 let event_b_i = 'block: {
987 let next = if i + 1 < spans.len() {
988 spans[i + 1].0.id_last().into()
989 } else {
990 match last_frontiers_or_last_bi {
991 Either::Left(last_frontiers) => last_frontiers.clone(),
992 Either::Right(right) => break 'block right,
993 }
994 };
995 stack_diff_batch = Some(calc_diff(&this_id_span.id_last().into(), &next));
996 stack_diff_batch.as_ref().unwrap()
997 };
998
999 let mut event_a_prime = if let Some(mut last_ci) = last_ci.take() {
1003 last_ci.transform(&event_a_i, true);
1007
1008 event_a_i.compose(&last_ci);
1009 event_a_i
1010 } else {
1011 event_a_i
1012 };
1013 if i == spans.len() - 1 {
1014 on_last_event_a(&event_a_prime);
1015 }
1016 event_a_prime.transform(event_b_i, true);
1020
1021 let c_i = event_a_prime;
1024 last_ci = Some(c_i);
1025 });
1026 }
1027
1028 last_ci.unwrap()
1029}