Engine: a group rolled back inside a command keeps the redo history

A command that changed the workbook cleared the redo history even when an open group owned
its step. When the group was then rolled back inside the same command (the debugger's Stop
with Undo Changes), the rollback put the redo history back and the command's bookkeeping cleared
it again. Now only a command that makes a step of its own clears the redo history; a group
clears it when it ends as a step, and a rollback restores it.
This commit is contained in:
prxnvt
2026-10-22 02:00:54 -04:00
parent eb3eaa052d
commit 2287f63a95
2 changed files with 29 additions and 1 deletions
+3 -1
View File
@@ -469,8 +469,10 @@ impl Session {
d.undo.truncate(len);
}
d.undo.push(HistoryEntry { label, wb, selection: sel });
d.redo.clear();
}
d.redo.clear();
// Inside a group the redo history waits for the group: `group_end` clears it when the
// group becomes a step, and `group_rollback` puts it back as it was.
d.revision += 1;
}
// The history limit applies once the outermost command has collapsed its steps, so the
+26
View File
@@ -226,6 +226,13 @@ fn register_group_commands() {
});
// A command that is one group keeps the group's name.
register("test.onlyGroup", |s, _| s.grouped("Named Step", |s| put(s, "D1", "x")).map(|_| json!({})));
// An edit, then the rollback of the innermost group that was open before the command.
register("test.rollbackOpenGroup", |s, _| {
put(s, "E1", "gone")?;
let token = s.groups.last().map(|g| crate::GroupToken { id: g.id, doc: g.doc });
s.group_rollback(token.ok_or_else(|| EngineError::Other("no group".into()))?)?;
Ok(json!({}))
});
// An edit, then the end of the innermost group that was open before the command.
register("test.endOpenGroup", |s, _| {
put(s, "E1", "end")?;
@@ -278,6 +285,25 @@ fn a_stale_group_step_does_not_name_a_later_command() {
assert!(v(&s, "B2").is_empty() && v(&s, "D1").is_empty());
}
#[test]
fn a_group_rolled_back_inside_a_command_keeps_the_redo_history() {
// The debugger's Stop with Undo Changes rolls its group back inside a command. The rollback
// put the redo history back, and the command's own bookkeeping then cleared it again.
register_group_commands();
let mut s = s();
set(&mut s, "A1", "first");
s.execute("edit.undo", json!({})).unwrap();
assert_eq!(s.doc().unwrap().redo.len(), 1);
let token = s.group_begin("Debug").unwrap();
set(&mut s, "B1", "inside");
s.execute("test.rollbackOpenGroup", json!({})).unwrap();
assert!(!s.in_group(token.doc));
assert!(v(&s, "B1").is_empty() && v(&s, "E1").is_empty());
assert_eq!(s.doc().unwrap().redo.len(), 1, "the redo history survives the rolled-back group");
s.execute("edit.redo", json!({})).unwrap();
assert_eq!(v(&s, "A1").display(), "first");
}
/// Fills the undo history to the limit with edits of Z1 ("v0" to "v99"), and returns its snapshots.
fn fill_history(s: &mut Session) -> Vec<std::sync::Arc<gridcraft_model::Workbook>> {
for i in 0..crate::UNDO_LIMIT {