gpui: Simplify bindings_for_action API (#34857)

Closes #ISSUE

Simplifies the API to no longer have a variant that returns indices. The
downside is that a few places that used to call
`bindings_for_action_with_indices` now compare `Box<dyn Action>` instead
of indices, however the result is the removal of wrapper code and index
handling that is largely unnecessary

Release Notes:

- N/A *or* Added/Fixed/Improved ...

Co-authored-by: Conrad <conrad@zed.dev>
This commit is contained in:
Ben Kunkle 2025-07-22 09:59:51 -05:00 committed by GitHub
parent 2eeab5b0bf
commit 1a76a6b0bf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 31 additions and 61 deletions

View file

@ -50,8 +50,8 @@
/// KeyBinding::new("cmd-k left", pane::SplitLeft, Some("Pane")) /// KeyBinding::new("cmd-k left", pane::SplitLeft, Some("Pane"))
/// ///
use crate::{ use crate::{
Action, ActionRegistry, App, BindingIndex, DispatchPhase, EntityId, FocusId, KeyBinding, Action, ActionRegistry, App, DispatchPhase, EntityId, FocusId, KeyBinding, KeyContext, Keymap,
KeyContext, Keymap, Keystroke, ModifiersChangedEvent, Window, Keystroke, ModifiersChangedEvent, Window,
}; };
use collections::FxHashMap; use collections::FxHashMap;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -406,16 +406,11 @@ impl DispatchTree {
// methods, but this can't be done very cleanly since keymap must be borrowed. // methods, but this can't be done very cleanly since keymap must be borrowed.
let keymap = self.keymap.borrow(); let keymap = self.keymap.borrow();
keymap keymap
.bindings_for_action_with_indices(action) .bindings_for_action(action)
.filter(|(binding_index, binding)| { .filter(|binding| {
Self::binding_matches_predicate_and_not_shadowed( Self::binding_matches_predicate_and_not_shadowed(&keymap, &binding, context_stack)
&keymap,
*binding_index,
&binding.keystrokes,
context_stack,
)
}) })
.map(|(_, binding)| binding.clone()) .cloned()
.collect() .collect()
} }
@ -428,28 +423,22 @@ impl DispatchTree {
) -> Option<KeyBinding> { ) -> Option<KeyBinding> {
let keymap = self.keymap.borrow(); let keymap = self.keymap.borrow();
keymap keymap
.bindings_for_action_with_indices(action) .bindings_for_action(action)
.rev() .rev()
.find_map(|(binding_index, binding)| { .find(|binding| {
let found = Self::binding_matches_predicate_and_not_shadowed( Self::binding_matches_predicate_and_not_shadowed(&keymap, &binding, context_stack)
&keymap,
binding_index,
&binding.keystrokes,
context_stack,
);
if found { Some(binding.clone()) } else { None }
}) })
.cloned()
} }
fn binding_matches_predicate_and_not_shadowed( fn binding_matches_predicate_and_not_shadowed(
keymap: &Keymap, keymap: &Keymap,
binding_index: BindingIndex, binding: &KeyBinding,
keystrokes: &[Keystroke],
context_stack: &[KeyContext], context_stack: &[KeyContext],
) -> bool { ) -> bool {
let (bindings, _) = keymap.bindings_for_input_with_indices(&keystrokes, context_stack); let (bindings, _) = keymap.bindings_for_input(&binding.keystrokes, context_stack);
if let Some((highest_precedence_index, _)) = bindings.iter().next() { if let Some(found) = bindings.iter().next() {
binding_index == *highest_precedence_index found.action.partial_eq(binding.action.as_ref())
} else { } else {
false false
} }

View file

@ -77,15 +77,6 @@ impl Keymap {
&'a self, &'a self,
action: &'a dyn Action, action: &'a dyn Action,
) -> impl 'a + DoubleEndedIterator<Item = &'a KeyBinding> { ) -> impl 'a + DoubleEndedIterator<Item = &'a KeyBinding> {
self.bindings_for_action_with_indices(action)
.map(|(_, binding)| binding)
}
/// Like `bindings_for_action_with_indices`, but also returns the binding indices.
pub fn bindings_for_action_with_indices<'a>(
&'a self,
action: &'a dyn Action,
) -> impl 'a + DoubleEndedIterator<Item = (BindingIndex, &'a KeyBinding)> {
let action_id = action.type_id(); let action_id = action.type_id();
let binding_indices = self let binding_indices = self
.binding_indices_by_action_id .binding_indices_by_action_id
@ -118,7 +109,7 @@ impl Keymap {
} }
} }
Some((BindingIndex(*ix), binding)) Some(binding)
}) })
} }
@ -153,22 +144,8 @@ impl Keymap {
input: &[Keystroke], input: &[Keystroke],
context_stack: &[KeyContext], context_stack: &[KeyContext],
) -> (SmallVec<[KeyBinding; 1]>, bool) { ) -> (SmallVec<[KeyBinding; 1]>, bool) {
let (bindings, pending) = self.bindings_for_input_with_indices(input, context_stack); let mut matched_bindings = SmallVec::<[(usize, BindingIndex, &KeyBinding); 1]>::new();
let bindings = bindings let mut pending_bindings = SmallVec::<[(BindingIndex, &KeyBinding); 1]>::new();
.into_iter()
.map(|(_, binding)| binding)
.collect::<SmallVec<[KeyBinding; 1]>>();
(bindings, pending)
}
/// Like `bindings_for_input`, but also returns the binding indices.
pub fn bindings_for_input_with_indices(
&self,
input: &[Keystroke],
context_stack: &[KeyContext],
) -> (SmallVec<[(BindingIndex, KeyBinding); 1]>, bool) {
let mut bindings: SmallVec<[(usize, BindingIndex, &KeyBinding); 1]> = SmallVec::new();
let mut pending_bindings: SmallVec<[(BindingIndex, &KeyBinding); 1]> = SmallVec::new();
for (ix, binding) in self.bindings().enumerate().rev() { for (ix, binding) in self.bindings().enumerate().rev() {
let Some(depth) = self.binding_enabled(binding, &context_stack) else { let Some(depth) = self.binding_enabled(binding, &context_stack) else {
@ -179,26 +156,30 @@ impl Keymap {
}; };
if !pending { if !pending {
bindings.push((depth, BindingIndex(ix), binding)) matched_bindings.push((depth, BindingIndex(ix), binding));
} else { } else {
pending_bindings.push((BindingIndex(ix), binding)) pending_bindings.push((BindingIndex(ix), binding));
} }
} }
bindings.sort_by(|(depth_a, ix_a, _), (depth_b, ix_b, _)| { matched_bindings.sort_by(|(depth_a, ix_a, _), (depth_b, ix_b, _)| {
depth_b.cmp(depth_a).then(ix_b.cmp(ix_a)) depth_b.cmp(depth_a).then(ix_b.cmp(ix_a))
}); });
let bindings: SmallVec<[_; 1]> = bindings let mut bindings: SmallVec<[_; 1]> = SmallVec::new();
.into_iter() let mut first_binding_index = None;
.take_while(|(_, _, binding)| !is_no_action(&*binding.action)) for (_, ix, binding) in matched_bindings {
.map(|(_, ix, binding)| (ix, binding.clone())) if is_no_action(&*binding.action) {
.collect(); break;
}
bindings.push(binding.clone());
first_binding_index.get_or_insert(ix);
}
let mut pending = HashSet::default(); let mut pending = HashSet::default();
for (ix, binding) in pending_bindings.into_iter().rev() { for (ix, binding) in pending_bindings.into_iter().rev() {
if let Some((binding_ix, _)) = bindings.first() if let Some(binding_ix) = first_binding_index
&& *binding_ix > ix && binding_ix > ix
{ {
continue; continue;
} }