yrs/
undo.rs

1use std::collections::HashSet;
2use std::fmt::Formatter;
3use std::ops::{Deref, DerefMut};
4use std::sync::atomic::{AtomicPtr, Ordering};
5use std::sync::Arc;
6
7use crate::block::ItemPtr;
8use crate::branch::{Branch, BranchPtr};
9use crate::iter::TxnIterator;
10use crate::slice::BlockSlice;
11use crate::sync::Clock;
12use crate::transaction::Origin;
13use crate::{DeleteSet, Doc, Observer, ReadTxn, Transact, TransactionAcqError, TransactionMut, ID};
14
15/// Undo manager is a structure used to perform undo/redo operations over the associated shared
16/// type(s).
17///
18/// Undo-/redo-able actions (a.k.a. [StackItem]s) are not equivalent to [TransactionMut]
19/// unit of work, but rather a series of updates batched within specified time intervals
20/// (see: [Options::capture_timeout_millis]) and their corresponding origins
21/// (see: [Doc::transact_mut_with] and [UndoManager::include_origin]).
22///
23/// Individual stack item boundaries can be also specified explicitly by calling [UndoManager::reset],
24/// which denotes the end of the batch.
25///
26/// In order to revert an operation, call [UndoManager::undo], then [UndoManager::redo] to bring it
27/// back.
28///
29/// Users can also subscribe to change notifications observed by undo manager:
30/// - [UndoManager::observe_item_added], which is fired every time a new [StackItem] is created.
31/// - [UndoManager::observe_item_updated], which is fired every time when an existing [StackItem]
32///    had been extended due to new document changes arriving before capture timeout for that stack
33///    item finished.
34/// - [UndoManager::observe_item_popped], which is fired whenever [StackItem] is being from undo
35///    manager as a result of calling either [UndoManager::undo] or [UndoManager::redo] method.
36pub struct UndoManager<M> {
37    state: Arc<Inner<M>>,
38    doc: Doc,
39}
40
41#[cfg(feature = "sync")]
42type UndoFn<M> = Box<dyn Fn(&TransactionMut, &mut Event<M>) + Send + Sync + 'static>;
43
44#[cfg(not(feature = "sync"))]
45type UndoFn<M> = Box<dyn Fn(&TransactionMut, &mut Event<M>) + 'static>;
46
47#[cfg(feature = "sync")]
48pub trait Meta: Default + Send + Sync {}
49#[cfg(feature = "sync")]
50impl<M> Meta for M where M: Default + Send + Sync {}
51
52#[cfg(not(feature = "sync"))]
53pub trait Meta: Default {}
54#[cfg(not(feature = "sync"))]
55impl<M> Meta for M where M: Default {}
56
57struct Inner<M> {
58    scope: HashSet<BranchPtr>,
59    options: Options,
60    undo_stack: UndoStack<M>,
61    redo_stack: UndoStack<M>,
62    undoing: bool,
63    redoing: bool,
64    last_change: u64,
65    observer_added: Observer<UndoFn<M>>,
66    observer_updated: Observer<UndoFn<M>>,
67    observer_popped: Observer<UndoFn<M>>,
68}
69
70impl<M> UndoManager<M>
71where
72    M: Meta + 'static,
73{
74    /// Creates a new instance of the [UndoManager] working in a `scope` of a particular shared
75    /// type and document. While it's possible for undo manager to observe multiple shared types
76    /// (see: [UndoManager::expand_scope]), it can only work with a single document at the same time.
77    #[cfg(not(target_family = "wasm"))]
78    pub fn new<T>(doc: &Doc, scope: &T) -> Self
79    where
80        T: AsRef<Branch>,
81    {
82        Self::with_scope_and_options(doc, scope, Options::default())
83    }
84
85    #[inline]
86    pub fn doc(&self) -> &Doc {
87        &self.doc
88    }
89
90    /// Creates a new instance of the [UndoManager] working in a context of a given document, but
91    /// without any pre-initialize scope. While it's possible for undo manager to observe multiple
92    /// shared types (see: [UndoManager::expand_scope]), it can only work with a single document
93    /// at the same time.
94    pub fn with_options(doc: &Doc, options: Options) -> Self {
95        let mut inner = Arc::new(Inner {
96            scope: HashSet::new(),
97            options,
98            undo_stack: UndoStack::default(),
99            redo_stack: UndoStack::default(),
100            undoing: false,
101            redoing: false,
102            last_change: 0,
103            observer_added: Observer::new(),
104            observer_updated: Observer::new(),
105            observer_popped: Observer::new(),
106        });
107        let origin = Origin::from(Arc::as_ptr(&inner) as usize);
108        let inner_mut = Arc::get_mut(&mut inner).unwrap();
109        inner_mut.options.tracked_origins.insert(origin.clone());
110        let ptr = AtomicPtr::new(inner_mut as *mut Inner<M>);
111
112        doc.observe_destroy_with(origin.clone(), move |txn, _| {
113            let ptr = ptr.load(Ordering::Acquire);
114            let inner = unsafe { ptr.as_mut().unwrap() };
115            Self::handle_destroy(txn, inner)
116        })
117        .unwrap();
118        let ptr = AtomicPtr::new(inner_mut as *mut Inner<M>);
119
120        doc.observe_after_transaction_with(origin, move |txn| {
121            let ptr = ptr.load(Ordering::Acquire);
122            let inner = unsafe { ptr.as_mut().unwrap() };
123            Self::handle_after_transaction(inner, txn);
124        })
125        .unwrap();
126
127        UndoManager {
128            state: inner,
129            doc: doc.clone(),
130        }
131    }
132
133    /// Creates a new instance of the [UndoManager] working in a `scope` of a particular shared
134    /// type and document. While it's possible for undo manager to observe multiple shared types
135    /// (see: [UndoManager::expand_scope]), it can only work with a single document at the same time.
136    pub fn with_scope_and_options<T>(doc: &Doc, scope: &T, options: Options) -> Self
137    where
138        T: AsRef<Branch>,
139    {
140        let mut mgr = Self::with_options(doc, options);
141        mgr.expand_scope(scope);
142        mgr
143    }
144
145    fn should_skip(inner: &Inner<M>, txn: &TransactionMut) -> bool {
146        if let Some(capture_transaction) = &inner.options.capture_transaction {
147            if !capture_transaction(txn) {
148                return true;
149            }
150        }
151        !inner
152            .scope
153            .iter()
154            .any(|parent| txn.changed_parent_types.contains(parent))
155            || !txn
156                .origin()
157                .map(|o| inner.options.tracked_origins.contains(o))
158                .unwrap_or(inner.options.tracked_origins.len() == 1) // tracked origins contain only undo manager itself
159    }
160
161    fn handle_after_transaction(inner: &mut Inner<M>, txn: &mut TransactionMut) {
162        if Self::should_skip(inner, txn) {
163            return;
164        }
165        let undoing = inner.undoing;
166        let redoing = inner.redoing;
167        if undoing {
168            inner.last_change = 0; // next undo should not be appended to last stack item
169        } else if !redoing {
170            // neither undoing nor redoing: delete redoStack
171            let len = inner.redo_stack.len();
172            for item in inner.redo_stack.drain(0..len) {
173                Self::clear_item(&inner.scope, txn, item);
174            }
175        }
176
177        let mut insertions = DeleteSet::new();
178        for (client, &end_clock) in txn.after_state().iter() {
179            let start_clock = txn.before_state.get(client);
180            let diff = end_clock - start_clock;
181            if diff != 0 {
182                insertions.insert(ID::new(*client, start_clock), diff);
183            }
184        }
185        let now = inner.options.timestamp.now();
186        let stack = if undoing {
187            &mut inner.redo_stack
188        } else {
189            &mut inner.undo_stack
190        };
191        let extend = !undoing
192            && !redoing
193            && !stack.is_empty()
194            && inner.last_change > 0
195            && now - inner.last_change < inner.options.capture_timeout_millis;
196
197        if extend {
198            // append change to last stack op
199            if let Some(last_op) = stack.last_mut() {
200                // always true - we checked if stack is empty above
201                last_op.deletions.merge(txn.delete_set.clone());
202                last_op.insertions.merge(insertions);
203            }
204        } else {
205            // create a new stack op
206            let item = StackItem::new(txn.delete_set.clone(), insertions);
207            stack.push(item);
208        }
209
210        if !undoing && !redoing {
211            inner.last_change = now;
212        }
213        // make sure that deleted structs are not gc'd
214        let ds = txn.delete_set.clone();
215        let mut deleted = ds.deleted_blocks();
216        while let Some(slice) = deleted.next(txn) {
217            if let Some(item) = slice.as_item() {
218                if inner.scope.iter().any(|b| b.is_parent_of(Some(item))) {
219                    item.keep(true);
220                }
221            }
222        }
223
224        let last_op = stack.last_mut().unwrap();
225        let meta = std::mem::take(&mut last_op.meta);
226        let mut event = if undoing {
227            Event::undo(meta, txn.origin.clone(), txn.changed_parent_types.clone())
228        } else {
229            Event::redo(meta, txn.origin.clone(), txn.changed_parent_types.clone())
230        };
231        if !extend {
232            if inner.observer_added.has_subscribers() {
233                inner.observer_added.trigger(|fun| fun(txn, &mut event));
234            }
235        } else {
236            if inner.observer_updated.has_subscribers() {
237                inner.observer_updated.trigger(|fun| fun(txn, &mut event));
238            }
239        }
240        last_op.meta = event.meta;
241    }
242
243    fn handle_destroy(txn: &TransactionMut, inner: &mut Inner<M>) {
244        let origin = Origin::from(inner as *mut Inner<M> as usize);
245        if inner.options.tracked_origins.remove(&origin) {
246            if let Some(events) = txn.events() {
247                events.destroy_events.unsubscribe(&origin);
248                events.after_transaction_events.unsubscribe(&origin);
249            }
250        }
251    }
252
253    /// Registers a callback function to be called every time a new [StackItem] is created. This
254    /// usually happens when a new update over an tracked shared type happened after capture timeout
255    /// threshold from the previous stack item occurence has been reached or [UndoManager::reset]
256    /// has been called.
257    ///
258    /// Returns a subscription object which - when dropped - will unregister provided callback.
259    #[cfg(feature = "sync")]
260    pub fn observe_item_added<F>(&self, f: F) -> crate::Subscription
261    where
262        F: Fn(&TransactionMut, &mut Event<M>) + Send + Sync + 'static,
263    {
264        self.state.observer_added.subscribe(Box::new(f))
265    }
266
267    /// Registers a callback function to be called every time a new [StackItem] is created. This
268    /// usually happens when a new update over an tracked shared type happened after capture timeout
269    /// threshold from the previous stack item occurence has been reached or [UndoManager::reset]
270    /// has been called.
271    ///
272    /// Returns a subscription object which - when dropped - will unregister provided callback.
273    #[cfg(not(feature = "sync"))]
274    pub fn observe_item_added<F>(&self, f: F) -> crate::Subscription
275    where
276        F: Fn(&TransactionMut, &mut Event<M>) + 'static,
277    {
278        self.state.observer_added.subscribe(Box::new(f))
279    }
280
281    /// Registers a callback function to be called every time a new [StackItem] is created. This
282    /// usually happens when a new update over an tracked shared type happened after capture timeout
283    /// threshold from the previous stack item occurence has been reached or [UndoManager::reset]
284    /// has been called.
285    ///
286    /// Provided `key` is used to identify the origin of the callback. It can be used to unregister
287    /// the callback later on.
288    #[cfg(feature = "sync")]
289    pub fn observe_item_added_with<K, F>(&self, key: K, f: F)
290    where
291        K: Into<Origin>,
292        F: Fn(&TransactionMut, &mut Event<M>) + Send + Sync + 'static,
293    {
294        self.state
295            .observer_added
296            .subscribe_with(key.into(), Box::new(f))
297    }
298
299    /// Registers a callback function to be called every time a new [StackItem] is created. This
300    /// usually happens when a new update over an tracked shared type happened after capture timeout
301    /// threshold from the previous stack item occurence has been reached or [UndoManager::reset]
302    /// has been called.
303    ///
304    /// Provided `key` is used to identify the origin of the callback. It can be used to unregister
305    /// the callback later on.
306    #[cfg(not(feature = "sync"))]
307    pub fn observe_item_added_with<K, F>(&self, key: K, f: F)
308    where
309        K: Into<Origin>,
310        F: Fn(&TransactionMut, &mut Event<M>) + 'static,
311    {
312        self.state
313            .observer_added
314            .subscribe_with(key.into(), Box::new(f))
315    }
316
317    pub fn unobserve_item_added<K>(&self, key: K) -> bool
318    where
319        K: Into<Origin>,
320    {
321        self.state.observer_added.unsubscribe(&key.into())
322    }
323
324    /// Registers a callback function to be called every time an existing [StackItem] has been
325    /// extended as a result of updates from tracked types which happened before a capture timeout
326    /// has passed.
327    ///
328    /// Returns a subscription object which - when dropped - will unregister provided callback.
329    #[cfg(feature = "sync")]
330    pub fn observe_item_updated<F>(&self, f: F) -> crate::Subscription
331    where
332        F: Fn(&TransactionMut, &mut Event<M>) + Send + Sync + 'static,
333    {
334        self.state.observer_updated.subscribe(Box::new(f))
335    }
336
337    /// Registers a callback function to be called every time an existing [StackItem] has been
338    /// extended as a result of updates from tracked types which happened before a capture timeout
339    /// has passed.
340    ///
341    /// Returns a subscription object which - when dropped - will unregister provided callback.
342    #[cfg(not(feature = "sync"))]
343    pub fn observe_item_updated<F>(&self, f: F) -> crate::Subscription
344    where
345        F: Fn(&TransactionMut, &mut Event<M>) + 'static,
346    {
347        self.state.observer_updated.subscribe(Box::new(f))
348    }
349
350    /// Registers a callback function to be called every time an existing [StackItem] has been
351    /// extended as a result of updates from tracked types which happened before a capture timeout
352    /// has passed.
353    ///
354    /// Provided `key` is used to identify the origin of the callback. It can be used to unregister
355    /// the callback later on.
356    #[cfg(feature = "sync")]
357    pub fn observe_item_updated_with<K, F>(&self, key: K, f: F)
358    where
359        K: Into<Origin>,
360        F: Fn(&TransactionMut, &mut Event<M>) + Send + Sync + 'static,
361    {
362        self.state
363            .observer_updated
364            .subscribe_with(key.into(), Box::new(f))
365    }
366
367    /// Registers a callback function to be called every time an existing [StackItem] has been
368    /// extended as a result of updates from tracked types which happened before a capture timeout
369    /// has passed.
370    ///
371    /// Provided `key` is used to identify the origin of the callback. It can be used to unregister
372    /// the callback later on.
373    #[cfg(not(feature = "sync"))]
374    pub fn observe_item_updated_with<K, F>(&self, key: K, f: F)
375    where
376        K: Into<Origin>,
377        F: Fn(&TransactionMut, &mut Event<M>) + 'static,
378    {
379        self.state
380            .observer_updated
381            .subscribe_with(key.into(), Box::new(f))
382    }
383
384    pub fn unobserve_item_updated<K>(&self, key: K) -> bool
385    where
386        K: Into<Origin>,
387    {
388        self.state.observer_updated.unsubscribe(&key.into())
389    }
390
391    /// Registers a callback function to be called every time an existing [StackItem] has been
392    /// removed as a result of [UndoManager::undo] or [UndoManager::redo] method.
393    ///
394    /// Returns a subscription object which - when dropped - will unregister provided callback.
395    #[cfg(feature = "sync")]
396    pub fn observe_item_popped<F>(&self, f: F) -> crate::Subscription
397    where
398        F: Fn(&TransactionMut, &mut Event<M>) + Send + Sync + 'static,
399    {
400        self.state.observer_popped.subscribe(Box::new(f))
401    }
402
403    /// Registers a callback function to be called every time an existing [StackItem] has been
404    /// removed as a result of [UndoManager::undo] or [UndoManager::redo] method.
405    ///
406    /// Returns a subscription object which - when dropped - will unregister provided callback.
407    #[cfg(not(feature = "sync"))]
408    pub fn observe_item_popped<F>(&self, f: F) -> crate::Subscription
409    where
410        F: Fn(&TransactionMut, &mut Event<M>) + 'static,
411    {
412        self.state.observer_popped.subscribe(Box::new(f))
413    }
414
415    /// Registers a callback function to be called every time an existing [StackItem] has been
416    /// removed as a result of [UndoManager::undo] or [UndoManager::redo] method.
417    ///
418    /// Provided `key` is used to identify the origin of the callback. It can be used to unregister
419    /// the callback later on.
420    #[cfg(feature = "sync")]
421    pub fn observe_item_popped_with<K, F>(&self, key: K, f: F)
422    where
423        K: Into<Origin>,
424        F: Fn(&TransactionMut, &mut Event<M>) + Send + Sync + 'static,
425    {
426        self.state
427            .observer_popped
428            .subscribe_with(key.into(), Box::new(f))
429    }
430
431    /// Registers a callback function to be called every time an existing [StackItem] has been
432    /// removed as a result of [UndoManager::undo] or [UndoManager::redo] method.
433    ///
434    /// Provided `key` is used to identify the origin of the callback. It can be used to unregister
435    /// the callback later on.
436    #[cfg(not(feature = "sync"))]
437    pub fn observe_item_popped_with<K, F>(&self, key: K, f: F)
438    where
439        K: Into<Origin>,
440        F: Fn(&TransactionMut, &mut Event<M>) + 'static,
441    {
442        self.state
443            .observer_popped
444            .subscribe_with(key.into(), Box::new(f))
445    }
446
447    pub fn unobserve_item_popped<K>(&self, key: K) -> bool
448    where
449        K: Into<Origin>,
450    {
451        self.state.observer_popped.unsubscribe(&key.into())
452    }
453
454    /// Extends a list of shared types tracked by current undo manager by a given `scope`.
455    pub fn expand_scope<T>(&mut self, scope: &T)
456    where
457        T: AsRef<Branch>,
458    {
459        let ptr = BranchPtr::from(scope.as_ref());
460        let inner = Arc::get_mut(&mut self.state).unwrap();
461        inner.scope.insert(ptr);
462    }
463
464    /// Extends a list of origins tracked by current undo manager by given `origin`. Origin markers
465    /// can be assigned to updates executing in a scope of a particular transaction
466    /// (see: [Doc::transact_mut_with]).
467    pub fn include_origin<O>(&mut self, origin: O)
468    where
469        O: Into<Origin>,
470    {
471        let inner = Arc::get_mut(&mut self.state).unwrap();
472        inner.options.tracked_origins.insert(origin.into());
473    }
474
475    /// Removes an `origin` from the list of origins tracked by a current undo manager.
476    pub fn exclude_origin<O>(&mut self, origin: O)
477    where
478        O: Into<Origin>,
479    {
480        let inner = Arc::get_mut(&mut self.state).unwrap();
481        inner.options.tracked_origins.remove(&origin.into());
482    }
483
484    /// Clears all [StackItem]s stored within current UndoManager, effectively resetting its state.
485    ///
486    /// # Deadlocks
487    ///
488    /// In order to perform its function, this method must guarantee that underlying document store
489    /// is not being modified by another running `TransactionMut`. It does so by acquiring
490    /// a read-only transaction itself. If transaction couldn't be acquired (because another
491    /// read-write transaction is in progress), it will hold current thread until, acquisition is
492    /// available.
493    pub fn clear(&mut self) {
494        let txn = self.doc.transact();
495        let inner = Arc::get_mut(&mut self.state).unwrap();
496
497        let len = inner.undo_stack.len();
498        for item in inner.undo_stack.drain(0..len) {
499            Self::clear_item(&inner.scope, &txn, item);
500        }
501
502        let len = inner.redo_stack.len();
503        for item in inner.redo_stack.drain(0..len) {
504            Self::clear_item(&inner.scope, &txn, item);
505        }
506    }
507
508    fn clear_item<T: ReadTxn>(scope: &HashSet<BranchPtr>, txn: &T, stack_item: StackItem<M>) {
509        let mut deleted = stack_item.deletions.deleted_blocks();
510        while let Some(slice) = deleted.next(txn) {
511            if let Some(item) = slice.as_item() {
512                if scope.iter().any(|b| b.is_parent_of(Some(item))) {
513                    item.keep(false);
514                }
515            }
516        }
517    }
518
519    pub fn as_origin(&self) -> Origin {
520        let mgr_ptr: *const Inner<M> = &*self.state;
521        Origin::from(mgr_ptr as usize)
522    }
523
524    /// [UndoManager] merges undo stack items if they were created withing the time gap smaller than
525    /// [Options::capture_timeout_millis]. You can call this method so that the next stack item won't be
526    /// merged.
527    ///
528    /// Example:
529    /// ```rust
530    /// use yrs::{Doc, GetString, Text, Transact, UndoManager};
531    /// let doc = Doc::new();
532    ///
533    /// // without UndoManager::stop
534    /// let txt = doc.get_or_insert_text("no-stop");
535    /// let mut mgr = UndoManager::new(&doc, &txt);
536    /// txt.insert(&mut doc.transact_mut(), 0, "a");
537    /// txt.insert(&mut doc.transact_mut(), 1, "b");
538    /// mgr.undo_blocking();
539    /// txt.get_string(&doc.transact()); // => "" (note that 'ab' was removed)
540    ///
541    /// // with UndoManager::stop
542    /// let txt = doc.get_or_insert_text("with-stop");
543    /// let mut mgr = UndoManager::new(&doc, &txt);
544    /// txt.insert(&mut doc.transact_mut(), 0, "a");
545    /// mgr.reset();
546    /// txt.insert(&mut doc.transact_mut(), 1, "b");
547    /// mgr.undo_blocking();
548    /// txt.get_string(&doc.transact()); // => "a" (note that only 'b' was removed)
549    /// ```
550    pub fn reset(&mut self) {
551        let inner = Arc::get_mut(&mut self.state).unwrap();
552        inner.last_change = 0;
553    }
554
555    /// Are there any undo steps available?
556    pub fn can_undo(&self) -> bool {
557        !self.state.undo_stack.is_empty()
558    }
559
560    /// Returns a list of [StackItem]s stored within current undo manager responsible for performing
561    /// potential undo operations.
562    pub fn undo_stack(&self) -> &[StackItem<M>] {
563        &self.state.undo_stack.0
564    }
565
566    /// Returns a list of [StackItem]s stored within current undo manager responsible for performing
567    /// potential redo operations.
568    pub fn redo_stack(&self) -> &[StackItem<M>] {
569        &self.state.redo_stack.0
570    }
571
572    /// Undo last action tracked by current undo manager. Actions (a.k.a. [StackItem]s) are groups
573    /// of updates performed in a given time range - they also can be separated explicitly by
574    /// calling [UndoManager::reset].
575    ///
576    /// Successful execution returns a boolean value telling if an undo call has performed any changes.
577    ///
578    /// # Deadlocks
579    ///
580    /// This method requires exclusive access to underlying document store. This means that
581    /// no other transaction on that same document can be active while calling this method.
582    /// Otherwise, it may cause a deadlock.
583    ///
584    /// See also: [UndoManager::try_undo] and [UndoManager::undo_blocking].
585    pub async fn undo(&mut self) -> bool {
586        let origin = self.as_origin();
587        let inner = Arc::get_mut(&mut self.state).unwrap();
588        let txn = crate::AsyncTransact::transact_mut_with(&self.doc, origin.clone()).await;
589        Self::undo_inner(inner, txn, origin)
590    }
591
592    /// Undo last action tracked by current undo manager. Actions (a.k.a. [StackItem]s) are groups
593    /// of updates performed in a given time range - they also can be separated explicitly by
594    /// calling [UndoManager::reset].
595    ///
596    /// Successful execution returns a boolean value telling if an undo call has performed any changes.
597    ///
598    /// # Deadlocks
599    ///
600    /// This method requires exclusive access to underlying document store. This means that
601    /// no other transaction on that same document can be active while calling this method.
602    /// Otherwise, it may cause a deadlock.
603    ///
604    /// See also: [UndoManager::try_undo] and [UndoManager::undo].
605    pub fn undo_blocking(&mut self) -> bool {
606        let origin = self.as_origin();
607        let inner = Arc::get_mut(&mut self.state).unwrap();
608        let txn = self.doc.transact_mut_with(origin.clone());
609        Self::undo_inner(inner, txn, origin)
610    }
611
612    /// Undo last action tracked by current undo manager. Actions (a.k.a. [StackItem]s) are groups
613    /// of updates performed in a given time range - they also can be separated explicitly by
614    /// calling [UndoManager::reset].
615    ///
616    /// Successful execution returns a boolean value telling if an undo call has performed any changes.
617    ///
618    /// See also: [UndoManager::undo] and [UndoManager::undo_blocking].
619    ///
620    /// # Errors
621    ///
622    /// This method requires exclusive access to underlying document store. This means that
623    /// no other transaction on that same document can be active while calling this method.
624    /// Otherwise, an error will be returned.
625    pub fn try_undo(&mut self) -> Result<bool, TransactionAcqError> {
626        let origin = self.as_origin();
627        let inner = Arc::get_mut(&mut self.state).unwrap();
628        let txn = self.doc.try_transact_mut_with(origin.clone())?;
629        let changed = Self::undo_inner(inner, txn, origin);
630        Ok(changed)
631    }
632
633    fn undo_inner(inner: &mut Inner<M>, mut txn: TransactionMut, origin: Origin) -> bool {
634        inner.undoing = true;
635        let result = Self::pop(
636            &mut inner.undo_stack,
637            &inner.redo_stack,
638            &mut txn,
639            &inner.scope,
640        );
641        txn.commit();
642        let changed = if let Some(item) = result {
643            let mut e = Event::undo(item.meta, Some(origin), txn.changed_parent_types.clone());
644            if inner.observer_popped.has_subscribers() {
645                inner.observer_popped.trigger(|fun| fun(&txn, &mut e));
646            }
647            true
648        } else {
649            false
650        };
651        inner.undoing = false;
652        changed
653    }
654
655    /// Are there any redo steps available?
656    pub fn can_redo(&self) -> bool {
657        !self.state.redo_stack.is_empty()
658    }
659
660    /// Redo'es last action previously undo'ed by current undo manager. Actions
661    /// (a.k.a. [StackItem]s) are groups of updates performed in a given time range - they also can
662    /// be separated explicitly by calling [UndoManager::reset].
663    ///
664    /// Successful execution returns a boolean value telling if an undo call has performed any changes.
665    ///
666    /// # Deadlocks
667    ///
668    /// This method requires exclusive access to underlying document store. This means that
669    /// no other transaction on that same document can be active while calling this method.
670    /// Otherwise, it may cause a deadlock.
671    ///
672    /// See also: [UndoManager::try_redo] and [UndoManager::redo_blocking].
673    pub async fn redo(&mut self) -> bool {
674        let origin = self.as_origin();
675        let inner = Arc::get_mut(&mut self.state).unwrap();
676        let txn = crate::AsyncTransact::transact_mut_with(&self.doc, origin.clone()).await;
677        let changed = Self::redo_inner(inner, txn, origin);
678        changed
679    }
680
681    /// Redo'es last action previously undo'ed by current undo manager. Actions
682    /// (a.k.a. [StackItem]s) are groups of updates performed in a given time range - they also can
683    /// be separated explicitly by calling [UndoManager::reset].
684    ///
685    /// Successful execution returns a boolean value telling if an undo call has performed any changes.
686    ///
687    /// # Deadlocks
688    ///
689    /// This method requires exclusive access to underlying document store. This means that
690    /// no other transaction on that same document can be active while calling this method.
691    /// Otherwise, it may cause a deadlock.
692    ///
693    /// See also: [UndoManager::try_redo] and [UndoManager::redo_blocking].
694    pub fn redo_blocking(&mut self) -> bool {
695        let origin = self.as_origin();
696        let inner = Arc::get_mut(&mut self.state).unwrap();
697        let txn = self.doc.transact_mut_with(origin.clone());
698        let changed = Self::redo_inner(inner, txn, origin);
699        changed
700    }
701
702    /// Redo'es last action previously undo'ed by current undo manager. Actions
703    /// (a.k.a. [StackItem]s) are groups of updates performed in a given time range - they also can
704    /// be separated explicitly by calling [UndoManager::reset].
705    ///
706    /// Successful execution returns a boolean value telling if an undo call has performed any changes.
707    ///
708    /// # Errors
709    ///
710    /// This method requires exclusive access to underlying document store. This means that
711    /// no other transaction on that same document can be active while calling this method.
712    /// Otherwise, an error will be returned.
713    pub fn try_redo(&mut self) -> Result<bool, TransactionAcqError> {
714        let origin = self.as_origin();
715        let inner = Arc::get_mut(&mut self.state).unwrap();
716        let txn = self.doc.try_transact_mut_with(origin.clone())?;
717        let changed = Self::redo_inner(inner, txn, origin);
718        Ok(changed)
719    }
720
721    fn redo_inner(inner: &mut Inner<M>, mut txn: TransactionMut, origin: Origin) -> bool {
722        inner.redoing = true;
723        let result = Self::pop(
724            &mut inner.redo_stack,
725            &inner.undo_stack,
726            &mut txn,
727            &inner.scope,
728        );
729        txn.commit();
730        let changed = if let Some(item) = result {
731            let mut e = Event::redo(item.meta, Some(origin), txn.changed_parent_types.clone());
732            if inner.observer_popped.has_subscribers() {
733                inner.observer_popped.trigger(|fun| fun(&txn, &mut e));
734            }
735            true
736        } else {
737            false
738        };
739        inner.redoing = false;
740        changed
741    }
742
743    fn pop(
744        stack: &mut UndoStack<M>,
745        other: &UndoStack<M>,
746        txn: &mut TransactionMut,
747        scope: &HashSet<BranchPtr>,
748    ) -> Option<StackItem<M>> {
749        let mut result = None;
750        while let Some(item) = stack.pop() {
751            let mut to_redo = HashSet::<ItemPtr>::new();
752            let mut to_delete = Vec::<ItemPtr>::new();
753            let mut change_performed = false;
754
755            let deleted: Vec<_> = item.insertions.deleted_blocks().collect(txn);
756            for slice in deleted {
757                if let BlockSlice::Item(slice) = slice {
758                    let mut item = txn.store.materialize(slice);
759                    if item.redone.is_some() {
760                        let slice = txn.store_mut().follow_redone(item.id())?;
761                        item = txn.store.materialize(slice);
762                    }
763
764                    if !item.is_deleted() && scope.iter().any(|b| b.is_parent_of(Some(item))) {
765                        to_delete.push(item);
766                    }
767                }
768            }
769
770            let mut deleted = item.deletions.deleted_blocks();
771            while let Some(slice) = deleted.next(txn) {
772                if let BlockSlice::Item(slice) = slice {
773                    let ptr = txn.store.materialize(slice);
774                    if scope.iter().any(|b| b.is_parent_of(Some(ptr)))
775                        && !item.insertions.is_deleted(ptr.id())
776                    // Never redo structs in stackItem.insertions because they were created and deleted in the same capture interval.
777                    {
778                        to_redo.insert(ptr);
779                    }
780                }
781            }
782
783            for &ptr in to_redo.iter() {
784                let mut ptr = ptr;
785                change_performed |= ptr
786                    .redo(txn, &to_redo, &item.insertions, stack, other)
787                    .is_some();
788            }
789
790            // We want to delete in reverse order so that children are deleted before
791            // parents, so we have more information available when items are filtered.
792            for &item in to_delete.iter().rev() {
793                // if self.options.delete_filter(item) {
794                txn.delete(item);
795                change_performed = true;
796            }
797
798            if change_performed {
799                result = Some(item);
800                break;
801            }
802        }
803        result
804    }
805}
806
807impl<M: std::fmt::Debug> std::fmt::Debug for UndoManager<M> {
808    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
809        let mut s = f.debug_struct("UndoManager");
810        let state = &self.state;
811        s.field("scope", &state.scope);
812        s.field("tracked_origins", &state.options.tracked_origins);
813        if !state.undo_stack.is_empty() {
814            s.field("undo", &state.undo_stack);
815        }
816        if !state.redo_stack.is_empty() {
817            s.field("redo", &state.redo_stack);
818        }
819        s.finish()
820    }
821}
822
823impl<M> Drop for UndoManager<M> {
824    fn drop(&mut self) {
825        let origin = Origin::from(Arc::as_ptr(&self.state) as usize);
826        self.doc.unobserve_destroy(origin.clone()).unwrap();
827        self.doc.unobserve_after_transaction(origin).unwrap();
828    }
829}
830
831#[repr(transparent)]
832#[derive(Debug, Clone, Eq, PartialEq, Default)]
833pub(crate) struct UndoStack<M>(Vec<StackItem<M>>);
834
835impl<M> Deref for UndoStack<M> {
836    type Target = Vec<StackItem<M>>;
837
838    fn deref(&self) -> &Self::Target {
839        &self.0
840    }
841}
842
843impl<M> DerefMut for UndoStack<M> {
844    fn deref_mut(&mut self) -> &mut Self::Target {
845        &mut self.0
846    }
847}
848
849impl<M> UndoStack<M> {
850    pub fn is_deleted(&self, id: &ID) -> bool {
851        for item in self.0.iter() {
852            if item.deletions.is_deleted(id) {
853                return true;
854            }
855        }
856        false
857    }
858}
859
860/// Set of options used to configure [UndoManager].
861pub struct Options {
862    /// Undo-/redo-able updates are grouped together in time-constrained snapshots. This field
863    /// determines the period of time, every snapshot will be automatically made in.
864    pub capture_timeout_millis: u64,
865
866    /// List of origins tracked by corresponding [UndoManager].
867    /// If provided, it will track only updates made within transactions of specific origin.
868    /// If not provided, it will track only updates made within transaction with no origin defined.
869    pub tracked_origins: HashSet<Origin>,
870
871    /// Custom logic decider, that along with [tracked_origins] can be used to determine if
872    /// transaction changes should be captured or not.
873    pub capture_transaction: Option<CaptureTransactionFn>,
874
875    /// Custom clock function, that can be used to generate timestamps used by
876    /// [Options::capture_timeout_millis].
877    pub timestamp: Arc<dyn Clock>,
878}
879
880pub type CaptureTransactionFn = Arc<dyn Fn(&TransactionMut) -> bool + Send + Sync + 'static>;
881
882#[cfg(not(target_family = "wasm"))]
883impl Default for Options {
884    fn default() -> Self {
885        Options {
886            capture_timeout_millis: 500,
887            tracked_origins: HashSet::new(),
888            capture_transaction: None,
889            timestamp: Arc::new(crate::sync::time::SystemClock),
890        }
891    }
892}
893
894/// A unit of work for the [UndoManager]. It contains a compressed information about all updates and
895/// deletions tracked by a corresponding undo manager. Whenever an [UndoManger::undo] or
896/// [UndoManager::redo] methods are called a last [StackItem] is being used to modify a state of
897/// the document.
898///
899/// Stack items are stored internally by undo manager and created automatically whenever a new
900/// update from tracked shared type and transaction of tracked origin has been committed within
901/// a threshold specified by [Options::capture_timeout_millis] time window since the previous stack
902/// item creation. They can also be created explicitly by calling [UndoManager::reset], which marks
903/// the end of the last stack item batch.
904#[derive(Debug, Clone, Eq, PartialEq, Hash)]
905pub struct StackItem<T> {
906    deletions: DeleteSet,
907    insertions: DeleteSet,
908
909    /// A custom user metadata that can be attached to a particular [StackItem]. It can be used
910    /// to carry over the additional information (such as ie. user cursor position) between
911    /// undo/redo operations.
912    pub meta: T,
913}
914
915impl<M: Default> StackItem<M> {
916    fn new(deletions: DeleteSet, insertions: DeleteSet) -> Self {
917        StackItem {
918            deletions,
919            insertions,
920            meta: M::default(),
921        }
922    }
923
924    /// A set of identifiers of element deleted at part of the timeframe current [StackItem] is
925    /// responsible for.
926    pub fn deletions(&self) -> &DeleteSet {
927        &self.deletions
928    }
929
930    /// A set of identifiers of element inserted at part of the timeframe current [StackItem] is
931    /// responsible for.
932    pub fn insertions(&self) -> &DeleteSet {
933        &self.insertions
934    }
935}
936
937impl<M> std::fmt::Display for StackItem<M> {
938    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
939        write!(f, "StackItem(")?;
940        if !self.deletions.is_empty() {
941            write!(f, "-{}", self.deletions)?;
942        }
943        if !self.insertions.is_empty() {
944            write!(f, "+{}", self.insertions)?;
945        }
946        write!(f, ")")
947    }
948}
949
950#[derive(Debug)]
951pub struct Event<M> {
952    meta: M,
953    origin: Option<Origin>,
954    kind: EventKind,
955    changed_parent_types: Vec<BranchPtr>,
956}
957
958impl<M> Event<M> {
959    fn undo(meta: M, origin: Option<Origin>, changed_parent_types: Vec<BranchPtr>) -> Self {
960        Event {
961            meta,
962            origin,
963            changed_parent_types,
964            kind: EventKind::Undo,
965        }
966    }
967
968    fn redo(meta: M, origin: Option<Origin>, changed_parent_types: Vec<BranchPtr>) -> Self {
969        Event {
970            meta,
971            origin,
972            changed_parent_types,
973            kind: EventKind::Redo,
974        }
975    }
976
977    pub fn meta(&self) -> &M {
978        &self.meta
979    }
980
981    pub fn meta_mut(&mut self) -> &mut M {
982        &mut self.meta
983    }
984
985    /// Checks if given shared collection has changed in the scope of currently notified update.
986    pub fn has_changed<T: AsRef<Branch>>(&self, target: &T) -> bool {
987        let ptr = BranchPtr::from(target.as_ref());
988        self.changed_parent_types.contains(&ptr)
989    }
990
991    /// Returns a transaction origin related to this update notification.
992    pub fn origin(&self) -> Option<&Origin> {
993        self.origin.as_ref()
994    }
995
996    /// Returns an enum informing if current update is result of undo or redo operation.
997    pub fn kind(&self) -> EventKind {
998        self.kind
999    }
1000
1001    /// Returns info about all changed shared collections.
1002    pub fn changed_parent_types(&self) -> &[BranchPtr] {
1003        &self.changed_parent_types
1004    }
1005}
1006
1007/// Enum which informs if correlated [Event] was produced as a result of either undo or redo
1008/// operation over [UndoManager].
1009#[repr(u8)]
1010#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
1011pub enum EventKind {
1012    /// Referenced event was result of [UndoManager::undo] operation.
1013    Undo,
1014    /// Referenced event was result of [UndoManager::redo] operation.
1015    Redo,
1016}
1017
1018#[cfg(test)]
1019mod test {
1020    use std::collections::HashMap;
1021    use std::convert::TryInto;
1022    use std::sync::atomic::{AtomicUsize, Ordering};
1023    use std::sync::Arc;
1024
1025    use crate::test_utils::exchange_updates;
1026    use crate::types::text::{Diff, YChange};
1027    use crate::types::{Attrs, ToJson};
1028    use crate::undo::Options;
1029    use crate::updates::decoder::Decode;
1030    use crate::{
1031        any, Any, Array, ArrayPrelim, Doc, GetString, Map, MapPrelim, MapRef, ReadTxn, StateVector,
1032        Text, TextPrelim, TextRef, Transact, UndoManager, Update, Xml, XmlElementPrelim,
1033        XmlElementRef, XmlFragment, XmlTextPrelim,
1034    };
1035
1036    #[test]
1037    fn undo_text() {
1038        let d1 = Doc::with_client_id(1);
1039        let txt1 = d1.get_or_insert_text("test");
1040        let mut mgr = UndoManager::new(&d1, &txt1);
1041
1042        let d2 = Doc::with_client_id(2);
1043        let txt2 = d2.get_or_insert_text("test");
1044
1045        // items that are added & deleted in the same transaction won't be undo
1046        txt1.insert(&mut d1.transact_mut(), 0, "test");
1047        txt1.remove_range(&mut d1.transact_mut(), 0, 4);
1048        mgr.undo_blocking();
1049        assert_eq!(txt1.get_string(&d1.transact()), "");
1050
1051        // follow redone items
1052        txt1.insert(&mut d1.transact_mut(), 0, "a");
1053        mgr.reset();
1054        txt1.remove_range(&mut d1.transact_mut(), 0, 1);
1055        mgr.reset();
1056        mgr.undo_blocking();
1057        assert_eq!(txt1.get_string(&d1.transact()), "a");
1058        mgr.undo_blocking();
1059        assert_eq!(txt1.get_string(&d1.transact()), "");
1060
1061        txt1.insert(&mut d1.transact_mut(), 0, "abc");
1062        txt2.insert(&mut d2.transact_mut(), 0, "xyz");
1063
1064        exchange_updates(&[&d1, &d2]);
1065        mgr.undo_blocking();
1066        assert_eq!(txt1.get_string(&d1.transact()), "xyz");
1067        mgr.redo_blocking();
1068        assert_eq!(txt1.get_string(&d1.transact()), "abcxyz");
1069
1070        exchange_updates(&[&d1, &d2]);
1071
1072        txt2.remove_range(&mut d2.transact_mut(), 0, 1);
1073
1074        exchange_updates(&[&d1, &d2]);
1075
1076        mgr.undo_blocking();
1077        assert_eq!(txt1.get_string(&d1.transact()), "xyz");
1078        mgr.redo_blocking();
1079        assert_eq!(txt1.get_string(&d1.transact()), "bcxyz");
1080
1081        // test marks
1082        let attrs = Attrs::from([("bold".into(), true.into())]);
1083        txt1.format(&mut d1.transact_mut(), 1, 3, attrs.clone());
1084        let diff = txt1.diff(&d1.transact(), YChange::identity);
1085        assert_eq!(
1086            diff,
1087            vec![
1088                Diff::new("b".into(), None),
1089                Diff::new("cxy".into(), Some(Box::new(attrs.clone()))),
1090                Diff::new("z".into(), None),
1091            ]
1092        );
1093
1094        mgr.undo_blocking();
1095        let diff = txt1.diff(&d1.transact(), YChange::identity);
1096        assert_eq!(diff, vec![Diff::new("bcxyz".into(), None)]);
1097
1098        mgr.redo_blocking();
1099        let diff = txt1.diff(&d1.transact(), YChange::identity);
1100        assert_eq!(
1101            diff,
1102            vec![
1103                Diff::new("b".into(), None),
1104                Diff::new("cxy".into(), Some(Box::new(attrs.clone()))),
1105                Diff::new("z".into(), None),
1106            ]
1107        );
1108    }
1109
1110    #[test]
1111    fn double_undo() {
1112        let doc = Doc::with_client_id(1);
1113        let txt = doc.get_or_insert_text("test");
1114        txt.insert(&mut doc.transact_mut(), 0, "1221");
1115
1116        let mut mgr = UndoManager::new(&doc, &txt);
1117        txt.insert(&mut doc.transact_mut(), 2, "3");
1118        txt.insert(&mut doc.transact_mut(), 3, "3");
1119
1120        mgr.undo_blocking();
1121        mgr.undo_blocking();
1122
1123        txt.insert(&mut doc.transact_mut(), 2, "3");
1124        assert_eq!(txt.get_string(&doc.transact()), "12321");
1125    }
1126
1127    #[test]
1128    fn undo_map() {
1129        let d1 = Doc::with_client_id(1);
1130        let map1 = d1.get_or_insert_map("test");
1131
1132        let d2 = Doc::with_client_id(2);
1133        let map2 = d2.get_or_insert_map("test");
1134
1135        map1.insert(&mut d1.transact_mut(), "a", 0);
1136        let mut mgr = UndoManager::new(&d1, &map1);
1137        map1.insert(&mut d1.transact_mut(), "a", 1);
1138        mgr.undo_blocking();
1139        assert_eq!(map1.get(&d1.transact(), "a").unwrap(), 0.into());
1140        mgr.redo_blocking();
1141        assert_eq!(map1.get(&d1.transact(), "a").unwrap(), 1.into());
1142
1143        // testing sub-types and if it can restore a whole type
1144        let sub_type = map1.insert(&mut d1.transact_mut(), "a", MapPrelim::default());
1145        sub_type.insert(&mut d1.transact_mut(), "x", 42);
1146        let actual = map1.to_json(&d1.transact());
1147        let expected = Any::from_json(r#"{ "a": { "x": 42 } }"#).unwrap();
1148        assert_eq!(actual, expected);
1149
1150        mgr.undo_blocking();
1151        assert_eq!(map1.get(&d1.transact(), "a").unwrap(), 1.into());
1152        mgr.redo_blocking();
1153        let actual = map1.to_json(&d1.transact());
1154        let expected = Any::from_json(r#"{ "a": { "x": 42 } }"#).unwrap();
1155        assert_eq!(actual, expected);
1156
1157        exchange_updates(&[&d1, &d2]);
1158
1159        // if content is overwritten by another user, undo operations should be skipped
1160        map2.insert(&mut d2.transact_mut(), "a", 44);
1161
1162        exchange_updates(&[&d1, &d2]);
1163        exchange_updates(&[&d1, &d2]);
1164
1165        mgr.undo_blocking();
1166        assert_eq!(map1.get(&d1.transact(), "a").unwrap(), 44.into());
1167        mgr.redo_blocking();
1168        assert_eq!(map1.get(&d1.transact(), "a").unwrap(), 44.into());
1169
1170        // test setting value multiple times
1171        map1.insert(&mut d1.transact_mut(), "b", "initial");
1172        mgr.reset();
1173        map1.insert(&mut d1.transact_mut(), "b", "val1");
1174        map1.insert(&mut d1.transact_mut(), "b", "val2");
1175        mgr.reset();
1176        mgr.undo_blocking();
1177        assert_eq!(map1.get(&d1.transact(), "b").unwrap(), "initial".into());
1178    }
1179
1180    #[test]
1181    fn undo_array() {
1182        let d1 = Doc::with_client_id(1);
1183        let array1 = d1.get_or_insert_array("test");
1184
1185        let d2 = Doc::with_client_id(2);
1186        let array2 = d2.get_or_insert_array("test");
1187
1188        let mut mgr = UndoManager::new(&d1, &array1);
1189        array1.insert_range(&mut d1.transact_mut(), 0, [1, 2, 3]);
1190        array2.insert_range(&mut d2.transact_mut(), 0, [4, 5, 6]);
1191
1192        exchange_updates(&[&d1, &d2]);
1193
1194        assert_eq!(
1195            array1.to_json(&d1.transact()),
1196            vec![1, 2, 3, 4, 5, 6].into()
1197        );
1198        mgr.undo_blocking();
1199        assert_eq!(array1.to_json(&d1.transact()), vec![4, 5, 6].into());
1200        mgr.redo_blocking();
1201        assert_eq!(
1202            array1.to_json(&d1.transact()),
1203            vec![1, 2, 3, 4, 5, 6].into()
1204        );
1205
1206        exchange_updates(&[&d1, &d2]);
1207
1208        array2.remove_range(&mut d2.transact_mut(), 0, 1); // user2 deletes [1]
1209
1210        exchange_updates(&[&d1, &d2]);
1211
1212        mgr.undo_blocking();
1213        assert_eq!(array1.to_json(&d1.transact()), vec![4, 5, 6].into());
1214        mgr.redo_blocking();
1215        assert_eq!(array1.to_json(&d1.transact()), vec![2, 3, 4, 5, 6].into());
1216        array1.remove_range(&mut d1.transact_mut(), 0, 5);
1217
1218        // test nested structure
1219        let map = array1.insert(&mut d1.transact_mut(), 0, MapPrelim::default());
1220        let actual = array1.to_json(&d1.transact());
1221        let expected = Any::from_json(r#"[{}]"#).unwrap();
1222        assert_eq!(actual, expected);
1223        mgr.reset();
1224        map.insert(&mut d1.transact_mut(), "a", 1);
1225        let actual = array1.to_json(&d1.transact());
1226        let expected = Any::from_json(r#"[{"a":1}]"#).unwrap();
1227        assert_eq!(actual, expected);
1228
1229        mgr.undo_blocking();
1230        let actual = array1.to_json(&d1.transact());
1231        let expected = Any::from_json(r#"[{}]"#).unwrap();
1232        assert_eq!(actual, expected);
1233
1234        mgr.undo_blocking();
1235        assert_eq!(array1.to_json(&d1.transact()), vec![2, 3, 4, 5, 6].into());
1236
1237        mgr.redo_blocking();
1238        let actual = array1.to_json(&d1.transact());
1239        let expected = Any::from_json(r#"[{}]"#).unwrap();
1240        assert_eq!(actual, expected);
1241
1242        mgr.redo_blocking();
1243        let actual = array1.to_json(&d1.transact());
1244        let expected = Any::from_json(r#"[{"a":1}]"#).unwrap();
1245        assert_eq!(actual, expected);
1246
1247        exchange_updates(&[&d1, &d2]);
1248
1249        let map2 = array2
1250            .get(&d2.transact(), 0)
1251            .unwrap()
1252            .cast::<MapRef>()
1253            .unwrap();
1254        map2.insert(&mut d2.transact_mut(), "b", 2);
1255        exchange_updates(&[&d1, &d2]);
1256
1257        let actual = array1.to_json(&d1.transact());
1258        let expected = Any::from_json(r#"[{"a":1,"b":2}]"#).unwrap();
1259        assert_eq!(actual, expected);
1260
1261        mgr.undo_blocking();
1262        let actual = array1.to_json(&d1.transact());
1263        let expected = Any::from_json(r#"[{"b":2}]"#).unwrap();
1264        assert_eq!(actual, expected);
1265
1266        mgr.undo_blocking();
1267        assert_eq!(array1.to_json(&d1.transact()), vec![2, 3, 4, 5, 6].into());
1268
1269        mgr.redo_blocking();
1270        let actual = array1.to_json(&d1.transact());
1271        let expected = Any::from_json(r#"[{"b":2}]"#).unwrap();
1272        assert_eq!(actual, expected);
1273
1274        mgr.redo_blocking();
1275        let actual = array1.to_json(&d1.transact());
1276        let expected = Any::from_json(r#"[{"a":1,"b":2}]"#).unwrap();
1277        assert_eq!(actual, expected);
1278    }
1279
1280    #[test]
1281    fn undo_xml() {
1282        let d1 = Doc::with_client_id(1);
1283        let frag = d1.get_or_insert_xml_fragment("xml");
1284        let xml1 = frag.insert(
1285            &mut d1.transact_mut(),
1286            0,
1287            XmlElementPrelim::empty("undefined"),
1288        );
1289
1290        let mut mgr = UndoManager::new(&d1, &xml1);
1291        let child = xml1.insert(&mut d1.transact_mut(), 0, XmlElementPrelim::empty("p"));
1292        let text_child = child.insert(&mut d1.transact_mut(), 0, XmlTextPrelim::new("content"));
1293
1294        assert_eq!(
1295            xml1.get_string(&d1.transact()),
1296            "<undefined><p>content</p></undefined>"
1297        );
1298        // format textchild and revert that change
1299        mgr.reset();
1300        text_child.format(
1301            &mut d1.transact_mut(),
1302            3,
1303            4,
1304            Attrs::from([("bold".into(), any!({}))]),
1305        );
1306        assert_eq!(
1307            xml1.get_string(&d1.transact()),
1308            "<undefined><p>con<bold>tent</bold></p></undefined>"
1309        );
1310        mgr.undo_blocking();
1311        assert_eq!(
1312            xml1.get_string(&d1.transact()),
1313            "<undefined><p>content</p></undefined>"
1314        );
1315        mgr.redo_blocking();
1316        assert_eq!(
1317            xml1.get_string(&d1.transact()),
1318            "<undefined><p>con<bold>tent</bold></p></undefined>"
1319        );
1320        xml1.remove_range(&mut d1.transact_mut(), 0, 1);
1321        assert_eq!(xml1.get_string(&d1.transact()), "<undefined></undefined>");
1322        mgr.undo_blocking();
1323        assert_eq!(
1324            xml1.get_string(&d1.transact()),
1325            "<undefined><p>con<bold>tent</bold></p></undefined>"
1326        );
1327    }
1328
1329    #[test]
1330    fn undo_events() {
1331        use crate::undo::UndoManager;
1332        type Metadata = HashMap<String, usize>;
1333
1334        let doc = Doc::with_client_id(1);
1335        let txt = doc.get_or_insert_text("test");
1336        let mut mgr: UndoManager<Metadata> = UndoManager::new(&doc, &txt);
1337
1338        let result = Arc::new(AtomicUsize::new(0));
1339        let counter = AtomicUsize::new(1);
1340
1341        let txt_clone = txt.clone();
1342        let _sub1 = mgr.observe_item_added(move |_, e| {
1343            assert!(e.has_changed(&txt_clone));
1344            let c = counter.fetch_add(1, Ordering::SeqCst);
1345            let e = e.meta_mut().entry("test".to_string()).or_default();
1346            *e = c;
1347        });
1348
1349        let txt_clone = txt.clone();
1350        let result_clone = result.clone();
1351        let _sub2 = mgr.observe_item_popped(move |_, e| {
1352            assert!(e.has_changed(&txt_clone));
1353            if let Some(&v) = e.meta_mut().get("test") {
1354                result_clone.store(v, Ordering::Relaxed);
1355            }
1356        });
1357
1358        txt.insert(&mut doc.transact_mut(), 0, "abc");
1359        mgr.undo_blocking();
1360        assert_eq!(result.load(Ordering::SeqCst), 1);
1361        mgr.redo_blocking();
1362        assert_eq!(result.load(Ordering::SeqCst), 2);
1363    }
1364
1365    #[test]
1366    fn undo_until_change_performed() {
1367        let d1 = Doc::with_client_id(1);
1368        let arr1 = d1.get_or_insert_array("array");
1369
1370        let d2 = Doc::with_client_id(2);
1371        let arr2 = d2.get_or_insert_array("array");
1372
1373        let map1a = arr1.push_back(
1374            &mut d1.transact_mut(),
1375            MapPrelim::from([("hello".to_owned(), "world".to_owned())]),
1376        );
1377
1378        let map1b = arr1.push_back(
1379            &mut d1.transact_mut(),
1380            MapPrelim::from([("key".to_owned(), "value".to_owned())]),
1381        );
1382
1383        exchange_updates(&[&d1, &d2]);
1384
1385        let mut mgr1 = UndoManager::new(&d1, &arr1);
1386        mgr1.include_origin(d1.client_id());
1387
1388        let mut mgr2 = UndoManager::new(&d2, &arr2);
1389        mgr2.include_origin(d2.client_id());
1390
1391        map1b.insert(
1392            &mut d1.transact_mut_with(d1.client_id()),
1393            "key",
1394            "value modified",
1395        );
1396
1397        exchange_updates(&[&d1, &d2]);
1398        mgr1.reset();
1399
1400        map1a.insert(
1401            &mut d1.transact_mut_with(d1.client_id()),
1402            "hello",
1403            "world modified",
1404        );
1405
1406        exchange_updates(&[&d1, &d2]);
1407
1408        arr2.remove_range(&mut d2.transact_mut_with(d2.client_id()), 0, 1);
1409
1410        exchange_updates(&[&d1, &d2]);
1411        mgr2.undo_blocking();
1412
1413        exchange_updates(&[&d1, &d2]);
1414        mgr1.undo_blocking();
1415        exchange_updates(&[&d1, &d2]);
1416
1417        assert_eq!(map1b.get(&d1.transact(), "key"), Some("value".into()));
1418    }
1419
1420    #[test]
1421    fn nested_undo() {
1422        // This issue has been reported in https://github.com/yjs/yjs/issues/317
1423        let doc = Doc::with_options(crate::doc::Options {
1424            skip_gc: true,
1425            client_id: 1,
1426            ..crate::doc::Options::default()
1427        });
1428        let design = doc.get_or_insert_map("map");
1429        let mut mgr = UndoManager::with_scope_and_options(&doc, &design, {
1430            let mut o = Options::default();
1431            o.capture_timeout_millis = 0;
1432            o
1433        });
1434        {
1435            let mut txn = doc.transact_mut();
1436            design.insert(
1437                &mut txn,
1438                "text",
1439                MapPrelim::from([(
1440                    "blocks",
1441                    MapPrelim::from([("text", "Type something".to_owned())]),
1442                )]),
1443            );
1444        }
1445        let text = design
1446            .get(&doc.transact(), "text")
1447            .unwrap()
1448            .cast::<MapRef>()
1449            .unwrap();
1450
1451        {
1452            let mut txn = doc.transact_mut();
1453            text.insert(&mut txn, "blocks", MapPrelim::from([("text", "Something")]));
1454        }
1455
1456        {
1457            let mut txn = doc.transact_mut();
1458            text.insert(
1459                &mut txn,
1460                "blocks",
1461                MapPrelim::from([("text", "Something else")]),
1462            );
1463        }
1464
1465        assert_eq!(
1466            design.to_json(&doc.transact()),
1467            Any::from_json(r#"{ "text": { "blocks": { "text": "Something else" } } }"#).unwrap()
1468        );
1469        mgr.undo_blocking();
1470        assert_eq!(
1471            design.to_json(&doc.transact()),
1472            Any::from_json(r#"{ "text": { "blocks": { "text": "Something" } } }"#).unwrap()
1473        );
1474        mgr.undo_blocking();
1475        assert_eq!(
1476            design.to_json(&doc.transact()),
1477            Any::from_json(r#"{ "text": { "blocks": { "text": "Type something" } } }"#).unwrap()
1478        );
1479        mgr.undo_blocking();
1480        assert_eq!(
1481            design.to_json(&doc.transact()),
1482            Any::from_json(r#"{}"#).unwrap()
1483        );
1484        mgr.redo_blocking();
1485        assert_eq!(
1486            design.to_json(&doc.transact()),
1487            Any::from_json(r#"{ "text": { "blocks": { "text": "Type something" } } }"#).unwrap()
1488        );
1489        mgr.redo_blocking();
1490        assert_eq!(
1491            design.to_json(&doc.transact()),
1492            Any::from_json(r#"{ "text": { "blocks": { "text": "Something" } } }"#).unwrap()
1493        );
1494        mgr.redo_blocking();
1495        assert_eq!(
1496            design.to_json(&doc.transact()),
1497            Any::from_json(r#"{ "text": { "blocks": { "text": "Something else" } } }"#).unwrap()
1498        );
1499    }
1500
1501    #[test]
1502    fn consecutive_redo_bug() {
1503        // https://github.com/yjs/yjs/issues/355
1504        let doc = Doc::with_client_id(1);
1505        let root = doc.get_or_insert_map("root");
1506        let mut mgr = UndoManager::new(&doc, &root);
1507
1508        root.insert(
1509            &mut doc.transact_mut(),
1510            "a",
1511            MapPrelim::from([("x".to_owned(), Any::from(0)), ("y".to_owned(), 0.into())]),
1512        );
1513        let point = root
1514            .get(&doc.transact(), "a")
1515            .unwrap()
1516            .cast::<MapRef>()
1517            .unwrap();
1518        mgr.reset();
1519
1520        point.insert(&mut doc.transact_mut(), "x", 100);
1521        point.insert(&mut doc.transact_mut(), "y", 100);
1522        mgr.reset();
1523
1524        point.insert(&mut doc.transact_mut(), "x", 200);
1525        point.insert(&mut doc.transact_mut(), "y", 200);
1526        mgr.reset();
1527
1528        point.insert(&mut doc.transact_mut(), "x", 300);
1529        point.insert(&mut doc.transact_mut(), "y", 300);
1530        mgr.reset();
1531
1532        let actual = point.to_json(&doc.transact());
1533        assert_eq!(actual, Any::from_json(r#"{"x":300,"y":300}"#).unwrap());
1534
1535        mgr.undo_blocking(); // x=200, y=200
1536        let actual = point.to_json(&doc.transact());
1537        assert_eq!(actual, Any::from_json(r#"{"x":200,"y":200}"#).unwrap());
1538
1539        mgr.undo_blocking(); // x=100, y=100
1540        let actual = point.to_json(&doc.transact());
1541        assert_eq!(actual, Any::from_json(r#"{"x":100,"y":100}"#).unwrap());
1542
1543        mgr.undo_blocking(); // x=0, y=0
1544        let actual = point.to_json(&doc.transact());
1545        assert_eq!(actual, Any::from_json(r#"{"x":0,"y":0}"#).unwrap());
1546
1547        mgr.undo_blocking(); // null
1548        assert_eq!(root.get(&doc.transact(), "a"), None);
1549
1550        mgr.redo_blocking(); // x=0, y=0
1551        let point = root
1552            .get(&doc.transact(), "a")
1553            .unwrap()
1554            .cast::<MapRef>()
1555            .unwrap();
1556
1557        assert_eq!(actual, Any::from_json(r#"{"x":0,"y":0}"#).unwrap());
1558
1559        mgr.redo_blocking(); // x=100, y=100
1560        let actual = point.to_json(&doc.transact());
1561        assert_eq!(actual, Any::from_json(r#"{"x":100,"y":100}"#).unwrap());
1562
1563        mgr.redo_blocking(); // x=200, y=200
1564        let actual = point.to_json(&doc.transact());
1565        assert_eq!(actual, Any::from_json(r#"{"x":200,"y":200}"#).unwrap());
1566
1567        mgr.redo_blocking(); // x=300, y=300
1568        let actual = point.to_json(&doc.transact());
1569        assert_eq!(actual, Any::from_json(r#"{"x":300,"y":300}"#).unwrap());
1570    }
1571
1572    #[test]
1573    fn undo_xml_bug() {
1574        // https://github.com/yjs/yjs/issues/304
1575        const ORIGIN: &str = "origin";
1576        let doc = Doc::with_client_id(1);
1577        let f = doc.get_or_insert_xml_fragment("t");
1578        let mut mgr = UndoManager::with_scope_and_options(&doc, &f, {
1579            let mut o = Options::default();
1580            o.capture_timeout_millis = 0;
1581            o
1582        });
1583        mgr.include_origin(ORIGIN);
1584
1585        // create element
1586        {
1587            let mut txn = doc.transact_mut_with(ORIGIN);
1588            let e = f.insert(&mut txn, 0, XmlElementPrelim::empty("test-node"));
1589            e.insert_attribute(&mut txn, "a", "100");
1590            e.insert_attribute(&mut txn, "b", "0");
1591        }
1592
1593        // change one attribute
1594        {
1595            let mut txn = doc.transact_mut_with(ORIGIN);
1596            let e: XmlElementRef = f.get(&txn, 0).unwrap().try_into().unwrap();
1597            e.insert_attribute(&mut txn, "a", "200");
1598        }
1599
1600        // change both attributes
1601        {
1602            let mut txn = doc.transact_mut_with(ORIGIN);
1603            let e: XmlElementRef = f.get(&txn, 0).unwrap().try_into().unwrap();
1604            e.insert_attribute(&mut txn, "a", "180");
1605            e.insert_attribute(&mut txn, "b", "50");
1606        }
1607
1608        mgr.undo_blocking();
1609        mgr.undo_blocking();
1610        mgr.undo_blocking();
1611
1612        mgr.redo_blocking();
1613        mgr.redo_blocking();
1614        mgr.redo_blocking();
1615
1616        let str = f.get_string(&doc.transact());
1617        assert!(
1618            str == r#"<test-node a="180" b="50"></test-node>"#
1619                || str == r#"<test-node b="50" a="180"></test-node>"#
1620        );
1621    }
1622
1623    #[test]
1624    fn undo_block_bug() {
1625        // https://github.com/yjs/yjs/issues/343
1626        let doc = Doc::with_options({
1627            let mut o = crate::doc::Options::default();
1628            o.client_id = 1;
1629            o.skip_gc = true;
1630            o
1631        });
1632        let design = doc.get_or_insert_map("map");
1633        let mut mgr = UndoManager::with_scope_and_options(&doc, &design, {
1634            let mut o = Options::default();
1635            o.capture_timeout_millis = 0;
1636            o
1637        });
1638        let text = {
1639            let mut txn = doc.transact_mut();
1640            design.insert(
1641                &mut txn,
1642                "text",
1643                MapPrelim::from([(
1644                    "blocks",
1645                    MapPrelim::from([("text".to_owned(), "1".to_owned())]),
1646                )]),
1647            )
1648        };
1649        {
1650            let mut txn = doc.transact_mut();
1651            text.insert(
1652                &mut txn,
1653                "blocks",
1654                MapPrelim::from([("text".to_owned(), "1".to_owned())]),
1655            );
1656        }
1657        {
1658            let mut txn = doc.transact_mut();
1659            text.insert(
1660                &mut txn,
1661                "blocks",
1662                MapPrelim::from([("text".to_owned(), "3".to_owned())]),
1663            );
1664        }
1665        {
1666            let mut txn = doc.transact_mut();
1667            text.insert(
1668                &mut txn,
1669                "blocks",
1670                MapPrelim::from([("text".to_owned(), "4".to_owned())]),
1671            );
1672        }
1673        // {"text":{"blocks":{"text":"4"}}}
1674        mgr.undo_blocking(); // {"text":{"blocks":{"3"}}}
1675        mgr.undo_blocking(); // {"text":{"blocks":{"text":"2"}}}
1676        mgr.undo_blocking(); // {"text":{"blocks":{"text":"1"}}}
1677        mgr.undo_blocking(); // {}
1678        mgr.redo_blocking(); // {"text":{"blocks":{"text":"1"}}}
1679        mgr.redo_blocking(); // {"text":{"blocks":{"text":"2"}}}
1680        mgr.redo_blocking(); // {"text":{"blocks":{"text":"3"}}}
1681        mgr.redo_blocking(); // {"text":{}}
1682        let actual = design.to_json(&doc.transact());
1683        assert_eq!(
1684            actual,
1685            Any::from_json(r#"{"text":{"blocks":{"text":"4"}}}"#).unwrap()
1686        );
1687    }
1688
1689    #[test]
1690    fn undo_delete_text_format() {
1691        // https://github.com/yjs/yjs/issues/392
1692        fn send(src: &Doc, dst: &Doc) {
1693            let update = Update::decode_v1(
1694                &src.transact()
1695                    .encode_state_as_update_v1(&StateVector::default()),
1696            )
1697            .unwrap();
1698            dst.transact_mut().apply_update(update).unwrap();
1699        }
1700
1701        let doc1 = Doc::with_client_id(1);
1702        let txt = doc1.get_or_insert_text("test");
1703        txt.insert(
1704            &mut doc1.transact_mut(),
1705            0,
1706            "Attack ships on fire off the shoulder of Orion.",
1707        ); // D1: 'Attack ships on fire off the shoulder of Orion.'
1708        let doc2 = Doc::with_client_id(2);
1709        let txt2 = doc2.get_or_insert_text("test");
1710
1711        send(&doc1, &doc2); // D2: 'Attack ships on fire off the shoulder of Orion.'
1712        let mut mgr = UndoManager::new(&doc1, &txt);
1713
1714        let attrs = Attrs::from([("bold".into(), true.into())]);
1715        txt.format(&mut doc1.transact_mut(), 13, 7, attrs.clone()); // D1: 'Attack ships <b>on fire</b> off the shoulder of Orion.'
1716
1717        mgr.reset();
1718
1719        send(&doc1, &doc2); // D2: 'Attack ships <b>on fire</b> off the shoulder of Orion.'
1720
1721        let attrs2 = Attrs::from([("bold".into(), Any::Null)]);
1722        txt.format(&mut doc1.transact_mut(), 16, 4, attrs2.clone()); // D1: 'Attack ships <b>on </b>fire off the shoulder of Orion.'
1723
1724        let expected = vec![
1725            Diff::new("Attack ships ".into(), None),
1726            Diff::new("on ".into(), Some(Box::new(attrs.clone()))),
1727            Diff::new("fire off the shoulder of Orion.".into(), None),
1728        ];
1729        let actual = txt.diff(&doc1.transact(), YChange::identity);
1730        assert_eq!(actual, expected);
1731
1732        mgr.reset();
1733        send(&doc1, &doc2); // D2: 'Attack ships <b>on </b>fire off the shoulder of Orion.'
1734
1735        mgr.undo_blocking(); // D1: 'Attack ships <b>on fire</b> off the shoulder of Orion.'
1736        send(&doc1, &doc2); // D2: 'Attack ships <b>on fire</b> off the shoulder of Orion.'
1737
1738        let expected = vec![
1739            Diff::new("Attack ships ".into(), None),
1740            Diff::new("on fire".into(), Some(Box::new(attrs))),
1741            Diff::new(" off the shoulder of Orion.".into(), None),
1742        ];
1743        let actual = txt.diff(&doc1.transact(), YChange::identity);
1744        assert_eq!(actual, expected);
1745        let actual = txt2.diff(&doc2.transact(), YChange::identity);
1746        assert_eq!(actual, expected);
1747    }
1748
1749    #[test]
1750    fn special_deletion_case() {
1751        // https://github.com/yjs/yjs/issues/447
1752        const ORIGIN: &str = "undoable";
1753        let doc = Doc::with_client_id(1);
1754        let f = doc.get_or_insert_xml_fragment("test");
1755        let mut mgr = UndoManager::new(&doc, &f);
1756        mgr.include_origin(ORIGIN);
1757        {
1758            let mut txn = doc.transact_mut();
1759            let e = f.insert(&mut txn, 0, XmlElementPrelim::empty("test"));
1760            e.insert_attribute(&mut txn, "a", "1");
1761            e.insert_attribute(&mut txn, "b", "2");
1762        }
1763        let s = f.get_string(&doc.transact());
1764        assert!(s == r#"<test a="1" b="2"></test>"# || s == r#"<test b="2" a="1"></test>"#);
1765        {
1766            // change attribute "b" and delete test-node
1767            let mut txn = doc.transact_mut_with(ORIGIN);
1768            let e: XmlElementRef = f.get(&txn, 0).unwrap().try_into().unwrap();
1769            e.insert_attribute(&mut txn, "b", "3");
1770            f.remove_range(&mut txn, 0, 1);
1771        }
1772        assert_eq!(f.get_string(&doc.transact()), "");
1773
1774        mgr.undo_blocking();
1775        let s = f.get_string(&doc.transact());
1776        assert!(s == r#"<test a="1" b="2"></test>"# || s == r#"<test b="2" a="1"></test>"#);
1777    }
1778
1779    #[test]
1780    fn undo_in_embed() {
1781        let d1 = Doc::with_client_id(1);
1782        let txt1 = d1.get_or_insert_text("test");
1783        let mut mgr = UndoManager::new(&d1, &txt1);
1784
1785        let d2 = Doc::with_client_id(2);
1786        let txt2 = d2.get_or_insert_text("test");
1787
1788        let attrs = Attrs::from([("bold".into(), true.into())]);
1789        let nested = txt1.insert_embed_with_attributes(
1790            &mut d1.transact_mut(),
1791            0,
1792            TextPrelim::new("initial text"),
1793            attrs,
1794        );
1795        assert_eq!(
1796            nested.get_string(&d1.transact()),
1797            "initial text".to_string()
1798        );
1799        mgr.reset();
1800        {
1801            let mut txn = d1.transact_mut();
1802            let len = nested.len(&txn);
1803            nested.remove_range(&mut txn, 0, len);
1804        }
1805        nested.insert(&mut d1.transact_mut(), 0, "other text");
1806        assert_eq!(nested.get_string(&d1.transact()), "other text".to_string());
1807        mgr.undo_blocking();
1808        assert_eq!(
1809            nested.get_string(&d1.transact()),
1810            "initial text".to_string()
1811        );
1812
1813        exchange_updates(&[&d1, &d2]);
1814        let diff = txt2.diff(&d1.transact(), YChange::identity);
1815        let nested2 = diff[0].insert.clone().cast::<TextRef>().unwrap();
1816        assert_eq!(
1817            nested2.get_string(&d2.transact()),
1818            "initial text".to_string()
1819        );
1820
1821        mgr.undo_blocking();
1822        assert_eq!(nested.len(&d1.transact()), 0);
1823    }
1824
1825    #[test]
1826    fn github_issue_345() {
1827        // https://github.com/y-crdt/y-crdt/issues/345
1828        let doc = Doc::new();
1829        let map = doc.get_or_insert_map("r");
1830        let mut mgr = UndoManager::with_scope_and_options(&doc, &map, Options::default());
1831        mgr.include_origin(doc.client_id());
1832
1833        let s1 = map.insert(
1834            &mut doc.transact_mut_with(doc.client_id()),
1835            "s1",
1836            MapPrelim::default(),
1837        );
1838        mgr.reset();
1839
1840        let mut txn = doc.transact_mut_with(doc.client_id());
1841        let a = s1.insert(&mut txn, "a", ArrayPrelim::default());
1842        a.insert(&mut txn, 0, "a1");
1843        drop(txn);
1844        mgr.reset();
1845
1846        let mut txn = doc.transact_mut_with(doc.client_id());
1847        let b = s1.insert(&mut txn, "b", ArrayPrelim::default());
1848        b.insert(&mut txn, 0, "b1");
1849        drop(txn);
1850        mgr.reset();
1851
1852        b.insert(&mut doc.transact_mut_with(doc.client_id()), 1, "b2");
1853        mgr.reset();
1854
1855        a.insert(&mut doc.transact_mut_with(doc.client_id()), 1, "a3");
1856        mgr.reset();
1857
1858        a.insert(&mut doc.transact_mut_with(doc.client_id()), 2, "a4");
1859        mgr.reset();
1860
1861        a.insert(&mut doc.transact_mut_with(doc.client_id()), 3, "a5");
1862        mgr.reset();
1863
1864        let actual = map.to_json(&doc.transact());
1865        assert_eq!(
1866            actual,
1867            any!({"s1": {"a": ["a1", "a3", "a4", "a5"], "b": ["b1", "b2"]}})
1868        );
1869
1870        mgr.undo_blocking(); // {"s1": {"a": ["a1", "a3", "a4"], "b": ["b1", "b2"]}}
1871        mgr.undo_blocking(); // {"s1": {"a": ["a1", "a3"], "b": ["b1", "b2"]}}
1872        mgr.undo_blocking(); // {"s1": {"a": ["a1"], "b": ["b1", "b2"]}}
1873        mgr.undo_blocking(); // {"s1": {"a": ["a1"], "b": ["b1"]}}
1874        let actual = map.to_json(&doc.transact());
1875        assert_eq!(actual, any!({"s1": {"a": ["a1"], "b": ["b1"]}}));
1876
1877        mgr.redo_blocking();
1878        let actual = map.to_json(&doc.transact());
1879        assert_eq!(actual, any!({"s1": {"b": ["b1", "b2"], "a": ["a1"]}}));
1880
1881        mgr.redo_blocking();
1882        let actual = map.to_json(&doc.transact());
1883        assert_eq!(actual, any!({"s1": {"a": ["a1", "a3"], "b": ["b1", "b2"]}}));
1884
1885        mgr.redo_blocking();
1886        let actual = map.to_json(&doc.transact());
1887        assert_eq!(
1888            actual,
1889            any!({"s1": {"a": ["a1", "a3", "a4"], "b": ["b1", "b2"]}})
1890        );
1891
1892        mgr.redo_blocking();
1893        let actual = map.to_json(&doc.transact());
1894        assert_eq!(
1895            actual,
1896            any!({"s1": {"a": ["a1", "a3", "a4", "a5"], "b": ["b1", "b2"]}})
1897        );
1898    }
1899
1900    #[test]
1901    fn github_issue_345_part_2() {
1902        // https://github.com/y-crdt/y-crdt/issues/345
1903        let d = Doc::new();
1904        let r = d.get_or_insert_map("r");
1905
1906        let s1 = r.insert(&mut d.transact_mut(), "s1", MapPrelim::default());
1907
1908        let mut mgr = UndoManager::with_scope_and_options(&d, &r, Options::default());
1909        {
1910            let mut txn = d.transact_mut();
1911            let b1 = s1.insert(&mut txn, "b1", MapPrelim::default());
1912            b1.insert(&mut txn, "f1", 11);
1913        }
1914        mgr.reset();
1915
1916        s1.remove(&mut d.transact_mut(), "b1");
1917        mgr.reset();
1918
1919        {
1920            let mut txn = d.transact_mut();
1921            let b1 = s1.insert(&mut txn, "b1", MapPrelim::default());
1922            b1.insert(&mut txn, "f1", 20);
1923        }
1924        mgr.reset();
1925
1926        let actual = r.to_json(&d.transact());
1927        assert_eq!(actual, any!({"s1": {"b1": {"f1": 20}}}));
1928
1929        mgr.undo_blocking();
1930        let actual = r.to_json(&d.transact());
1931        assert_eq!(actual, any!({"s1": {}}));
1932        assert!(mgr.can_undo(), "should be able to undo");
1933
1934        mgr.undo_blocking();
1935        let actual = r.to_json(&d.transact());
1936        assert_eq!(actual, any!({"s1": {"b1": {"f1": 11}}}));
1937        assert!(mgr.can_undo(), "should be able to undo to the init state");
1938    }
1939
1940    #[test]
1941    fn issue_371() {
1942        let doc = Doc::with_client_id(1);
1943
1944        let r = doc.get_or_insert_map("r");
1945        let s1 = r.insert(&mut doc.transact_mut(), "s1", MapPrelim::default()); // { s1: {} }
1946        let b1_arr = s1.insert(&mut doc.transact_mut(), "b1", ArrayPrelim::default()); // { s1: { b1: [] } }
1947        let el1 = b1_arr.insert(&mut doc.transact_mut(), 0, MapPrelim::default()); // { s1: { b1: [{}] } }
1948        el1.insert(&mut doc.transact_mut(), "f1", 8); // { s1: { b1: [{ f1: 8 }] } }
1949        el1.insert(&mut doc.transact_mut(), "f2", true); // { s1: { b1: [{ f1: 8, f2: true }] } }
1950
1951        let mut mgr = UndoManager::with_scope_and_options(&doc, &r, Options::default());
1952        {
1953            let mut txn = doc.transact_mut();
1954            let el0 = b1_arr.insert(&mut txn, 0, MapPrelim::default()); // { s1: { b1: [{}, { f1: 8, f2: true }] } }
1955            el0.insert(&mut txn, "f1", 8); // { s1: { b1: [{ f1: 8 }, { f1: 8, f2: true }] } }
1956            el0.insert(&mut txn, "f2", false); // { s1: { b1: [{ f1: 8, f2: false }, { f1: 8, f2: true }] } }
1957
1958            el1.insert(&mut txn, "f1", 13); // { s1: { b1: [{ f1: 8, f2: false }, { f1: 13, f2: true }] } }
1959            el1.remove(&mut txn, "f2"); // { s1: { b1: [{ f1: 8, f2: false }, { f1: 13 }] } }
1960        }
1961        mgr.reset();
1962
1963        el1.insert(&mut doc.transact_mut(), "f2", false); // { s1: { b1: [{ f1: 8, f2: false }, { f1: 13, f2: false }] } }
1964        mgr.reset();
1965
1966        el1.insert(&mut doc.transact_mut(), "f2", true); // { s1: { b1: [{ f1: 8, f2: false }, { f1: 13, f2: true }] } }
1967        mgr.reset();
1968
1969        mgr.undo_blocking(); // { s1: { b1: [{ f1: 8, f2: false }, { f1: 13, f2: false }] } }
1970        assert_eq!(
1971            r.to_json(&doc.transact()),
1972            any!({ "s1": { "b1": [{ "f1": 8, "f2": false }, { "f1": 13, "f2": false }] } })
1973        );
1974        mgr.undo_blocking(); // { s1: { b1: [{ f1: 8, f2: false }, { f1: 13 }] } }
1975        assert_eq!(
1976            r.to_json(&doc.transact()),
1977            any!({ "s1": { "b1": [{ "f1": 8, "f2": false }, { "f1": 13 }] } })
1978        );
1979        mgr.undo_blocking(); // { s1: { b1: [{ f1: 8, f2: true }] } }
1980        assert_eq!(
1981            r.to_json(&doc.transact()),
1982            any!({ "s1": { "b1": [{ "f1": 8, "f2": true }] } })
1983        );
1984        assert!(!mgr.undo_blocking()); // no more changes tracked by undo manager
1985    }
1986
1987    #[test]
1988    fn issue_371_2() {
1989        let doc = Doc::with_client_id(1);
1990        let r = doc.get_or_insert_map("r");
1991        let s1 = r.insert(&mut doc.transact_mut(), "s1", MapPrelim::default()); // { s1:{} }
1992        s1.insert(&mut doc.transact_mut(), "f2", "AAA"); // { s1: { f2: AAA } }
1993        s1.insert(&mut doc.transact_mut(), "f1", false); // { s1: { f1: false, f2: AAA } }
1994
1995        let mut mgr = UndoManager::with_scope_and_options(&doc, &r, Options::default());
1996        s1.remove(&mut doc.transact_mut(), "f2"); // { s1: { f1: false } }
1997        mgr.reset();
1998
1999        s1.insert(&mut doc.transact_mut(), "f2", "C1"); // { s1: { f1: false, f2: C1 } }
2000        mgr.reset();
2001
2002        s1.insert(&mut doc.transact_mut(), "f2", "C2"); // { s1: { f1: false, f2: C2 } }
2003        mgr.reset();
2004
2005        mgr.undo_blocking(); // { s1: { f1: false, f2: C1 } }
2006        assert_eq!(
2007            r.to_json(&doc.transact()),
2008            any!({ "s1": { "f1": false, "f2": "C1" } })
2009        );
2010        mgr.undo_blocking(); // { s1: { f1: false } }
2011        assert_eq!(r.to_json(&doc.transact()), any!({ "s1": { "f1": false } }));
2012        mgr.undo_blocking(); // { s1: { f1: false, f2: AAA } }
2013        assert_eq!(
2014            r.to_json(&doc.transact()),
2015            any!({ "s1": { "f1": false, "f2": "AAA" } })
2016        );
2017        assert!(!mgr.undo_blocking()); // no more changes tracked by undo manager
2018    }
2019
2020    #[test]
2021    fn issue_380() {
2022        let d = Doc::with_client_id(1);
2023        let r = d.get_or_insert_map("r"); // {r:{}}
2024        let s1 = r.insert(&mut d.transact_mut(), "s1", MapPrelim::default()); // {r:{s1:{}}
2025        let b1_arr = s1.insert(&mut d.transact_mut(), "b1", ArrayPrelim::default()); // {r:{s1:{b1:[]}}
2026
2027        let b1_el1 = b1_arr.insert(&mut d.transact_mut(), 0, MapPrelim::default()); // {r:{s1:{b1:[{}]}}
2028        let b2_arr = b1_el1.insert(&mut d.transact_mut(), "b2", ArrayPrelim::default()); // {r:{s1:{b1:[{b2:[]}]}}
2029        let b2_arr_nest = b2_arr.insert(&mut d.transact_mut(), 0, ArrayPrelim::default()); // {r:{s1:{b1:[{b2:[[]]}]}}
2030        b2_arr_nest.insert(&mut d.transact_mut(), 0, 232291652); // {r:{s1:{b1:[{b2:[[232291652]]}]}}
2031        b2_arr_nest.insert(&mut d.transact_mut(), 1, -30); // {r:{s1:{b1:[{b2:[[232291652, -30]]}]}}
2032
2033        let mut mgr = UndoManager::with_scope_and_options(&d, &r, Options::default());
2034
2035        let mut txn = d.transact_mut();
2036        b2_arr_nest.remove(&mut txn, 1); // {r:{s1:{b1:[{b2:[[232291652]]}]}}
2037        b2_arr_nest.insert(&mut txn, 1, -5); // {r:{s1:{b1:[{b2:[[232291652, -5]]}]}}
2038
2039        drop(txn);
2040        mgr.reset();
2041
2042        let mut txn = d.transact_mut();
2043
2044        let b1_el0 = b1_arr.insert(&mut txn, 0, MapPrelim::default()); // {r:{s1:{b1:[{},{b2:[[232291652, -5]]}]}}
2045        let b2_0_arr = b1_el0.insert(&mut txn, "b2", ArrayPrelim::default()); // {r:{s1:{b1:[{b2:[]},{b2:[[232291652, -5]]}]}}
2046        let b2_0_arr_nest = b2_0_arr.insert(&mut txn, 0, ArrayPrelim::default()); // {r:{s1:{b1:[{b2:[[]]},{b2:[[232291652, -5]]}]}}
2047        b2_0_arr_nest.insert(&mut txn, 0, 232291652); // {r:{s1:{b1:[{b2:[[232291652]]},{b2:[[232291652, -5]]}]}}
2048        b2_0_arr_nest.insert(&mut txn, 1, -6); // {r:{s1:{b1:[{b2:[[232291652, -6]]},{b2:[[232291652, -5]]}]}}
2049        b1_arr.remove(&mut txn, 1); // {r:{s1:{b1:[{b2:[[232291652, -6]]}]}}
2050
2051        let b1_el1 = b1_arr.insert(&mut txn, 1, MapPrelim::default()); // {r:{s1:{b1:[{b2:[[232291652, -6]]}, {}]}}
2052        b1_el1.insert(&mut txn, "f2", "C1"); // {r:{s1:{b1:[{b2:[[232291652, -6]]}, {f2:C1}]}}
2053
2054        drop(txn);
2055        mgr.reset();
2056        assert_eq!(
2057            r.to_json(&d.transact()),
2058            any!({"s1":{"b1":[{"b2":[[232291652, -6]]}, {"f2":"C1"}]}})
2059        );
2060
2061        mgr.undo_blocking(); // {r:{s1:{b1:[{b2:[[232291652, -5]]}]}}
2062        assert_eq!(
2063            r.to_json(&d.transact()),
2064            any!({"s1":{"b1":[{"b2":[[232291652, -5]]}]}})
2065        );
2066
2067        mgr.undo_blocking(); // {r:{s1:{b1:[{b2:[[232291652, -30]]}]}}
2068        assert_eq!(
2069            r.to_json(&d.transact()),
2070            any!({"s1":{"b1":[{"b2":[[232291652, -30]]}]}})
2071        );
2072    }
2073}