diff --git a/crates/engine/src/cmd/edit.rs b/crates/engine/src/cmd/edit.rs index 08f765c83..41276c232 100644 --- a/crates/engine/src/cmd/edit.rs +++ b/crates/engine/src/cmd/edit.rs @@ -713,7 +713,7 @@ fn cut(s: &mut Session, p: &Value) -> Result { let ids = selected_roots(s)?; copy(s, p)?; // The selected ruler guides it copied go with the objects, in the same undo step (#3226). - if s.doc()?.selection.anchors.is_empty() { clear(s, &json!({ "ids": ids, "guides": true })) } else { clear(s, &json!({})) } + if s.doc()?.selection.anchors.is_empty() { clear(s, &json!({ "ids": ids, "guides": true })) } else { clear(s, &json!({ "guides": true })) } } fn clear(s: &mut Session, p: &Value) -> Result { diff --git a/crates/engine/src/cmd/path.rs b/crates/engine/src/cmd/path.rs index f1bc3e501..2def5ce9e 100644 --- a/crates/engine/src/cmd/path.rs +++ b/crates/engine/src/cmd/path.rs @@ -1189,7 +1189,9 @@ fn convert_anchors(s: &mut Session, p: &Value) -> Result { ok() } -pub(crate) fn delete_anchors(s: &mut Session, _: &Value) -> Result { +pub(crate) fn delete_anchors(s: &mut Session, p: &Value) -> Result { + // `guides: true` (Cut, #3226): the selected ruler guides go in the same undo step. + let with_guides = p.get("guides").and_then(Value::as_bool).unwrap_or(false); let st = s.doc()?; let sel = st.selection.anchors.clone(); let segments = st.selection.segments.clone(); @@ -1197,6 +1199,10 @@ pub(crate) fn delete_anchors(s: &mut Session, _: &Value) -> Result { // also catches part of another path must still delete those whole objects. let whole = st.selection.whole(); s.edit("Clear", |d, selection| { + if with_guides { + let gone = selection.guides.clone(); + d.retain_guides(selection, |i, _| !gone.contains(&i)); + } let targets: BTreeSet = whole.into_iter().filter(|id| d.node(*id).is_some_and(|n| !n.is_layer())).collect(); let whole_ids: Vec = targets .iter() diff --git a/crates/engine/src/tests/guide_clipboard.rs b/crates/engine/src/tests/guide_clipboard.rs index ca1f9fd79..d71c1dd42 100644 --- a/crates/engine/src/tests/guide_clipboard.rs +++ b/crates/engine/src/tests/guide_clipboard.rs @@ -74,6 +74,16 @@ fn guides_paste_in_place_onto_another_artboard_and_cut_removes_them() { assert!(s.doc().unwrap().doc.node(NodeId(rect)).is_none()); assert_eq!(guides(&s).len(), before - 1, "the selected guide goes too"); assert_eq!(s.doc().unwrap().history.undo.len(), undo + 1); + // Cut with direct-selected anchors and a guide: the anchors and the guide go, one undo step. + let path = run(&mut s, "shape.rectangle", json!({"x": 40, "y": 40, "width": 20, "height": 20}))["id"].as_u64().unwrap(); + run(&mut s, "guide.add", json!({"vertical": false, "pos": 90})); + let n = guides(&s).len(); + run(&mut s, "select.anchors", json!({"id": path, "anchors": [[0, 0]], "mode": "set"})); + run(&mut s, "guide.select", json!({"indexes": [n - 1], "toggle": true})); + let undo = s.doc().unwrap().history.undo.len(); + run(&mut s, "edit.cut", json!({})); + assert_eq!(guides(&s).len(), n - 1, "the guide goes with the anchors"); + assert_eq!(s.doc().unwrap().history.undo.len(), undo + 1); // Nothing selected: Copy is off. run(&mut s, "select.none", json!({})); assert!(s.execute("edit.copy", &json!({})).is_err());