Skip to main content

loro_ffi/
undo.rs

1use std::sync::{Arc, Mutex};
2
3use loro::{LoroResult, PeerID};
4
5use crate::{Cursor, DiffEvent, LoroDoc, LoroValue, Side};
6
7pub struct UndoManager(Mutex<loro::UndoManager>);
8
9impl UndoManager {
10    /// Create a new UndoManager.
11    pub fn new(doc: &LoroDoc) -> Self {
12        Self(Mutex::new(loro::UndoManager::new(doc)))
13    }
14
15    /// Undo the last change made by the peer.
16    pub fn undo(&self) -> LoroResult<bool> {
17        self.0.lock().unwrap().undo()
18    }
19
20    /// Redo the last change made by the peer.
21    pub fn redo(&self) -> LoroResult<bool> {
22        self.0.lock().unwrap().redo()
23    }
24
25    /// Record a new checkpoint.
26    pub fn record_new_checkpoint(&self) -> LoroResult<()> {
27        self.0.lock().unwrap().record_new_checkpoint()
28    }
29
30    /// Whether the undo manager can undo.
31    pub fn can_undo(&self) -> bool {
32        self.0.lock().unwrap().can_undo()
33    }
34
35    /// Whether the undo manager can redo.
36    pub fn can_redo(&self) -> bool {
37        self.0.lock().unwrap().can_redo()
38    }
39
40    /// How many times the undo manager can undo.
41    pub fn undo_count(&self) -> u32 {
42        self.0.lock().unwrap().undo_count() as u32
43    }
44
45    /// How many times the undo manager can redo.
46    pub fn redo_count(&self) -> u32 {
47        self.0.lock().unwrap().redo_count() as u32
48    }
49
50    /// If a local event's origin matches the given prefix, it will not be recorded in the
51    /// undo stack.
52    pub fn add_exclude_origin_prefix(&self, prefix: &str) {
53        self.0.lock().unwrap().add_exclude_origin_prefix(prefix)
54    }
55
56    /// Set the maximum number of undo steps. The default value is 100.
57    pub fn set_max_undo_steps(&self, size: u32) {
58        self.0.lock().unwrap().set_max_undo_steps(size as usize)
59    }
60
61    /// Set the merge interval in ms. The default value is 0, which means no merge.
62    pub fn set_merge_interval(&self, interval: i64) {
63        self.0.lock().unwrap().set_merge_interval(interval)
64    }
65
66    /// Set the listener for push events.
67    /// The listener will be called when a new undo/redo item is pushed into the stack.
68    pub fn set_on_push(&self, on_push: Option<Arc<dyn OnPush>>) {
69        if let Some(on_push) = on_push {
70            self.0
71                .lock()
72                .unwrap()
73                .set_on_push(Some(Box::new(move |u, c, e| {
74                    loro::UndoItemMeta::from(on_push.on_push(u, c, e.map(|x| x.into())))
75                })));
76        } else {
77            self.0.lock().unwrap().set_on_push(None);
78        }
79    }
80
81    /// Set the listener for pop events.
82    /// The listener will be called when an undo/redo item is popped from the stack.
83    pub fn set_on_pop(&self, on_pop: Option<Arc<dyn OnPop>>) {
84        if let Some(on_pop) = on_pop {
85            self.0
86                .lock()
87                .unwrap()
88                .set_on_pop(Some(Box::new(move |u, c, m| {
89                    on_pop.on_pop(u, c, UndoItemMeta::from(m))
90                })));
91        } else {
92            self.0.lock().unwrap().set_on_pop(None);
93        }
94    }
95
96    pub fn group_start(&self) -> LoroResult<()> {
97        self.0.lock().unwrap().group_start()
98    }
99
100    pub fn group_end(&self) {
101        self.0.lock().unwrap().group_end()
102    }
103
104    pub fn peer(&self) -> PeerID {
105        self.0.lock().unwrap().peer()
106    }
107
108    pub fn top_undo_meta(&self) -> Option<UndoItemMeta> {
109        self.0
110            .lock()
111            .unwrap()
112            .top_undo_meta()
113            .map(UndoItemMeta::from)
114    }
115
116    pub fn top_redo_meta(&self) -> Option<UndoItemMeta> {
117        self.0
118            .lock()
119            .unwrap()
120            .top_redo_meta()
121            .map(UndoItemMeta::from)
122    }
123
124    pub fn top_undo_value(&self) -> Option<LoroValue> {
125        self.0.lock().unwrap().top_undo_value().map(Into::into)
126    }
127
128    pub fn top_redo_value(&self) -> Option<LoroValue> {
129        self.0.lock().unwrap().top_redo_value().map(Into::into)
130    }
131}
132
133#[uniffi::trait_interface]
134pub trait OnPush: Send + Sync {
135    fn on_push(
136        &self,
137        undo_or_redo: loro::UndoOrRedo,
138        counter_span: loro::CounterSpan,
139        diff_event: Option<DiffEvent>,
140    ) -> UndoItemMeta;
141}
142
143#[uniffi::trait_interface]
144pub trait OnPop: Send + Sync {
145    fn on_pop(
146        &self,
147        undo_or_redo: loro::undo::UndoOrRedo,
148        counter_span: loro::CounterSpan,
149        undo_meta: UndoItemMeta,
150    );
151}
152
153#[derive(Debug, Clone)]
154pub struct UndoItemMeta {
155    pub value: LoroValue,
156    pub cursors: Vec<CursorWithPos>,
157}
158
159impl From<loro::undo::UndoItemMeta> for UndoItemMeta {
160    fn from(meta: loro::undo::UndoItemMeta) -> Self {
161        Self {
162            value: meta.value.into(),
163            cursors: meta
164                .cursors
165                .into_iter()
166                .map(|c| CursorWithPos {
167                    cursor: Arc::new(c.cursor.into()),
168                    pos: AbsolutePosition {
169                        pos: c.pos.pos as u32,
170                        side: c.pos.side,
171                    },
172                })
173                .collect(),
174        }
175    }
176}
177
178impl From<&UndoItemMeta> for loro::undo::UndoItemMeta {
179    fn from(meta: &UndoItemMeta) -> Self {
180        loro::undo::UndoItemMeta {
181            value: (&meta.value).into(),
182            cursors: meta
183                .cursors
184                .iter()
185                .map(|c| loro::undo::CursorWithPos {
186                    cursor: c.cursor.as_ref().clone().into(),
187                    pos: loro::cursor::AbsolutePosition {
188                        pos: c.pos.pos as usize,
189                        side: c.pos.side,
190                    },
191                })
192                .collect(),
193        }
194    }
195}
196
197impl From<UndoItemMeta> for loro::undo::UndoItemMeta {
198    fn from(meta: UndoItemMeta) -> Self {
199        loro::undo::UndoItemMeta {
200            value: (meta.value).into(),
201            cursors: meta
202                .cursors
203                .into_iter()
204                .map(|c| loro::undo::CursorWithPos {
205                    cursor: c.cursor.as_ref().clone().into(),
206                    pos: loro::cursor::AbsolutePosition {
207                        pos: c.pos.pos as usize,
208                        side: c.pos.side,
209                    },
210                })
211                .collect(),
212        }
213    }
214}
215
216#[derive(Debug, Clone)]
217pub struct CursorWithPos {
218    pub cursor: Arc<Cursor>,
219    pub pos: AbsolutePosition,
220}
221
222#[derive(Debug, Clone, Copy)]
223pub struct AbsolutePosition {
224    pub pos: u32,
225    pub side: Side,
226}