diff --git a/crates/engine/src/cmd/keywords.rs b/crates/engine/src/cmd/keywords.rs index f167c5122..00d8185e4 100644 --- a/crates/engine/src/cmd/keywords.rs +++ b/crates/engine/src/cmd/keywords.rs @@ -145,7 +145,7 @@ pub fn specs() -> Vec { "Save Keyword Set", [], None, - "{name, keywords?: [up to 9, \"\" = an empty slot] (default: the current nine), replace?: the set it renames} — replaces a set of that name (or `replace`, in its place) and makes it current", + "{name, keywords?: [up to 9, \"\" = an empty slot] (default: the renamed set's, else the current nine), replace?: the set it renames} — replaces a set of that name (or `replace`, in its place) and makes it current", always, |s, p| { let name = str_param(p, "name") @@ -153,17 +153,30 @@ pub fn specs() -> Vec { .filter(|n| !n.is_empty() && !n.eq_ignore_ascii_case(RECENT)) .ok_or_else(|| bad("keyword.saveSet", "missing or reserved `name`"))? .to_string(); - let keywords = if p.get("keywords").is_some() { slots(&strs(p, "keywords")) } else { current_keywords(s) }; - let set = KeywordSet { name: name.clone(), keywords }; let same_name = |x: &KeywordSet, n: &str| x.name.to_lowercase() == n.to_lowercase(); - match str_param(p, "replace").map(str::trim) { - // rename: in its place, onto a name no other set has - Some(old) => { - let at = s - .keyword_sets + // the set it renames, if any (an empty `replace` is none) + let renames = match str_param(p, "replace").map(str::trim).filter(|o| !o.is_empty()) { + Some(old) => Some( + s.keyword_sets .iter() .position(|x| same_name(x, old)) - .ok_or_else(|| bad("keyword.saveSet", format!("no keyword set `{old}`")))?; + .ok_or_else(|| bad("keyword.saveSet", format!("no keyword set `{old}`")))?, + ), + None => None, + }; + // keywords not given: the renamed set's own, else the current nine + let keywords = if p.get("keywords").is_some() { + slots(&strs(p, "keywords")) + } else { + match renames.and_then(|at| s.keyword_sets.get(at)) { + Some(x) => x.keywords.clone(), + None => current_keywords(s), + } + }; + let set = KeywordSet { name: name.clone(), keywords }; + match renames { + // rename: in its place, onto a name no other set has + Some(at) => { if s.keyword_sets.iter().enumerate().any(|(i, x)| i != at && same_name(x, &name)) { return Err(bad("keyword.saveSet", format!("there is a keyword set “{name}” already"))); } diff --git a/crates/engine/src/tests_keyword_list.rs b/crates/engine/src/tests_keyword_list.rs index cfff1f4f6..a14f44af0 100644 --- a/crates/engine/src/tests_keyword_list.rs +++ b/crates/engine/src/tests_keyword_list.rs @@ -229,3 +229,13 @@ fn renaming_a_keyword_set_keeps_its_place() { assert!(err.contains("already"), "{err}"); assert!(s.execute("keyword.saveSet", &json!({"name": "Rings", "replace": "Lisbon", "keywords": []})).is_err(), "no such set"); } + +/// Renaming a set without giving its keywords keeps its keywords (not the current set's). +#[test] +fn renaming_a_set_alone_keeps_its_keywords() { + let mut s = Session::with_demo(); + s.execute("keyword.saveSet", &json!({"name": "Weddings", "keywords": ["ceremony"]})).unwrap(); + s.execute("keyword.saveSet", &json!({"name": "Travel", "keywords": ["harbour"]})).unwrap(); + s.execute("keyword.saveSet", &json!({"name": "Ceremonies", "replace": "Weddings"})).unwrap(); + assert_eq!(set_named(&mut s, "Ceremonies")["keywords"], json!(["ceremony"])); +}