From 9c0d1c19fe963665aa9e36601897938488cbaa8f Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Sat, 24 Oct 2026 11:24:29 +0200 Subject: [PATCH] Restore the clipboard when a command fails and is rolled back (#1167) Fixes #1100 Co-authored-by: Claude Opus 5.5 --- crates/engine/src/lib.rs | 3 +++ crates/engine/src/version_preview_tests.rs | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index 29b084f2..a2ac9da8 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -966,6 +966,8 @@ impl Session { ds.end_preview(); } let before = self.ds().ok().map(|d| (d.doc.clone(), d.selection.clone(), d.page, d.uid)); + // Cut and copy write the clipboard before a later check can refuse the command. + let clipboard_before = self.clipboard.clone(); let result = guard::run(id, || (spec.run)(self, params)) .and_then(|v| self.check_instance_structure(id, &before).map(|()| v)) .and_then(|v| self.check_preview(id, &before).map(|()| v)) @@ -998,6 +1000,7 @@ impl Session { ds.selection = sel; ds.page = page; } + self.clipboard = clipboard_before; } } result diff --git a/crates/engine/src/version_preview_tests.rs b/crates/engine/src/version_preview_tests.rs index e910006c..9423c34e 100644 --- a/crates/engine/src/version_preview_tests.rs +++ b/crates/engine/src/version_preview_tests.rs @@ -97,3 +97,22 @@ fn preview_keeps_the_unsaved_state_and_saving_saves_the_document() { assert_eq!(count(&s), three); let _ = std::fs::remove_dir_all(&dir); } + +#[test] +fn a_refused_cut_leaves_the_clipboard_as_it_was() { + let mut s = Session::new(); + let a = rect(&mut s, 0.0); + s.execute("file.saveVersion", &json!({"name": "One"})).unwrap(); + let b = rect(&mut s, 20.0); + s.execute("edit.copy", &json!({"ids": [b]})).unwrap(); + let roots = |s: &Session| s.clipboard.nodes.iter().map(|(r, _)| *r).collect::>(); + assert_eq!(roots(&s), vec![b]); + let source = s.clipboard.source.clone().unwrap(); + let depth = s.undo_depth(); + s.execute("version.preview", &json!({"index": 0})).unwrap(); + let err = s.execute("edit.cut", &json!({"ids": [a]})).unwrap_err(); + assert!(matches!(err, EngineError::Disabled(..)), "{err:?}"); + assert_eq!(roots(&s), vec![b], "failed edit.cut changed the clipboard"); + assert!(std::sync::Arc::ptr_eq(s.clipboard.source.as_ref().unwrap(), &source)); + assert_eq!(s.undo_depth(), depth, "history untouched"); +}