diff --git a/crates/gpui/src/platform/keystroke.rs b/crates/gpui/src/platform/keystroke.rs index 69d87ebdcb..8b6e72d150 100644 --- a/crates/gpui/src/platform/keystroke.rs +++ b/crates/gpui/src/platform/keystroke.rs @@ -417,6 +417,17 @@ impl Modifiers { self.control || self.alt || self.shift || self.platform || self.function } + /// Returns the XOR of two modifier sets + pub fn xor(&self, other: &Modifiers) -> Modifiers { + Modifiers { + control: self.control ^ other.control, + alt: self.alt ^ other.alt, + shift: self.shift ^ other.shift, + platform: self.platform ^ other.platform, + function: self.function ^ other.function, + } + } + /// Whether the semantically 'secondary' modifier key is pressed. /// /// On macOS, this is the command key. diff --git a/crates/settings_ui/src/keybindings.rs b/crates/settings_ui/src/keybindings.rs index 281b01df27..1b97798328 100644 --- a/crates/settings_ui/src/keybindings.rs +++ b/crates/settings_ui/src/keybindings.rs @@ -470,11 +470,22 @@ impl KeymapEditor { }, ) } else { - keystroke_query.iter().all(|key| { - keystrokes.iter().any(|keystroke| { - keystroke.key == key.key - && keystroke.modifiers == key.modifiers - }) + let key_press_query = + KeyPressIterator::new(keystroke_query.as_slice()); + let mut last_match_idx = 0; + + key_press_query.into_iter().all(|key| { + let key_presses = KeyPressIterator::new(keystrokes); + key_presses.into_iter().enumerate().any( + |(index, keystroke)| { + if last_match_idx > index || keystroke != key { + return false; + } + + last_match_idx = index; + true + }, + ) }) } }) @@ -2313,6 +2324,16 @@ enum CloseKeystrokeResult { None, } +#[derive(PartialEq, Eq, Debug, Clone)] +enum KeyPress<'a> { + Alt, + Control, + Function, + Shift, + Platform, + Key(&'a String), +} + struct KeystrokeInput { keystrokes: Vec, placeholder_keystrokes: Option>, @@ -2322,6 +2343,7 @@ struct KeystrokeInput { intercept_subscription: Option, _focus_subscriptions: [Subscription; 2], search: bool, + /// Handles tripe escape to stop recording close_keystrokes: Option>, close_keystrokes_start: Option, } @@ -2443,11 +2465,14 @@ impl KeystrokeInput { && last.key.is_empty() && keystrokes_len <= Self::KEYSTROKE_COUNT_MAX { - if !event.modifiers.modified() { + if self.search { + last.modifiers = last.modifiers.xor(&event.modifiers); + } else if !event.modifiers.modified() { self.keystrokes.pop(); } else { last.modifiers = event.modifiers; } + self.keystrokes_changed(cx); } else if keystrokes_len < Self::KEYSTROKE_COUNT_MAX { self.keystrokes.push(Self::dummy(event.modifiers)); @@ -2464,11 +2489,19 @@ impl KeystrokeInput { ) { let close_keystroke_result = self.handle_possible_close_keystroke(keystroke, window, cx); if close_keystroke_result != CloseKeystrokeResult::Close { - if let Some(last) = self.keystrokes.last() + let key_len = self.keystrokes.len(); + if let Some(last) = self.keystrokes.last_mut() && last.key.is_empty() - && self.keystrokes.len() <= Self::KEYSTROKE_COUNT_MAX + && key_len <= Self::KEYSTROKE_COUNT_MAX { - self.keystrokes.pop(); + if self.search { + last.key = keystroke.key.clone(); + self.keystrokes_changed(cx); + cx.stop_propagation(); + return; + } else { + self.keystrokes.pop(); + } } if self.keystrokes.len() < Self::KEYSTROKE_COUNT_MAX { if close_keystroke_result == CloseKeystrokeResult::Partial @@ -2511,10 +2544,11 @@ impl KeystrokeInput { { return placeholders; } - if self - .keystrokes - .last() - .map_or(false, |last| last.key.is_empty()) + if !self.search + && self + .keystrokes + .last() + .map_or(false, |last| last.key.is_empty()) { return &self.keystrokes[..self.keystrokes.len() - 1]; } @@ -2957,3 +2991,72 @@ mod persistence { } } } + +/// Iterator that yields KeyPress values from a slice of Keystrokes +struct KeyPressIterator<'a> { + keystrokes: &'a [Keystroke], + current_keystroke_index: usize, + current_key_press_index: usize, +} + +impl<'a> KeyPressIterator<'a> { + fn new(keystrokes: &'a [Keystroke]) -> Self { + Self { + keystrokes, + current_keystroke_index: 0, + current_key_press_index: 0, + } + } +} + +impl<'a> Iterator for KeyPressIterator<'a> { + type Item = KeyPress<'a>; + + fn next(&mut self) -> Option { + loop { + let keystroke = self.keystrokes.get(self.current_keystroke_index)?; + + match self.current_key_press_index { + 0 => { + self.current_key_press_index = 1; + if keystroke.modifiers.platform { + return Some(KeyPress::Platform); + } + } + 1 => { + self.current_key_press_index = 2; + if keystroke.modifiers.alt { + return Some(KeyPress::Alt); + } + } + 2 => { + self.current_key_press_index = 3; + if keystroke.modifiers.control { + return Some(KeyPress::Control); + } + } + 3 => { + self.current_key_press_index = 4; + if keystroke.modifiers.shift { + return Some(KeyPress::Shift); + } + } + 4 => { + self.current_key_press_index = 5; + if keystroke.modifiers.function { + return Some(KeyPress::Function); + } + } + _ => { + self.current_keystroke_index += 1; + self.current_key_press_index = 0; + + if keystroke.key.is_empty() { + continue; + } + return Some(KeyPress::Key(&keystroke.key)); + } + } + } + } +}