diff --git a/crates/engine/src/cmd/annotate.rs b/crates/engine/src/cmd/annotate.rs index 845a701..ba5b709 100644 --- a/crates/engine/src/cmd/annotate.rs +++ b/crates/engine/src/cmd/annotate.rs @@ -481,21 +481,35 @@ fn run_update(s: &mut Session, p: &Value) -> Result { /// DIMOVERRIDE: per-dimension style overrides (`clear` removes them); `text` sets the text. fn run_override(s: &mut Session, p: &Value) -> Result { - if p.get("text").is_some() && p.as_object().is_some_and(|o| o.keys().all(|k| matches!(k.as_str(), "text" | "handles" | "handle"))) { - let hs = targets(s, p)?; - let t = str_param(p, "text").unwrap_or("").to_string(); - let d = s.doc_mut()?; - for h in &hs { - let _ = d.modify_entity(*h, |e| { - if let EntityKind::Dimension(dm) = &mut e.kind { - dm.text = t.clone(); - dm.block = None; - } - }); - } - return Ok(json!({ "changed": hs.len() })); + if p.get("text").is_none() { + return super::props::dim_override(s, p); + } + let hs = targets(s, p)?; + let t = str_param(p, "text").unwrap_or("").to_string(); + // Dimension variables given next to `text` are applied first, so an unknown name still errors before anything changes. + let vars: serde_json::Map = p + .as_object() + .map(|o| o.iter().filter(|(k, _)| !matches!(k.as_str(), "text" | "handles" | "handle")).map(|(k, v)| (k.clone(), v.clone())).collect()) + .unwrap_or_default(); + let mut ignored = Value::Null; + if !vars.is_empty() { + let mut rest = vars; + rest.insert("handles".into(), json!(hs.iter().map(|h| h.hex()).collect::>())); + ignored = super::props::dim_override(s, &Value::Object(rest))?.get("ignored").cloned().unwrap_or(Value::Null); + } + let d = s.doc_mut()?; + for h in &hs { + let _ = d.modify_entity(*h, |e| { + if let EntityKind::Dimension(dm) = &mut e.kind { + dm.text = t.clone(); + dm.block = None; + } + }); + } + match ignored { + Value::Null => Ok(json!({ "changed": hs.len() })), + ig => Ok(json!({ "changed": hs.len(), "ignored": ig })), } - super::props::dim_override(s, p) } /// The two points a picked object is dimensioned between (DIMLINEAR "select object"). diff --git a/crates/engine/src/cmd/annotate_tests.rs b/crates/engine/src/cmd/annotate_tests.rs index 89a79f0..267510d 100644 --- a/crates/engine/src/cmd/annotate_tests.rs +++ b/crates/engine/src/cmd/annotate_tests.rs @@ -193,6 +193,23 @@ fn dimension_overrides() { assert_eq!(dim(&s, d).text, "<> TYP"); } +#[test] +fn dimension_override_text_with_variables() { + let mut s = Session::new(); + let d = h(&s.execute("dimlinear", &json!({ "p1": [0, 0], "p2": [10, 0], "at": [5, 2] })).unwrap()); + s.execute("dimoverride", &json!({ "handles": [d.hex()], "text": "<> OLD" })).unwrap(); + s.execute("dimoverride", &json!({ "handles": [d.hex()], "DIMTXT": 0.4 })).unwrap(); + let r = s.execute("dimoverride", &json!({ "handles": [d.hex()], "text": "<> TYP", "DIMDEC": 1 })).unwrap(); + assert_eq!(r["changed"], json!(1)); + let dm = dim(&s, d); + assert_eq!(dm.text, "<> TYP"); + assert_eq!(dm.overrides.get("decimals"), Some(&json!(1))); + assert!(dm.overrides.contains_key("textHeight")); + // An unknown variable next to valid text is still an error and changes nothing. + assert!(s.execute("dimoverride", &json!({ "handles": [d.hex()], "text": "X", "nope": 1 })).is_err()); + assert_eq!(dim(&s, d).text, "<> TYP"); +} + #[test] fn dimtedit_and_dimspace() { let mut s = Session::new();