Engine: Stop Action waits while a macro is being debugged

`automate.stopAction` rolls back the undo groups open on the workbook. While a macro was being
debugged it rolled back the debugger's own group and left the debugger set, pointing at a group
that was gone. It is now refused under the debugger's lock with "Finish or stop the macro first",
as edits are; `macro.debug {action: "stop"}` ends the run.

- Test: `stop_action_waits_while_a_macro_is_being_debugged`.
This commit is contained in:
prxnvt
2026-10-25 02:52:43 -04:00
parent ce2ab9a473
commit d309c85c31
3 changed files with 23 additions and 3 deletions
+4 -1
View File
@@ -615,7 +615,10 @@ impl Session {
// recording, so stopping again after the run keeps what was recorded.
let stops_record_actions =
id == "automate.recordActions" && (params.get("stop").and_then(Json::as_bool) == Some(true) || self.recording.is_some());
if (spec.undoable || stops_record_actions) && id != "macro.debug" && locked {
// Stop Action rolls back the open groups, the debugger's among them, and would leave the
// debugger pointing at a group that is gone: stopping the macro is the debugger's own.
let stops_groups = id == "automate.stopAction";
if (spec.undoable || stops_record_actions || stops_groups) && id != "macro.debug" && locked {
return Err(EngineError::Disabled(id.to_string(), macros::debug::BUSY.into()));
}
let before = self.active().map(|d| (d.uid, d.wb.clone(), d.selection.clone()));
+16
View File
@@ -295,6 +295,22 @@ fn shifting_makes_room_and_is_checked_again() {
assert!(Arc::ptr_eq(&s.doc().unwrap().wb, &start));
}
#[test]
fn stop_action_waits_while_a_macro_is_being_debugged() {
// Stop Action rolls back the open undo groups; the debugger's is one, so it waits for the
// debugger, which stops the macro itself.
let mut s = s();
data(&mut s);
put(&mut s, totals());
s.execute("macro.debug", json!({"action": "start", "name": "Totals", "inputs": "B2:C5", "breakpoints": ["1"]})).unwrap();
let r = s.execute("automate.stopAction", json!({}));
assert!(matches!(&r, Err(EngineError::Disabled(_, why)) if why == crate::macros::debug::BUSY), "{r:?}");
assert!(s.macro_debug.is_some(), "the debugger is still there");
s.execute("macro.debug", json!({"action": "stop"})).unwrap();
assert!(s.macro_debug.is_none());
s.execute("automate.stopAction", json!({})).unwrap();
}
#[test]
fn closing_the_debugged_workbook_ends_the_debugger() {
// `file.close` is not an edit, so the debugger's lock lets it through; the group went with
+3 -2
View File
@@ -150,8 +150,9 @@ on, as after any recalculation.
be given with any action; `breakpointsOnly: true` makes the list replace the steps' own flags. The
run stays one undo step; while it is open, edits of the workbooks it touched (its own, and any
other its steps changed) are refused with "Finish or stop the macro first", also when another
command makes them (stopping Record Actions saves a script), and other workbooks stay
editable. Each answer gives the next step with its parameters as they would run, the parameters
command makes them (stopping Record Actions saves a script), and so is Automate › Stop Action,
which would roll the run's undo group back under the debugger; `stop` ends the run instead. Other
workbooks stay editable. Each answer gives the next step with its parameters as they would run, the parameters
of the step that just ran, and the cells changed so far.
## Storage