Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions crates/loro-internal/src/undo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,16 @@ impl UndoManager {
self.inner.lock().borrow_mut().redo_stack.clear();
}

/// Clear only the redo stack, preserving the undo stack.
pub fn clear_redo(&self) {
self.inner.lock().borrow_mut().redo_stack.clear();
}

/// Clear only the undo stack, preserving the redo stack.
pub fn clear_undo(&self) {
self.inner.lock().borrow_mut().undo_stack.clear();
}

pub fn set_top_undo_meta(&self, meta: UndoItemMeta) {
self.inner.lock().borrow_mut().undo_stack.set_top_meta(meta);
}
Expand Down
56 changes: 56 additions & 0 deletions crates/loro-internal/tests/undo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,59 @@ fn test_undo_group_start_with_remote_ops() {
undo_manager.undo().unwrap();
assert_eq!(doc.get_text("text").to_string(), "test");
}

#[test]
fn test_clear_redo() {
let doc = LoroDoc::new();
let undo_manager = UndoManager::new(&doc);
let text = doc.get_text("text");

// Make some edits
text.update("hello", UpdateOptions::default()).unwrap();
doc.commit_then_renew();
text.update("hello world", UpdateOptions::default()).unwrap();
doc.commit_then_renew();

// Undo to create redo stack
undo_manager.undo().unwrap();
assert_eq!(text.to_string(), "hello");
assert!(undo_manager.can_redo(), "should be able to redo");
assert!(undo_manager.can_undo(), "should be able to undo");

// Clear only redo stack
undo_manager.clear_redo();
assert!(!undo_manager.can_redo(), "redo stack should be empty");
assert!(undo_manager.can_undo(), "undo stack should still have items");

// Verify undo still works
undo_manager.undo().unwrap();
assert_eq!(text.to_string(), "");
}

#[test]
fn test_clear_undo() {
let doc = LoroDoc::new();
let undo_manager = UndoManager::new(&doc);
let text = doc.get_text("text");

// Make some edits
text.update("hello", UpdateOptions::default()).unwrap();
doc.commit_then_renew();
text.update("hello world", UpdateOptions::default()).unwrap();
doc.commit_then_renew();

// Undo to create redo stack
undo_manager.undo().unwrap();
assert_eq!(text.to_string(), "hello");
assert!(undo_manager.can_redo(), "should be able to redo");
assert!(undo_manager.can_undo(), "should be able to undo");

// Clear only undo stack
undo_manager.clear_undo();
assert!(undo_manager.can_redo(), "redo stack should still have items");
assert!(!undo_manager.can_undo(), "undo stack should be empty");

// Verify redo still works
undo_manager.redo().unwrap();
assert_eq!(text.to_string(), "hello world");
}
28 changes: 28 additions & 0 deletions crates/loro-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5332,6 +5332,20 @@ impl UndoManager {
pub fn clear(&self) {
self.undo.lock().clear();
}

/// Clear only the redo stack, preserving the undo 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.
pub fn clearRedo(&self) {
self.undo.lock().clear_redo();
}

/// Clear only the undo stack, preserving the redo stack.
pub fn clearUndo(&self) {
self.undo.lock().clear_undo();
}
}

/// Use this function to throw an error after the micro task.
Expand Down Expand Up @@ -6558,6 +6572,20 @@ interface UndoManager {
* Ends the current grouping of undo operations.
*/
groupEnd(): void;

/**
* Clear only the redo stack, preserving the undo 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.
*/
clearRedo(): void;

/**
* Clear only the undo stack, preserving the redo stack.
*/
clearUndo(): void;
}
interface LoroDoc<T extends Record<string, Container> = Record<string, Container>> {
/**
Expand Down