mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-02 06:07:06 -06:00
Engine guard: a recovered panic restores every document's undo history and the active document (#3599)
run_guarded put back only the active document's art, selection and interaction. An undo step pushed before the panic stayed (Undo then stepped to a state equal to the current one) and Redo was lost; other documents a command touched kept their half-applied edits; the active document was restored by index. It now snapshots every open document with its history and restores them, and the active document by uid. Fixes #3599
This commit is contained in:
@@ -199,27 +199,30 @@ impl Session {
|
||||
r
|
||||
}
|
||||
|
||||
/// A top-level command: a panic (a bug) becomes [`EngineError::Internal`] and the active
|
||||
/// document goes back to how it was before the command, instead of crashing the frontend.
|
||||
/// A top-level command: a panic (a bug) becomes [`EngineError::Internal`] and every open
|
||||
/// document goes back to how it was before the command (its art, selection, interaction and
|
||||
/// undo history), and the same document stays active, instead of crashing the frontend.
|
||||
pub(crate) fn run_guarded(&mut self, id: &str, f: impl FnOnce(&mut Self) -> Result<Value>) -> Result<Value> {
|
||||
let snapshot = self.active().map(|d| (d.uid, d.doc.clone(), d.selection.clone(), d.interaction.clone()));
|
||||
let active = self.active;
|
||||
let snapshot: Vec<_> =
|
||||
self.docs.iter().map(|d| (d.uid, d.doc.clone(), d.selection.clone(), d.interaction.clone(), d.history.clone())).collect();
|
||||
let active = self.active().map(|d| d.uid);
|
||||
let r = match guard::catch_panic(|| self.run_nested(f)) {
|
||||
Ok(r) => r,
|
||||
Err(msg) => {
|
||||
self.depth = 0;
|
||||
self.journal_note.clear();
|
||||
if let Some((uid, doc, selection, interaction)) = snapshot
|
||||
&& let Some(st) = self.docs.iter_mut().find(|d| d.uid == uid)
|
||||
{
|
||||
for (uid, doc, selection, interaction, history) in snapshot {
|
||||
// A document the command closed stays closed.
|
||||
let Some(st) = self.docs.iter_mut().find(|d| d.uid == uid) else { continue };
|
||||
st.doc = doc;
|
||||
st.selection = selection;
|
||||
st.interaction = interaction;
|
||||
st.history = history;
|
||||
cmd::globaledit::follow_history(st);
|
||||
st.revision += 1;
|
||||
}
|
||||
if active.is_some_and(|i| i < self.docs.len()) {
|
||||
self.active = active;
|
||||
if let Some(i) = active.and_then(|uid| self.docs.iter().position(|d| d.uid == uid)) {
|
||||
self.active = Some(i);
|
||||
}
|
||||
Err(EngineError::Internal { cmd: id.to_string(), msg })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user