From 405e8d82203ecd7e006d1a45c8c511917416272b Mon Sep 17 00:00:00 2001 From: Nathaniel Ledford Date: Fri, 9 Oct 2026 16:06:31 -0400 Subject: [PATCH] LRC-LIB-KEYWORDS: a new keyword set doesn't take another set's name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Saving Recent Keywords as a set under the name of an existing one (Edit Set… on Recent Keywords) replaced that set's keywords without a word, while renaming onto a taken name was refused. `keyword.saveSet` takes `new: true` for a set that must be new, and refuses a name another set has. Co-Authored-By: Claude Opus 5.5 --- crates/engine/src/cmd/keywords.rs | 6 +++++- crates/engine/src/tests_keyword_list.rs | 11 +++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/cmd/keywords.rs b/crates/engine/src/cmd/keywords.rs index 00d8185e4f..7329a7c401 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 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", + "{name, keywords?: [up to 9, \"\" = an empty slot] (default: the renamed set's, else the current nine), replace?: the set it renames, new?: refuse a name another set has} — replaces a set of that name (or `replace`, in its place) and makes it current", always, |s, p| { let name = str_param(p, "name") @@ -184,6 +184,10 @@ pub fn specs() -> Vec { *x = set; } } + // a new set (`new`) never takes another set's name + None if bool_or(p, "new", false) && s.keyword_sets.iter().any(|x| same_name(x, &name)) => { + return Err(bad("keyword.saveSet", format!("there is a keyword set “{name}” already"))); + } None => match s.keyword_sets.iter_mut().find(|x| same_name(x, &name)) { Some(x) => *x = set, None => s.keyword_sets.push(set), diff --git a/crates/engine/src/tests_keyword_list.rs b/crates/engine/src/tests_keyword_list.rs index a14f44af0e..1d22da34ad 100644 --- a/crates/engine/src/tests_keyword_list.rs +++ b/crates/engine/src/tests_keyword_list.rs @@ -239,3 +239,14 @@ fn renaming_a_set_alone_keeps_its_keywords() { s.execute("keyword.saveSet", &json!({"name": "Ceremonies", "replace": "Weddings"})).unwrap(); assert_eq!(set_named(&mut s, "Ceremonies")["keywords"], json!(["ceremony"])); } + +/// A new set (`new: true`, what Edit Set… on Recent Keywords saves) never takes another set's +/// name: saving over it silently is refused. +#[test] +fn a_new_set_doesnt_take_a_sets_name() { + let mut s = Session::with_demo(); + s.execute("keyword.saveSet", &json!({"name": "Travel", "keywords": ["harbour", "lisbon"]})).unwrap(); + let err = s.execute("keyword.saveSet", &json!({"name": "travel", "new": true, "keywords": ["x"]})).unwrap_err().to_string(); + assert!(err.contains("already"), "{err}"); + assert_eq!(set_named(&mut s, "Travel")["keywords"], json!(["harbour", "lisbon"]), "left as it was"); +}