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
133pub trait OnPush: Send + Sync {
134    fn on_push(
135        &self,
136        undo_or_redo: loro::UndoOrRedo,
137        counter_span: loro::CounterSpan,
138        diff_event: Option<DiffEvent>,
139    ) -> UndoItemMeta;
140}
141
142pub trait OnPop: Send + Sync {
143    fn on_pop(
144        &self,
145        undo_or_redo: loro::undo::UndoOrRedo,
146        counter_span: loro::CounterSpan,
147        undo_meta: UndoItemMeta,
148    );
149}
150
151#[derive(Debug, Clone)]
152pub struct UndoItemMeta {
153    pub value: LoroValue,
154    pub cursors: Vec<CursorWithPos>,
155}
156
157impl From<loro::undo::UndoItemMeta> for UndoItemMeta {
158    fn from(meta: loro::undo::UndoItemMeta) -> Self {
159        Self {
160            value: meta.value.into(),
161            cursors: meta
162                .cursors
163                .into_iter()
164                .map(|c| CursorWithPos {
165                    cursor: Arc::new(c.cursor.into()),
166                    pos: AbsolutePosition {
167                        pos: c.pos.pos as u32,
168                        side: c.pos.side,
169                    },
170                })
171                .collect(),
172        }
173    }
174}
175
176impl From<&UndoItemMeta> for loro::undo::UndoItemMeta {
177    fn from(meta: &UndoItemMeta) -> Self {
178        loro::undo::UndoItemMeta {
179            value: (&meta.value).into(),
180            cursors: meta
181                .cursors
182                .iter()
183                .map(|c| loro::undo::CursorWithPos {
184                    cursor: c.cursor.as_ref().clone().into(),
185                    pos: loro::cursor::AbsolutePosition {
186                        pos: c.pos.pos as usize,
187                        side: c.pos.side,
188                    },
189                })
190                .collect(),
191        }
192    }
193}
194
195impl From<UndoItemMeta> for loro::undo::UndoItemMeta {
196    fn from(meta: UndoItemMeta) -> Self {
197        loro::undo::UndoItemMeta {
198            value: (meta.value).into(),
199            cursors: meta
200                .cursors
201                .into_iter()
202                .map(|c| loro::undo::CursorWithPos {
203                    cursor: c.cursor.as_ref().clone().into(),
204                    pos: loro::cursor::AbsolutePosition {
205                        pos: c.pos.pos as usize,
206                        side: c.pos.side,
207                    },
208                })
209                .collect(),
210        }
211    }
212}
213
214#[derive(Debug, Clone)]
215pub struct CursorWithPos {
216    pub cursor: Arc<Cursor>,
217    pub pos: AbsolutePosition,
218}
219
220#[derive(Debug, Clone, Copy)]
221pub struct AbsolutePosition {
222    pub pos: u32,
223    pub side: Side,
224}