Fix bindings_for_action handling of shadowed key bindings (#32220)

Fixes two things:

* ~3 months ago [in PR
#26420](https://github.com/zed-industries/zed/pull/26420/files#diff-33b58aa2da03d791c2c4761af6012851b7400e348922d64babe5fd48ac2a8e60)
`bindings_for_action` was changed to return bindings even when they are
shadowed (when the keystrokes would actually do something else).

* For edit prediction keybindings there was some odd behavior where
bindings for `edit_prediction_conflict` were taking precedence over
bindings for `edit_prediction` even when the `edit_prediction_conflict`
predicate didn't match. The workaround for this was #24812. The way it
worked was:

    - List all bindings for the action

- For each binding, get the highest precedence binding with the same
input sequence

- If the highest precedence binding has the same action, include this
binding. This was the bug - this meant that if a binding in the keymap
has the same keystrokes and action it can incorrectly take display
precedence even if its context predicate does not pass.

- Fix is to check that the highest precedence binding is a full match.
To do this efficiently, it's based on an index within the keymap
bindings.

Also adds `highest_precedence_binding_*` variants which avoid the
inefficiency of building lists of bindings just to use the last.

Release Notes:

- Fixed display of keybindings to skip bindings that are shadowed by a
binding that uses the same keystrokes.
- Fixed display of `editor::AcceptEditPrediction` bindings to use the
normal precedence that prioritizes user bindings.
This commit is contained in:
Michael Sloan 2025-06-06 00:24:59 -06:00 committed by GitHub
parent 37fa42d5cc
commit d801b7b12e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 168 additions and 89 deletions

View file

@ -35,8 +35,7 @@ impl KeyBinding {
if let Some(focused) = window.focused(cx) {
return Self::for_action_in(action, &focused, window, cx);
}
let key_binding =
gpui::Keymap::binding_to_display_from_bindings(window.bindings_for_action(action))?;
let key_binding = window.highest_precedence_binding_for_action(action)?;
Some(Self::new(key_binding, cx))
}
@ -47,9 +46,7 @@ impl KeyBinding {
window: &mut Window,
cx: &App,
) -> Option<Self> {
let key_binding = gpui::Keymap::binding_to_display_from_bindings(
window.bindings_for_action_in(action, focus),
)?;
let key_binding = window.highest_precedence_binding_for_action_in(action, focus)?;
Some(Self::new(key_binding, cx))
}
@ -355,8 +352,7 @@ impl KeyIcon {
/// Returns a textual representation of the key binding for the given [`Action`].
pub fn text_for_action(action: &dyn Action, window: &Window, cx: &App) -> Option<String> {
let bindings = window.bindings_for_action(action);
let key_binding = bindings.last()?;
let key_binding = window.highest_precedence_binding_for_action(action)?;
Some(text_for_keystrokes(key_binding.keystrokes(), cx))
}