From 80ec2dcc609c1630bea5e8ea2d22d85aa2822d6e Mon Sep 17 00:00:00 2001 From: Valentin Klein Date: Tue, 13 Oct 2026 10:37:42 +0200 Subject: [PATCH] Ctrl+Z / U during LINE, PLINE... removes the last segment instead of the whole command (#750) (#831) Co-authored-by: Claude Sonnet 5.5 --- crates/engine/src/lib.rs | 6 ++++++ crates/engine/src/tests.rs | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index 25a65974..40caf3b6 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -878,6 +878,12 @@ impl Session { } } let nested = transparent && spec.transparent && self.running.is_some() && spec.interactive.is_some(); + // Ctrl+Z / U / UNDO while a command's prompt offers its own Undo keyword (LINE, PLINE…) + // takes back the last segment and keeps the command running, as the keyword does. + if !nested && matches!(spec.id, "u" | "undo") && self.current_prompt().is_some_and(|p| p.keywords.iter().any(|k| k == "Undo")) { + self.echo(format!("Command: {}", spec.id.to_ascii_uppercase())); + return self.input(Input::Keyword("Undo".into())); + } if !nested && self.running.is_some() { self.cancel(); } diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index 97ce8be9..3b972e32 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -76,6 +76,43 @@ fn programmatic_undo_during_a_command_ends_it_first() { assert_eq!(kinds(&s), vec!["Line", "Text"]); } +#[test] +fn undo_during_line_removes_the_last_segment_and_keeps_line_running() { + let mut s = Session::new(); + s.cmdline("line 0,0 10,0 10,10 20,10").unwrap(); + assert_eq!(kinds(&s), vec!["Line", "Line", "Line"]); + // Ctrl+Z / the U command / the menu (Session::start) act as the Undo keyword. + s.start("u").unwrap(); + assert!(s.running.is_some()); + assert_eq!(kinds(&s), vec!["Line", "Line"]); + s.start("u").unwrap(); + assert!(s.running.is_some()); + assert_eq!(kinds(&s), vec!["Line"]); + // The command goes on from the last remaining point. + s.cmdline("10,5").unwrap(); + assert_eq!(kinds(&s), vec!["Line", "Line"]); + s.cmdline("").unwrap(); + assert!(s.running.is_none()); +} + +#[test] +fn undo_during_a_command_without_undo_keyword_ends_it_and_undoes() { + let mut s = Session::new(); + s.execute("line", &json!({"points": [[0, 0], [10, 0]]})).unwrap(); + // LINE at its first point offers no Undo: Ctrl+Z ends it, then undoes the earlier line. + s.cmdline("line").unwrap(); + assert!(s.running.is_some()); + s.start("u").unwrap(); + assert!(s.running.is_none()); + assert!(kinds(&s).is_empty()); + // TEXT offers no Undo keyword either. + s.execute("line", &json!({"points": [[0, 0], [10, 0]]})).unwrap(); + s.script("TEXT 0,0 5 0 HELLO\n").unwrap(); + s.start("u").unwrap(); + assert!(s.running.is_none()); + assert_eq!(kinds(&s), vec!["Line"]); +} + #[test] fn polar_and_direct_distance() { let mut s = Session::new();