diff options
| author | JJ Liebig <jonathan.liebig@gmail.com> | 2026-09-28 11:58:09 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-09-28 11:58:09 +0200 |
| commit | 7f89b11a11160874ba89f3be7c64b29960bd2df3 (patch) | |
| tree | 0e875b3fbbe5a701cfb0182bda0ce0994267584c /src/config/model.rs | |
| parent | 0d5d6f1f317e238c8297076bc6ab5c3a0cd56283 (diff) | |
feat: support multiple prefix keys (#4653)
* feat: support multiple prefix keys
keys.prefix accepts an array in addition to a single string so one
config can define several prefix keys. Every configured prefix enters
the same prefix mode, and the help panel lists them all.
Published keybinding profiles keep the primary prefix as a scalar and
carry the rest in an optional extra_prefixes field so older clients
keep working with the primary prefix.
* fix: reject an empty prefix list on reload
An explicitly empty keys.prefix array now stays empty through config
parsing so validation rejects it and a reload keeps the current
keybindings instead of silently falling back to ctrl+b.
Diffstat (limited to 'src/config/model.rs')
| -rw-r--r-- | src/config/model.rs | 43 |
1 files changed, 36 insertions, 7 deletions
diff --git a/src/config/model.rs b/src/config/model.rs index ce59eb2e..0e2e57ce 100644 --- a/src/config/model.rs +++ b/src/config/model.rs @@ -334,8 +334,9 @@ pub struct LoadedConfig { #[derive(Debug, Clone, Serialize)] pub struct KeysConfig { - /// Prefix key to enter prefix mode (e.g. "ctrl+b", "f12", "esc"). - pub prefix: String, + /// Prefix key(s) to enter prefix mode (e.g. "ctrl+b", "f12", "esc", or an + /// array to accept several). + pub prefix: BindingConfig, /// Open keybinding help. Default: "prefix+?" pub help: BindingConfig, /// Open settings. Default: "prefix+s" @@ -467,7 +468,12 @@ pub struct KeysConfig { #[serde(default)] pub(crate) struct KeysConfigOverlay { #[serde(skip_serializing_if = "Option::is_none")] - prefix: Option<String>, + prefix: Option<BindingConfig>, + /// Additional prefix keys published for cross-version compatibility. + /// Older clients parse `prefix` as a single string and ignore this field; + /// new clients merge it into the effective prefix list. + #[serde(skip_serializing_if = "Option::is_none")] + extra_prefixes: Option<BindingConfig>, #[serde(skip_serializing_if = "Option::is_none")] help: Option<BindingConfig>, #[serde(skip_serializing_if = "Option::is_none")] @@ -592,8 +598,13 @@ pub(crate) struct KeysConfigOverlay { } impl KeysConfigOverlay { - pub(crate) fn set_prefix(&mut self, prefix: String) { - self.prefix = Some(prefix); + pub(crate) fn set_prefixes(&mut self, prefixes: &[super::keybinds::KeyCombo]) { + let mut labels = prefixes + .iter() + .map(|combo| super::keybinds::format_key_combo(*combo)); + self.prefix = Some(BindingConfig::One(labels.next().unwrap_or_default())); + let extra: Vec<String> = labels.collect(); + self.extra_prefixes = (!extra.is_empty()).then_some(BindingConfig::Many(extra)); } } @@ -605,6 +616,25 @@ impl<'de> Deserialize<'de> for KeysConfig { let input = KeysConfigOverlay::deserialize(deserializer)?; let mut keys = KeysConfig::default(); + let prefix_was_supplied = input.prefix.is_some() || input.extra_prefixes.is_some(); + let mut prefix_values = Vec::new(); + if let Some(prefix) = input.prefix { + prefix_values.extend(prefix.into_values()); + } + if let Some(extra) = input.extra_prefixes { + prefix_values.extend(extra.into_values()); + } + if prefix_was_supplied { + // An explicitly empty list stays empty so prefix validation rejects + // it and a reload keeps the current keybindings. + keys.prefix = match prefix_values.len() { + 0 => BindingConfig::Many(Vec::new()), + 1 => BindingConfig::One(prefix_values.remove(0)), + _ => BindingConfig::Many(prefix_values), + }; + keys.user_fields.insert("prefix"); + } + macro_rules! apply_field { ($field:ident) => { if let Some(value) = input.$field { @@ -614,7 +644,6 @@ impl<'de> Deserialize<'de> for KeysConfig { }; } - apply_field!(prefix); apply_field!(help); apply_field!(settings); apply_field!(new_workspace); @@ -1088,7 +1117,7 @@ pub struct ExperimentalConfig { impl Default for KeysConfig { fn default() -> Self { Self { - prefix: "ctrl+b".into(), + prefix: BindingConfig::one("ctrl+b"), help: BindingConfig::one("prefix+?"), settings: BindingConfig::one("prefix+s"), new_workspace: BindingConfig::one("prefix+shift+n"), |
