Use correct context path for focused element in WindowContext::bindings_for_action (#18843)

Nathan Sobo and Michael Sloan created

Previously, we were reaching in and using the context_stack on the dispatch tree, which was incorrect.

/cc @as-cii 
/cc @ConradIrwin

Release Notes:

- N/A

---------

Co-authored-by: Michael Sloan <michael@zed.dev>

Change summary

crates/gpui/src/key_dispatch.rs | 13 +++++++--
crates/gpui/src/keymap.rs       |  6 ++--
crates/gpui/src/window.rs       | 49 +++++++++++++++--------------------
3 files changed, 34 insertions(+), 34 deletions(-)

Detailed changes

crates/gpui/src/key_dispatch.rs 🔗

@@ -68,7 +68,7 @@ pub(crate) struct DispatchNodeId(usize);
 
 pub(crate) struct DispatchTree {
     node_stack: Vec<DispatchNodeId>,
-    pub(crate) context_stack: Vec<KeyContext>,
+    context_stack: Vec<KeyContext>,
     view_stack: Vec<EntityId>,
     nodes: Vec<DispatchNode>,
     focusable_node_ids: FxHashMap<FocusId, DispatchNodeId>,
@@ -396,13 +396,13 @@ impl DispatchTree {
     pub fn bindings_for_action(
         &self,
         action: &dyn Action,
-        context_stack: &[KeyContext],
+        context_path: &[KeyContext],
     ) -> Vec<KeyBinding> {
         let keymap = self.keymap.borrow();
         keymap
             .bindings_for_action(action)
             .filter(|binding| {
-                let (bindings, _) = keymap.bindings_for_input(&binding.keystrokes, context_stack);
+                let (bindings, _) = keymap.bindings_for_input(&binding.keystrokes, context_path);
                 bindings
                     .iter()
                     .next()
@@ -519,6 +519,13 @@ impl DispatchTree {
         dispatch_path
     }
 
+    pub fn context_path(&self, node_id: DispatchNodeId) -> SmallVec<[KeyContext; 32]> {
+        self.dispatch_path(node_id)
+            .into_iter()
+            .filter_map(|node_id| self.node(node_id).context.clone())
+            .collect()
+    }
+
     pub fn focus_path(&self, focus_id: FocusId) -> SmallVec<[FocusId; 8]> {
         let mut focus_path: SmallVec<[FocusId; 8]> = SmallVec::new();
         let mut current_node_id = self.focusable_node_ids.get(&focus_id).copied();

crates/gpui/src/keymap.rs 🔗

@@ -106,7 +106,7 @@ impl Keymap {
     pub fn bindings_for_input(
         &self,
         input: &[Keystroke],
-        context_stack: &[KeyContext],
+        context_path: &[KeyContext],
     ) -> (SmallVec<[KeyBinding; 1]>, bool) {
         let possibilities = self.bindings().rev().filter_map(|binding| {
             binding
@@ -118,8 +118,8 @@ impl Keymap {
         let mut is_pending = None;
 
         'outer: for (binding, pending) in possibilities {
-            for depth in (0..=context_stack.len()).rev() {
-                if self.binding_enabled(binding, &context_stack[0..depth]) {
+            for depth in (0..=context_path.len()).rev() {
+                if self.binding_enabled(binding, &context_path[0..depth]) {
                     if is_pending.is_none() {
                         is_pending = Some(pending);
                     }

crates/gpui/src/window.rs 🔗

@@ -3683,22 +3683,11 @@ impl<'a> WindowContext<'a> {
 
     /// Returns all available actions for the focused element.
     pub fn available_actions(&self) -> Vec<Box<dyn Action>> {
-        let node_id = self
-            .window
-            .focus
-            .and_then(|focus_id| {
-                self.window
-                    .rendered_frame
-                    .dispatch_tree
-                    .focusable_node_id(focus_id)
-            })
-            .unwrap_or_else(|| self.window.rendered_frame.dispatch_tree.root_node_id());
-
         let mut actions = self
             .window
             .rendered_frame
             .dispatch_tree
-            .available_actions(node_id);
+            .available_actions(self.focused_node_id());
         for action_type in self.global_action_listeners.keys() {
             if let Err(ix) = actions.binary_search_by_key(action_type, |a| a.as_any().type_id()) {
                 let action = self.actions.build_action_type(action_type).ok();
@@ -3712,13 +3701,9 @@ impl<'a> WindowContext<'a> {
 
     /// Returns key bindings that invoke the given action on the currently focused element.
     pub fn bindings_for_action(&self, action: &dyn Action) -> Vec<KeyBinding> {
-        self.window
-            .rendered_frame
-            .dispatch_tree
-            .bindings_for_action(
-                action,
-                &self.window.rendered_frame.dispatch_tree.context_stack,
-            )
+        let dispatch_tree = &self.window.rendered_frame.dispatch_tree;
+        dispatch_tree
+            .bindings_for_action(action, &dispatch_tree.context_path(self.focused_node_id()))
     }
 
     /// Returns key bindings that invoke the given action on the currently focused element.
@@ -3734,15 +3719,23 @@ impl<'a> WindowContext<'a> {
     ) -> Vec<KeyBinding> {
         let dispatch_tree = &self.window.rendered_frame.dispatch_tree;
 
-        let Some(node_id) = dispatch_tree.focusable_node_id(focus_handle.id) else {
-            return vec![];
-        };
-        let context_stack: Vec<_> = dispatch_tree
-            .dispatch_path(node_id)
-            .into_iter()
-            .filter_map(|node_id| dispatch_tree.node(node_id).context.clone())
-            .collect();
-        dispatch_tree.bindings_for_action(action, &context_stack)
+        if let Some(node_id) = dispatch_tree.focusable_node_id(focus_handle.id) {
+            dispatch_tree.bindings_for_action(action, &dispatch_tree.context_path(node_id))
+        } else {
+            vec![]
+        }
+    }
+
+    fn focused_node_id(&self) -> DispatchNodeId {
+        self.window
+            .focus
+            .and_then(|focus_id| {
+                self.window
+                    .rendered_frame
+                    .dispatch_tree
+                    .focusable_node_id(focus_id)
+            })
+            .unwrap_or_else(|| self.window.rendered_frame.dispatch_tree.root_node_id())
     }
 
     /// Returns a generic event listener that invokes the given listener with the view and context associated with the given view handle.