diff --git a/crates/keymap/src/scope.rs b/crates/keymap/src/scope.rs index 287413c9..26b94e49 100644 --- a/crates/keymap/src/scope.rs +++ b/crates/keymap/src/scope.rs @@ -9,17 +9,21 @@ pub const MAX_SCOPE_NAME: usize = 64; const APP: &str = "Application"; -/// The name of a scope. Never empty, at most [`MAX_SCOPE_NAME`] bytes, without `:`. +/// The name of a scope. Never empty, at most [`MAX_SCOPE_NAME`] bytes, without `:` or outer +/// whitespace. #[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)] #[serde(transparent)] pub struct Scope(String); impl Scope { - /// `None` for an empty name, one over [`MAX_SCOPE_NAME`] bytes, or one with `:` or a control - /// character. + /// `None` for an empty name, one over [`MAX_SCOPE_NAME`] bytes, one with `:` or a control + /// character, or one with whitespace at either end, which binding text would lose. pub fn new(name: impl Into) -> Option { let name = name.into(); - let ok = !name.is_empty() && name.len() <= MAX_SCOPE_NAME && !name.chars().any(|c| c == ':' || c.is_control()); + let ok = !name.is_empty() + && name.len() <= MAX_SCOPE_NAME + && name.trim() == name + && !name.chars().any(|c| c == ':' || c.is_control()); ok.then_some(Scope(name)) } diff --git a/crates/keymap/tests/keymap.rs b/crates/keymap/tests/keymap.rs index 3a8a2483..74ba8d6e 100644 --- a/crates/keymap/tests/keymap.rs +++ b/crates/keymap/tests/keymap.rs @@ -375,6 +375,15 @@ fn binding_text_round_trips_for_every_key() { assert_eq!(Binding::parse(": :"), Ok(Binding::new(Trigger::double(Chord::plain(Key::Colon))))); } +#[test] +fn scope_names_with_outer_whitespace_are_refused() { + for name in [" Timeline", "Timeline ", "\u{a0}Timeline", ""] { + assert_eq!(Scope::new(name), None, "{name:?}"); + } + assert_eq!(Scope::new("Time line").map(|s| s.to_string()).as_deref(), Some("Time line")); + assert_eq!(Binding::parse(" Timeline : K").map(|b| b.scope), Ok(Some(scope("Timeline")))); +} + #[test] fn edits_are_refused_when_the_overrides_are_full() { let catalog = vec![app("edit.copy", &["Cmd+C"]), app("edit.duplicate", &["Cmd+D"]), app("edit.cut", &[])];