diff --git a/crates/settings_ui/src/keybindings.rs b/crates/settings_ui/src/keybindings.rs index 81c461fed6..60e527677a 100644 --- a/crates/settings_ui/src/keybindings.rs +++ b/crates/settings_ui/src/keybindings.rs @@ -374,6 +374,14 @@ impl Focusable for KeymapEditor { } } } +/// Helper function to check if two keystroke sequences match exactly +fn keystrokes_match_exactly(keystrokes1: &[Keystroke], keystrokes2: &[Keystroke]) -> bool { + keystrokes1.len() == keystrokes2.len() + && keystrokes1 + .iter() + .zip(keystrokes2) + .all(|(k1, k2)| k1.key == k2.key && k1.modifiers == k2.modifiers) +} impl KeymapEditor { fn new(workspace: WeakEntity, window: &mut Window, cx: &mut Context) -> Self { @@ -549,13 +557,7 @@ impl KeymapEditor { .keystrokes() .is_some_and(|keystrokes| { if exact_match { - keystroke_query.len() == keystrokes.len() - && keystroke_query.iter().zip(keystrokes).all( - |(query, keystroke)| { - query.key == keystroke.key - && query.modifiers == keystroke.modifiers - }, - ) + keystrokes_match_exactly(&keystroke_query, keystrokes) } else if keystroke_query.len() > keystrokes.len() { return false; } else { @@ -2340,8 +2342,50 @@ impl KeybindingEditorModal { self.save_or_display_error(cx); } - fn cancel(&mut self, _: &menu::Cancel, _window: &mut Window, cx: &mut Context) { - cx.emit(DismissEvent) + fn cancel(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context) { + cx.emit(DismissEvent); + } + + fn get_matching_bindings_count(&self, cx: &Context) -> usize { + let current_keystrokes = self.keybind_editor.read(cx).keystrokes().to_vec(); + + if current_keystrokes.is_empty() { + return 0; + } + + self.keymap_editor + .read(cx) + .keybindings + .iter() + .enumerate() + .filter(|(idx, binding)| { + // Don't count the binding we're currently editing + if !self.creating && *idx == self.editing_keybind_idx { + return false; + } + + binding + .keystrokes() + .map(|keystrokes| keystrokes_match_exactly(keystrokes, ¤t_keystrokes)) + .unwrap_or(false) + }) + .count() + } + + fn show_matching_bindings(&mut self, _window: &mut Window, cx: &mut Context) { + let keystrokes = self.keybind_editor.read(cx).keystrokes().to_vec(); + + // Dismiss the modal + cx.emit(DismissEvent); + + // Update the keymap editor to show matching keystrokes + self.keymap_editor.update(cx, |editor, cx| { + editor.filter_state = FilterState::All; + editor.search_mode = SearchMode::KeyStroke { exact_match: true }; + editor.keystroke_editor.update(cx, |keystroke_editor, cx| { + keystroke_editor.set_keystrokes(keystrokes, cx); + }); + }); } } @@ -2356,6 +2400,7 @@ fn remove_key_char(Keystroke { modifiers, key, .. }: Keystroke) -> Keystroke { impl Render for KeybindingEditorModal { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { let theme = cx.theme().colors(); + let matching_bindings_count = self.get_matching_bindings_count(cx); v_flex() .w(rems(34.)) @@ -2427,19 +2472,37 @@ impl Render for KeybindingEditorModal { ), ) .footer( - ModalFooter::new().end_slot( - h_flex() - .gap_1() - .child( - Button::new("cancel", "Cancel") - .on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))), - ) - .child(Button::new("save-btn", "Save").on_click(cx.listener( - |this, _event, _window, cx| { - this.save_or_display_error(cx); - }, - ))), - ), + ModalFooter::new() + .start_slot( + div().when(matching_bindings_count > 0, |this| { + this.child( + Button::new("show_matching", format!( + "There {} {} {} with the same keystrokes. Click to view", + if matching_bindings_count == 1 { "is" } else { "are" }, + matching_bindings_count, + if matching_bindings_count == 1 { "binding" } else { "bindings" } + )) + .style(ButtonStyle::Transparent) + .color(Color::Accent) + .on_click(cx.listener(|this, _, window, cx| { + this.show_matching_bindings(window, cx); + })) + ) + }) + ) + .end_slot( + h_flex() + .gap_1() + .child( + Button::new("cancel", "Cancel") + .on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))), + ) + .child(Button::new("save-btn", "Save").on_click(cx.listener( + |this, _event, _window, cx| { + this.save_or_display_error(cx); + }, + ))), + ), ), ) }