From be99983b72be3958e25cefe28dc4a5a0b22b7e22 Mon Sep 17 00:00:00 2001 From: Helen Kwok <107752876+helenkwok@users.noreply.github.com> Date: Sat, 19 Sep 2026 18:01:14 +0930 Subject: [PATCH] Control: a point step that takes keywords accepts points active_command_metadata listed one input kind per step. A point step that also takes keyword letters (point_step_accepts_keywords) reports SingleToken, so MOVE and COPY's base point with [Displacement] were described as accepting only token and enter, and the input example was the D keyword, though the step takes a point. List point as well; the keyword letters are still listed as token from the options. --- src/app/control/mod.rs | 45 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/src/app/control/mod.rs b/src/app/control/mod.rs index b96183f30..e24167479 100644 --- a/src/app/control/mod.rs +++ b/src/app/control/mod.rs @@ -326,11 +326,18 @@ fn active_command_metadata(command: &dyn crate::command::CadCommand) -> Value { accepts.push("entity"); } if accepts.is_empty() && !command.needs_tangent_pick() { - accepts.push(match command.input_kind() { + let kind = match command.input_kind() { InputKind::Point => "point", InputKind::SingleToken => "token", InputKind::FreeText => "text", - }); + }; + // A point step that also takes keyword letters (MOVE/COPY's base + // point with [Displacement]) reports SingleToken so the letters reach + // the command line, but it still takes a point. + if command.point_step_accepts_keywords() && kind != "point" { + accepts.push("point"); + } + accepts.push(kind); } if options.iter().any(|option| !option.keyword.is_empty()) && !accepts.contains(&"token") { accepts.push("token"); @@ -1490,6 +1497,40 @@ mod tests { assert!(app.opening.is_none()); } + #[test] + fn point_steps_that_take_keywords_accept_points() { + let mut app = OpenCADStudio::new_for_test(); + request(&mut app, json!({"op":"new"})); + request(&mut app, json!({"op":"run","cmd":"CIRCLE 0,0 3"})); + request(&mut app, json!({"op":"select","type":"CIRCLE"})); + let started = request(&mut app, json!({"op":"start","cmd":"MOVE"})); + assert_eq!(started["status"], "waiting_input", "{started}"); + let command = &started["state"]["command"]; + let accepts = command["accepts"].as_array().unwrap(); + assert!(accepts.contains(&json!("point")), "{command}"); + assert!(accepts.contains(&json!("token")), "{command}"); + assert_eq!(command["input_example"]["kind"], "point"); + let base = request( + &mut app, + json!({"op":"input","kind":"point","point":[0.,0.,0.]}), + ); + assert_eq!(base["status"], "waiting_input", "{base}"); + let moved = request( + &mut app, + json!({"op":"input","kind":"point","point":[0.,-10.,0.]}), + ); + assert_eq!(moved["status"], "completed", "{moved}"); + let circle = app.tabs[app.active_tab] + .scene + .document + .entities() + .find_map(|e| match e { + acadrust::EntityType::Circle(c) => Some(c.center.y), + _ => None, + }); + assert_eq!(circle, Some(-10.0)); + } + #[test] fn embed_image_op_places_an_ole2frame_and_undoes_it() { let mut app = OpenCADStudio::new_for_test();