Skip to content

Commit accfb92

Browse files
author
andreim27
committed
feat(undo): add clearRedo and clearUndo methods
Add methods to selectively clear only the redo or undo stack, preserving the other stack. This is useful when coordinating undo/redo across multiple participants (e.g., multiple editors) where a new edit in one participant should invalidate redo in all other participants without affecting their undo history.
1 parent f9a6adb commit accfb92

3 files changed

Lines changed: 94 additions & 0 deletions

File tree

crates/loro-internal/src/undo.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,16 @@ impl UndoManager {
955955
self.inner.lock().borrow_mut().redo_stack.clear();
956956
}
957957

958+
/// Clear only the redo stack, preserving the undo stack.
959+
pub fn clear_redo(&self) {
960+
self.inner.lock().borrow_mut().redo_stack.clear();
961+
}
962+
963+
/// Clear only the undo stack, preserving the redo stack.
964+
pub fn clear_undo(&self) {
965+
self.inner.lock().borrow_mut().undo_stack.clear();
966+
}
967+
958968
pub fn set_top_undo_meta(&self, meta: UndoItemMeta) {
959969
self.inner.lock().borrow_mut().undo_stack.set_top_meta(meta);
960970
}

crates/loro-internal/tests/undo.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,59 @@ fn test_undo_group_start_with_remote_ops() {
165165
undo_manager.undo().unwrap();
166166
assert_eq!(doc.get_text("text").to_string(), "test");
167167
}
168+
169+
#[test]
170+
fn test_clear_redo() {
171+
let doc = LoroDoc::new();
172+
let undo_manager = UndoManager::new(&doc);
173+
let text = doc.get_text("text");
174+
175+
// Make some edits
176+
text.update("hello", UpdateOptions::default()).unwrap();
177+
doc.commit_then_renew();
178+
text.update("hello world", UpdateOptions::default()).unwrap();
179+
doc.commit_then_renew();
180+
181+
// Undo to create redo stack
182+
undo_manager.undo().unwrap();
183+
assert_eq!(text.to_string(), "hello");
184+
assert!(undo_manager.can_redo(), "should be able to redo");
185+
assert!(undo_manager.can_undo(), "should be able to undo");
186+
187+
// Clear only redo stack
188+
undo_manager.clear_redo();
189+
assert!(!undo_manager.can_redo(), "redo stack should be empty");
190+
assert!(undo_manager.can_undo(), "undo stack should still have items");
191+
192+
// Verify undo still works
193+
undo_manager.undo().unwrap();
194+
assert_eq!(text.to_string(), "");
195+
}
196+
197+
#[test]
198+
fn test_clear_undo() {
199+
let doc = LoroDoc::new();
200+
let undo_manager = UndoManager::new(&doc);
201+
let text = doc.get_text("text");
202+
203+
// Make some edits
204+
text.update("hello", UpdateOptions::default()).unwrap();
205+
doc.commit_then_renew();
206+
text.update("hello world", UpdateOptions::default()).unwrap();
207+
doc.commit_then_renew();
208+
209+
// Undo to create redo stack
210+
undo_manager.undo().unwrap();
211+
assert_eq!(text.to_string(), "hello");
212+
assert!(undo_manager.can_redo(), "should be able to redo");
213+
assert!(undo_manager.can_undo(), "should be able to undo");
214+
215+
// Clear only undo stack
216+
undo_manager.clear_undo();
217+
assert!(undo_manager.can_redo(), "redo stack should still have items");
218+
assert!(!undo_manager.can_undo(), "undo stack should be empty");
219+
220+
// Verify redo still works
221+
undo_manager.redo().unwrap();
222+
assert_eq!(text.to_string(), "hello world");
223+
}

crates/loro-wasm/src/lib.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5332,6 +5332,20 @@ impl UndoManager {
53325332
pub fn clear(&self) {
53335333
self.undo.lock().clear();
53345334
}
5335+
5336+
/// Clear only the redo stack, preserving the undo stack.
5337+
///
5338+
/// This is useful when coordinating undo/redo across multiple participants
5339+
/// (e.g., multiple editors) where a new edit in one participant should
5340+
/// invalidate redo in all other participants.
5341+
pub fn clearRedo(&self) {
5342+
self.undo.lock().clear_redo();
5343+
}
5344+
5345+
/// Clear only the undo stack, preserving the redo stack.
5346+
pub fn clearUndo(&self) {
5347+
self.undo.lock().clear_undo();
5348+
}
53355349
}
53365350

53375351
/// Use this function to throw an error after the micro task.
@@ -6558,6 +6572,20 @@ interface UndoManager {
65586572
* Ends the current grouping of undo operations.
65596573
*/
65606574
groupEnd(): void;
6575+
6576+
/**
6577+
* Clear only the redo stack, preserving the undo stack.
6578+
*
6579+
* This is useful when coordinating undo/redo across multiple participants
6580+
* (e.g., multiple editors) where a new edit in one participant should
6581+
* invalidate redo in all other participants.
6582+
*/
6583+
clearRedo(): void;
6584+
6585+
/**
6586+
* Clear only the undo stack, preserving the redo stack.
6587+
*/
6588+
clearUndo(): void;
65616589
}
65626590
interface LoroDoc<T extends Record<string, Container> = Record<string, Container>> {
65636591
/**

0 commit comments

Comments
 (0)