From 75efe07d855388e450895e1928c11eb04ffa1c5e Mon Sep 17 00:00:00 2001 From: Elarionus <29511515+Elarionus@users.noreply.github.com> Date: Fri, 30 Oct 2026 19:16:59 -0700 Subject: [PATCH] Enter on a partly typed command runs the highlighted suggestion (#2135) Co-authored-by: Ethan Dale --- crates/ui-egui/src/cmdline.rs | 39 ++++++++++++++++++++++++++++++++++- crates/ui-egui/src/lib.rs | 13 +++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/src/cmdline.rs b/crates/ui-egui/src/cmdline.rs index 9d8bd9821..40397df99 100644 --- a/crates/ui-egui/src/cmdline.rs +++ b/crates/ui-egui/src/cmdline.rs @@ -443,7 +443,9 @@ pub fn submit(app: &mut CadApp) { } return; } - app.cmdline(&text); + // A partly typed command name runs the highlighted (first) AutoComplete suggestion. + let completion = if app.session.prompting() { None } else { suggestions(text.trim()).into_iter().next() }; + app.cmdline_or(&text, completion.map(|s| s.id.to_ascii_lowercase()).as_deref()); } fn visible_lines(app: &CadApp, canvas: Rect, bar: Rect) -> usize { @@ -924,4 +926,39 @@ mod autocomplete_tests { tab(&mut app); assert_eq!(app.cmd.buffer, "RECTANG"); } + + fn app() -> CadApp { + let mut app = CadApp::new(cadcraft_engine::Session::empty(), crate::Services::default()); + app.start("ui.sample"); + app + } + + #[test] + fn enter_on_a_partial_name_runs_the_highlighted_suggestion() { + let mut app = app(); + app.cmd.buffer = "RECT".to_string(); + submit(&mut app); + assert_eq!(app.session.running.as_ref().map(|r| r.id.as_str()), Some("rectang")); + assert!(!app.session.log.iter().any(|l| l.contains("unknown command")), "{:?}", app.session.log); + } + + #[test] + fn enter_keeps_exact_names_and_reports_names_nothing_matches() { + let mut app = app(); + // An exact alias is not replaced by a longer suggestion. + app.cmd.buffer = "l".to_string(); + submit(&mut app); + assert_eq!(app.session.running.as_ref().map(|r| r.id.as_str()), Some("line")); + app.session.cancel(); + // A system variable name still acts as SETVAR. + app.cmd.buffer = "orthomode".to_string(); + submit(&mut app); + assert_eq!(app.session.running.as_ref().map(|r| r.id.as_str()), Some("setvar")); + app.session.cancel(); + // No suggestion: still an unknown command. + app.cmd.buffer = "zzqx".to_string(); + submit(&mut app); + assert!(app.session.running.is_none()); + assert!(app.session.log.iter().any(|l| l.contains("unknown command")), "{:?}", app.session.log); + } } diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 0253a2cec..97ab2faf4 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -422,6 +422,13 @@ impl CadApp { /// Submit a line of command-line text. A refused line is echoed to the history; the error is /// also returned. pub fn cmdline(&mut self, text: &str) -> Option { + self.cmdline_or(text, None) + } + + /// [`CadApp::cmdline`], but a command name nothing knows (no command, alias, UI command, LISP + /// command or system variable) runs `fallback` instead: Enter on a partly typed name accepts + /// the highlighted AutoComplete suggestion. + pub fn cmdline_or(&mut self, text: &str, fallback: Option<&str>) -> Option { if !self.session.prompting() { let first = text.split_whitespace().next().unwrap_or(""); // `cmd {json}` is a programmatic call: the engine runs it with those parameters. @@ -430,7 +437,11 @@ impl CadApp { return None; } } - let e = self.session.cmdline(text).err()?.to_string(); + let e = match (self.session.cmdline(text), fallback) { + (Ok(()), _) => return None, + (Err(cadcraft_engine::EngineError::UnknownCommand(_)), Some(f)) => return self.cmdline(f), + (Err(e), _) => e.to_string(), + }; self.session.echo(e.clone()); Some(e) }