Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T03:32:33.648Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

key_dispatch.rs

1196 lines · 42.1 KB · rust
1//! KeyDispatch is where GPUI deals with binding actions to key events.
2//!
3//! The key pieces to making a key binding work are to define an action,
4//! implement a method that takes that action as a type parameter,
5//! and then to register the action during render on a focused node
6//! with a keymap context:
7//!
8//! ```ignore
9//! actions!(editor,[Undo, Redo]);
10//!
11//! impl Editor {
12//!   fn undo(&mut self, _: &Undo, _window: &mut Window, _cx: &mut Context<Self>) { ... }
13//!   fn redo(&mut self, _: &Redo, _window: &mut Window, _cx: &mut Context<Self>) { ... }
14//! }
15//!
16//! impl Render for Editor {
17//!   fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
18//!     div()
19//!       .track_focus(&self.focus_handle(cx))
20//!       .key_context("Editor")
21//!       .on_action(cx.listener(Editor::undo))
22//!       .on_action(cx.listener(Editor::redo))
23//!     ...
24//!    }
25//! }
26//!```
27//!
28//! The keybindings themselves are managed independently by calling cx.bind_keys().
29//! (Though mostly when developing Zed itself, you just need to add a new line to
30//!  assets/keymaps/default-{platform}.json).
31//!
32//! ```ignore
33//! cx.bind_keys([
34//!   KeyBinding::new("cmd-z", Editor::undo, Some("Editor")),
35//!   KeyBinding::new("cmd-shift-z", Editor::redo, Some("Editor")),
36//! ])
37//! ```
38//!
39//! With all of this in place, GPUI will ensure that if you have an Editor that contains
40//! the focus, hitting cmd-z will Undo.
41//!
42//! In real apps, it is a little more complicated than this, because typically you have
43//! several nested views that each register keyboard handlers. In this case action matching
44//! bubbles up from the bottom. For example in Zed, the Workspace is the top-level view, which contains Pane's, which contain Editors. If there are conflicting keybindings defined
45//! then the Editor's bindings take precedence over the Pane's bindings, which take precedence over the Workspace.
46//!
47//! In GPUI, keybindings are not limited to just single keystrokes, you can define
48//! sequences by separating the keys with a space:
49//!
50//!  KeyBinding::new("cmd-k left", pane::SplitLeft, Some("Pane"))
51
52use crate::{
53    Action, ActionRegistry, App, DispatchPhase, EntityId, FocusId, KeyBinding, KeyContext, Keymap,
54    Keystroke, ModifiersChangedEvent, Window,
55};
56use collections::FxHashMap;
57use smallvec::SmallVec;
58use std::{
59    any::{Any, TypeId},
60    cell::RefCell,
61    mem,
62    ops::Range,
63    rc::Rc,
64};
65
66/// ID of a node within `DispatchTree`. Note that these are **not** stable between frames, and so a
67/// `DispatchNodeId` should only be used with the `DispatchTree` that provided it.
68#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
69pub(crate) struct DispatchNodeId(usize);
70
71pub(crate) struct DispatchTree {
72    node_stack: Vec<DispatchNodeId>,
73    pub(crate) context_stack: Vec<KeyContext>,
74    view_stack: Vec<EntityId>,
75    nodes: Vec<DispatchNode>,
76    focusable_node_ids: FxHashMap<FocusId, DispatchNodeId>,
77    view_node_ids: FxHashMap<EntityId, DispatchNodeId>,
78    keymap: Rc<RefCell<Keymap>>,
79    action_registry: Rc<ActionRegistry>,
80}
81
82#[derive(Default)]
83pub(crate) struct DispatchNode {
84    pub key_listeners: Vec<KeyListener>,
85    pub action_listeners: Vec<DispatchActionListener>,
86    pub modifiers_changed_listeners: Vec<ModifiersChangedListener>,
87    pub context: Option<KeyContext>,
88    pub focus_id: Option<FocusId>,
89    view_id: Option<EntityId>,
90    parent: Option<DispatchNodeId>,
91}
92
93pub(crate) struct ReusedSubtree {
94    old_range: Range<usize>,
95    new_range: Range<usize>,
96    contains_focus: bool,
97}
98
99impl ReusedSubtree {
100    pub fn refresh_node_id(&self, node_id: DispatchNodeId) -> DispatchNodeId {
101        debug_assert!(
102            self.old_range.contains(&node_id.0),
103            "node {} was not part of the reused subtree {:?}",
104            node_id.0,
105            self.old_range
106        );
107        DispatchNodeId((node_id.0 - self.old_range.start) + self.new_range.start)
108    }
109
110    pub fn contains_focus(&self) -> bool {
111        self.contains_focus
112    }
113}
114
115#[derive(Default, Debug)]
116pub(crate) struct Replay {
117    pub(crate) keystroke: Keystroke,
118    pub(crate) bindings: SmallVec<[KeyBinding; 1]>,
119}
120
121#[derive(Default, Debug)]
122pub(crate) struct DispatchResult {
123    pub(crate) pending: SmallVec<[Keystroke; 1]>,
124    pub(crate) pending_has_binding: bool,
125    pub(crate) bindings: SmallVec<[KeyBinding; 1]>,
126    pub(crate) to_replay: SmallVec<[Replay; 1]>,
127    pub(crate) context_stack: Vec<KeyContext>,
128}
129
130type KeyListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut Window, &mut App)>;
131type ModifiersChangedListener = Rc<dyn Fn(&ModifiersChangedEvent, &mut Window, &mut App)>;
132
133#[derive(Clone)]
134pub(crate) struct DispatchActionListener {
135    pub(crate) action_type: TypeId,
136    pub(crate) listener: Rc<dyn Fn(&dyn Any, DispatchPhase, &mut Window, &mut App)>,
137}
138
139impl DispatchTree {
140    pub fn new(keymap: Rc<RefCell<Keymap>>, action_registry: Rc<ActionRegistry>) -> Self {
141        Self {
142            node_stack: Vec::new(),
143            context_stack: Vec::new(),
144            view_stack: Vec::new(),
145            nodes: Vec::new(),
146            focusable_node_ids: FxHashMap::default(),
147            view_node_ids: FxHashMap::default(),
148            keymap,
149            action_registry,
150        }
151    }
152
153    pub fn clear(&mut self) {
154        self.node_stack.clear();
155        self.context_stack.clear();
156        self.view_stack.clear();
157        self.nodes.clear();
158        self.focusable_node_ids.clear();
159        self.view_node_ids.clear();
160    }
161
162    pub fn len(&self) -> usize {
163        self.nodes.len()
164    }
165
166    pub fn push_node(&mut self) -> DispatchNodeId {
167        let parent = self.node_stack.last().copied();
168        let node_id = DispatchNodeId(self.nodes.len());
169
170        self.nodes.push(DispatchNode {
171            parent,
172            ..Default::default()
173        });
174        self.node_stack.push(node_id);
175        node_id
176    }
177
178    pub fn set_active_node(&mut self, node_id: DispatchNodeId) {
179        let next_node_parent = self.nodes[node_id.0].parent;
180        while self.node_stack.last().copied() != next_node_parent && !self.node_stack.is_empty() {
181            self.pop_node();
182        }
183
184        if self.node_stack.last().copied() == next_node_parent {
185            self.node_stack.push(node_id);
186            let active_node = &self.nodes[node_id.0];
187            if let Some(view_id) = active_node.view_id {
188                self.view_stack.push(view_id)
189            }
190            if let Some(context) = active_node.context.clone() {
191                self.context_stack.push(context);
192            }
193        } else {
194            debug_assert_eq!(self.node_stack.len(), 0);
195
196            let mut current_node_id = Some(node_id);
197            while let Some(node_id) = current_node_id {
198                let node = &self.nodes[node_id.0];
199                if let Some(context) = node.context.clone() {
200                    self.context_stack.push(context);
201                }
202                if let Some(view_id) = node.view_id {
203                    self.view_stack.push(view_id);
204                }
205                self.node_stack.push(node_id);
206                current_node_id = node.parent;
207            }
208
209            self.context_stack.reverse();
210            self.view_stack.reverse();
211            self.node_stack.reverse();
212        }
213    }
214
215    pub fn set_key_context(&mut self, context: KeyContext) {
216        self.active_node().context = Some(context.clone());
217        self.context_stack.push(context);
218    }
219
220    pub fn set_focus_id(&mut self, focus_id: FocusId) {
221        let node_id = *self.node_stack.last().unwrap();
222        self.nodes[node_id.0].focus_id = Some(focus_id);
223        self.focusable_node_ids.insert(focus_id, node_id);
224    }
225
226    pub fn set_view_id(&mut self, view_id: EntityId) {
227        if self.view_stack.last().copied() != Some(view_id) {
228            let node_id = *self.node_stack.last().unwrap();
229            self.nodes[node_id.0].view_id = Some(view_id);
230            self.view_node_ids.insert(view_id, node_id);
231            self.view_stack.push(view_id);
232        }
233    }
234
235    pub fn pop_node(&mut self) {
236        let node = &self.nodes[self.active_node_id().unwrap().0];
237        if node.context.is_some() {
238            self.context_stack.pop();
239        }
240        if node.view_id.is_some() {
241            self.view_stack.pop();
242        }
243        self.node_stack.pop();
244    }
245
246    fn move_node(&mut self, source: &mut DispatchNode) {
247        self.push_node();
248        if let Some(context) = source.context.clone() {
249            self.set_key_context(context);
250        }
251        if let Some(focus_id) = source.focus_id {
252            self.set_focus_id(focus_id);
253        }
254        if let Some(view_id) = source.view_id {
255            self.set_view_id(view_id);
256        }
257
258        let target = self.active_node();
259        target.key_listeners = mem::take(&mut source.key_listeners);
260        target.action_listeners = mem::take(&mut source.action_listeners);
261        target.modifiers_changed_listeners = mem::take(&mut source.modifiers_changed_listeners);
262    }
263
264    pub fn reuse_subtree(
265        &mut self,
266        old_range: Range<usize>,
267        source: &mut Self,
268        focus: Option<FocusId>,
269    ) -> ReusedSubtree {
270        let new_range = self.nodes.len()..self.nodes.len() + old_range.len();
271
272        let mut contains_focus = false;
273        let mut source_stack = vec![];
274        for (source_node_id, source_node) in source
275            .nodes
276            .iter_mut()
277            .enumerate()
278            .skip(old_range.start)
279            .take(old_range.len())
280        {
281            let source_node_id = DispatchNodeId(source_node_id);
282            while let Some(source_ancestor) = source_stack.last() {
283                if source_node.parent == Some(*source_ancestor) {
284                    break;
285                } else {
286                    source_stack.pop();
287                    self.pop_node();
288                }
289            }
290
291            source_stack.push(source_node_id);
292            if source_node.focus_id.is_some() && source_node.focus_id == focus {
293                contains_focus = true;
294            }
295            self.move_node(source_node);
296        }
297
298        while !source_stack.is_empty() {
299            source_stack.pop();
300            self.pop_node();
301        }
302
303        ReusedSubtree {
304            old_range,
305            new_range,
306            contains_focus,
307        }
308    }
309
310    pub fn truncate(&mut self, index: usize) {
311        for node in &self.nodes[index..] {
312            if let Some(focus_id) = node.focus_id {
313                self.focusable_node_ids.remove(&focus_id);
314            }
315
316            if let Some(view_id) = node.view_id {
317                self.view_node_ids.remove(&view_id);
318            }
319        }
320        self.nodes.truncate(index);
321    }
322
323    pub fn on_key_event(&mut self, listener: KeyListener) {
324        self.active_node().key_listeners.push(listener);
325    }
326
327    pub fn on_modifiers_changed(&mut self, listener: ModifiersChangedListener) {
328        self.active_node()
329            .modifiers_changed_listeners
330            .push(listener);
331    }
332
333    pub fn on_action(
334        &mut self,
335        action_type: TypeId,
336        listener: Rc<dyn Fn(&dyn Any, DispatchPhase, &mut Window, &mut App)>,
337    ) {
338        self.active_node()
339            .action_listeners
340            .push(DispatchActionListener {
341                action_type,
342                listener,
343            });
344    }
345
346    pub fn focus_contains(&self, parent: FocusId, child: FocusId) -> bool {
347        if parent == child {
348            return true;
349        }
350
351        if let Some(parent_node_id) = self.focusable_node_ids.get(&parent) {
352            let mut current_node_id = self.focusable_node_ids.get(&child).copied();
353            while let Some(node_id) = current_node_id {
354                if node_id == *parent_node_id {
355                    return true;
356                }
357                current_node_id = self.nodes[node_id.0].parent;
358            }
359        }
360        false
361    }
362
363    pub fn available_actions(&self, target: DispatchNodeId) -> Vec<Box<dyn Action>> {
364        let mut actions = Vec::<Box<dyn Action>>::new();
365        for node_id in self.dispatch_path(target) {
366            let node = &self.nodes[node_id.0];
367            for DispatchActionListener { action_type, .. } in &node.action_listeners {
368                if let Err(ix) = actions.binary_search_by_key(action_type, |a| a.as_any().type_id())
369                {
370                    // Intentionally silence these errors without logging.
371                    // If an action cannot be built by default, it's not available.
372                    let action = self.action_registry.build_action_type(action_type).ok();
373                    if let Some(action) = action {
374                        actions.insert(ix, action);
375                    }
376                }
377            }
378        }
379        actions
380    }
381
382    pub fn is_action_available(&self, action: &dyn Action, target: DispatchNodeId) -> bool {
383        for node_id in self.dispatch_path(target) {
384            let node = &self.nodes[node_id.0];
385            if node
386                .action_listeners
387                .iter()
388                .any(|listener| listener.action_type == action.as_any().type_id())
389            {
390                return true;
391            }
392        }
393        false
394    }
395
396    /// Returns key bindings that invoke an action on the currently focused element. Bindings are
397    /// returned in the order they were added. For display, the last binding should take precedence.
398    ///
399    /// Bindings are only included if they are the highest precedence match for their keystrokes, so
400    /// shadowed bindings are not included.
401    pub fn bindings_for_action(
402        &self,
403        action: &dyn Action,
404        context_stack: &[KeyContext],
405    ) -> Vec<KeyBinding> {
406        // Ideally this would return a `DoubleEndedIterator` to avoid `highest_precedence_*`
407        // methods, but this can't be done very cleanly since keymap must be borrowed.
408        let keymap = self.keymap.borrow();
409        keymap
410            .bindings_for_action(action)
411            .filter(|binding| {
412                Self::binding_matches_predicate_and_not_shadowed(&keymap, binding, context_stack)
413            })
414            .cloned()
415            .collect()
416    }
417
418    /// Returns the highest precedence binding for the given action and context stack. This is the
419    /// same as the last result of `bindings_for_action`, but more efficient than getting all bindings.
420    pub fn highest_precedence_binding_for_action(
421        &self,
422        action: &dyn Action,
423        context_stack: &[KeyContext],
424    ) -> Option<KeyBinding> {
425        let keymap = self.keymap.borrow();
426        keymap
427            .bindings_for_action(action)
428            .rev()
429            .find(|binding| {
430                Self::binding_matches_predicate_and_not_shadowed(&keymap, binding, context_stack)
431            })
432            .cloned()
433    }
434
435    fn binding_matches_predicate_and_not_shadowed(
436        keymap: &Keymap,
437        binding: &KeyBinding,
438        context_stack: &[KeyContext],
439    ) -> bool {
440        let (bindings, _) = keymap.bindings_for_input(&binding.keystrokes, context_stack);
441        if let Some(found) = bindings.iter().next() {
442            found.action.partial_eq(binding.action.as_ref())
443        } else {
444            false
445        }
446    }
447
448    fn bindings_for_input(
449        &self,
450        input: &[Keystroke],
451        dispatch_path: &SmallVec<[DispatchNodeId; 32]>,
452    ) -> (SmallVec<[KeyBinding; 1]>, bool, Vec<KeyContext>) {
453        let context_stack: Vec<KeyContext> = dispatch_path
454            .iter()
455            .filter_map(|node_id| self.node(*node_id).context.clone())
456            .collect();
457
458        let (bindings, partial) = self
459            .keymap
460            .borrow()
461            .bindings_for_input(input, &context_stack);
462        (bindings, partial, context_stack)
463    }
464
465    /// Find the bindings that can follow the current input sequence.
466    pub fn possible_next_bindings_for_input(
467        &self,
468        input: &[Keystroke],
469        context_stack: &[KeyContext],
470    ) -> Vec<KeyBinding> {
471        self.keymap
472            .borrow()
473            .possible_next_bindings_for_input(input, context_stack)
474    }
475
476    /// dispatch_key processes the keystroke
477    /// input should be set to the value of `pending` from the previous call to dispatch_key.
478    /// This returns three instructions to the input handler:
479    /// - bindings: any bindings to execute before processing this keystroke
480    /// - pending: the new set of pending keystrokes to store
481    /// - to_replay: any keystroke that had been pushed to pending, but are no-longer matched,
482    ///   these should be replayed first.
483    pub fn dispatch_key(
484        &mut self,
485        mut input: SmallVec<[Keystroke; 1]>,
486        keystroke: Keystroke,
487        dispatch_path: &SmallVec<[DispatchNodeId; 32]>,
488    ) -> DispatchResult {
489        input.push(keystroke.clone());
490        let (bindings, pending, context_stack) = self.bindings_for_input(&input, dispatch_path);
491
492        if pending {
493            return DispatchResult {
494                pending: input,
495                pending_has_binding: !bindings.is_empty(),
496                context_stack,
497                ..Default::default()
498            };
499        } else if !bindings.is_empty() {
500            return DispatchResult {
501                bindings,
502                context_stack,
503                ..Default::default()
504            };
505        } else if input.len() == 1 {
506            return DispatchResult {
507                context_stack,
508                ..Default::default()
509            };
510        }
511        input.pop();
512
513        let (suffix, mut to_replay) = self.replay_prefix(input, dispatch_path);
514
515        let mut result = self.dispatch_key(suffix, keystroke, dispatch_path);
516        to_replay.extend(result.to_replay);
517        result.to_replay = to_replay;
518        result
519    }
520
521    /// If the user types a matching prefix of a binding and then waits for a timeout
522    /// flush_dispatch() converts any previously pending input to replay events.
523    pub fn flush_dispatch(
524        &mut self,
525        input: SmallVec<[Keystroke; 1]>,
526        dispatch_path: &SmallVec<[DispatchNodeId; 32]>,
527    ) -> SmallVec<[Replay; 1]> {
528        let (suffix, mut to_replay) = self.replay_prefix(input, dispatch_path);
529
530        if !suffix.is_empty() {
531            to_replay.extend(self.flush_dispatch(suffix, dispatch_path))
532        }
533
534        to_replay
535    }
536
537    /// Converts the longest prefix of input to a replay event and returns the rest.
538    fn replay_prefix(
539        &self,
540        mut input: SmallVec<[Keystroke; 1]>,
541        dispatch_path: &SmallVec<[DispatchNodeId; 32]>,
542    ) -> (SmallVec<[Keystroke; 1]>, SmallVec<[Replay; 1]>) {
543        let mut to_replay: SmallVec<[Replay; 1]> = Default::default();
544        for last in (0..input.len()).rev() {
545            let (bindings, _, _) = self.bindings_for_input(&input[0..=last], dispatch_path);
546            if !bindings.is_empty() {
547                to_replay.push(Replay {
548                    keystroke: input.drain(0..=last).next_back().unwrap(),
549                    bindings,
550                });
551                break;
552            }
553        }
554        if to_replay.is_empty() {
555            to_replay.push(Replay {
556                keystroke: input.remove(0),
557                ..Default::default()
558            });
559        }
560        (input, to_replay)
561    }
562
563    pub fn dispatch_path(&self, target: DispatchNodeId) -> SmallVec<[DispatchNodeId; 32]> {
564        let mut dispatch_path: SmallVec<[DispatchNodeId; 32]> = SmallVec::new();
565        let mut current_node_id = Some(target);
566        while let Some(node_id) = current_node_id {
567            dispatch_path.push(node_id);
568            current_node_id = self.nodes.get(node_id.0).and_then(|node| node.parent);
569        }
570        dispatch_path.reverse(); // Reverse the path so it goes from the root to the focused node.
571        dispatch_path
572    }
573
574    pub fn focus_path(&self, focus_id: FocusId) -> SmallVec<[FocusId; 8]> {
575        let mut focus_path: SmallVec<[FocusId; 8]> = SmallVec::new();
576        let mut current_node_id = self.focusable_node_ids.get(&focus_id).copied();
577        while let Some(node_id) = current_node_id {
578            let node = self.node(node_id);
579            if let Some(focus_id) = node.focus_id {
580                focus_path.push(focus_id);
581            }
582            current_node_id = node.parent;
583        }
584        focus_path.reverse(); // Reverse the path so it goes from the root to the focused node.
585        focus_path
586    }
587
588    pub fn view_path_reversed(&self, view_id: EntityId) -> impl Iterator<Item = EntityId> {
589        let mut current_node_id = self.view_node_ids.get(&view_id).copied();
590
591        std::iter::successors(
592            current_node_id.map(|node_id| self.node(node_id)),
593            |node_id| Some(self.node(node_id.parent?)),
594        )
595        .filter_map(|node| node.view_id)
596    }
597
598    pub fn node(&self, node_id: DispatchNodeId) -> &DispatchNode {
599        &self.nodes[node_id.0]
600    }
601
602    fn active_node(&mut self) -> &mut DispatchNode {
603        let active_node_id = self.active_node_id().unwrap();
604        &mut self.nodes[active_node_id.0]
605    }
606
607    pub fn focusable_node_id(&self, target: FocusId) -> Option<DispatchNodeId> {
608        self.focusable_node_ids.get(&target).copied()
609    }
610
611    pub fn root_node_id(&self) -> DispatchNodeId {
612        debug_assert!(!self.nodes.is_empty());
613        DispatchNodeId(0)
614    }
615
616    pub fn active_node_id(&self) -> Option<DispatchNodeId> {
617        self.node_stack.last().copied()
618    }
619}
620
621#[cfg(test)]
622mod tests {
623    use crate::{
624        self as gpui, AppContext, DispatchResult, Element, ElementId, GlobalElementId,
625        InspectorElementId, Keystroke, LayoutId, Style,
626    };
627    use core::panic;
628    use smallvec::SmallVec;
629    use std::{cell::RefCell, ops::Range, rc::Rc};
630
631    use crate::{
632        ActionRegistry, App, Bounds, Context, DispatchTree, FocusHandle, InputHandler, IntoElement,
633        KeyBinding, KeyContext, Keymap, Pixels, Point, Render, Subscription, TestAppContext,
634        UTF16Selection, Unbind, Window,
635    };
636
637    actions!(dispatch_test, [TestAction, SecondaryTestAction]);
638
639    fn test_dispatch_tree(bindings: Vec<KeyBinding>) -> DispatchTree {
640        let registry = ActionRegistry::default();
641
642        DispatchTree::new(
643            Rc::new(RefCell::new(Keymap::new(bindings))),
644            Rc::new(registry),
645        )
646    }
647
648    #[test]
649    fn test_keybinding_for_action_bounds() {
650        let tree = test_dispatch_tree(vec![KeyBinding::new(
651            "cmd-n",
652            TestAction,
653            Some("ProjectPanel"),
654        )]);
655
656        let contexts = vec![
657            KeyContext::parse("Workspace").unwrap(),
658            KeyContext::parse("ProjectPanel").unwrap(),
659        ];
660
661        let keybinding = tree.bindings_for_action(&TestAction, &contexts);
662
663        assert!(keybinding[0].action.partial_eq(&TestAction))
664    }
665
666    #[test]
667    fn test_bindings_for_action_hides_targeted_unbind_in_active_context() {
668        let tree = test_dispatch_tree(vec![
669            KeyBinding::new("tab", TestAction, Some("Editor")),
670            KeyBinding::new(
671                "tab",
672                Unbind("dispatch_test::TestAction".into()),
673                Some("Editor && edit_prediction"),
674            ),
675            KeyBinding::new(
676                "tab",
677                SecondaryTestAction,
678                Some("Editor && showing_completions"),
679            ),
680        ]);
681
682        let contexts = vec![
683            KeyContext::parse("Workspace").unwrap(),
684            KeyContext::parse("Editor showing_completions edit_prediction").unwrap(),
685        ];
686
687        let bindings = tree.bindings_for_action(&TestAction, &contexts);
688        assert!(bindings.is_empty());
689
690        let highest = tree.highest_precedence_binding_for_action(&TestAction, &contexts);
691        assert!(highest.is_none());
692
693        let fallback_bindings = tree.bindings_for_action(&SecondaryTestAction, &contexts);
694        assert_eq!(fallback_bindings.len(), 1);
695        assert!(fallback_bindings[0].action.partial_eq(&SecondaryTestAction));
696    }
697
698    #[test]
699    fn test_bindings_for_action_keeps_targeted_binding_outside_unbind_context() {
700        let tree = test_dispatch_tree(vec![
701            KeyBinding::new("tab", TestAction, Some("Editor")),
702            KeyBinding::new(
703                "tab",
704                Unbind("dispatch_test::TestAction".into()),
705                Some("Editor && edit_prediction"),
706            ),
707            KeyBinding::new(
708                "tab",
709                SecondaryTestAction,
710                Some("Editor && showing_completions"),
711            ),
712        ]);
713
714        let contexts = vec![
715            KeyContext::parse("Workspace").unwrap(),
716            KeyContext::parse("Editor").unwrap(),
717        ];
718
719        let bindings = tree.bindings_for_action(&TestAction, &contexts);
720        assert_eq!(bindings.len(), 1);
721        assert!(bindings[0].action.partial_eq(&TestAction));
722
723        let highest = tree.highest_precedence_binding_for_action(&TestAction, &contexts);
724        assert!(highest.is_some_and(|binding| binding.action.partial_eq(&TestAction)));
725    }
726
727    /// Models the picker preview footer scenario: a picker action is bound in
728    /// `Picker > Editor`, but a base keymap binds the same chord to an editor
729    /// action in `Editor`. `Picker > Editor` and `Editor` resolve at the same
730    /// context depth, so at equal depth precedence is decided purely by load
731    /// order (later wins). Because base keymaps load after the default keymap,
732    /// the picker binding is shadowed unless it is (re)bound by an overlay that
733    /// loads after the base keymap - which is exactly what
734    /// `keymaps/specific-overrides*.json` does.
735    #[test]
736    fn test_overlay_after_base_restores_shadowed_picker_binding() {
737        // SecondaryTestAction stands in for the editor/base action (e.g.
738        // editor::AddSelectionBelow), TestAction for the picker action.
739        let contexts = vec![
740            KeyContext::parse("Picker").unwrap(),
741            KeyContext::parse("Editor").unwrap(),
742        ];
743
744        // Default keymap (picker binding) followed by a base keymap that binds
745        // the same chord to an editor action: the base binding wins and the
746        // picker action is shadowed, so its footer tooltip renders no shortcut.
747        let shadowed = test_dispatch_tree(vec![
748            KeyBinding::new("ctrl-alt-down", TestAction, Some("Picker > Editor")),
749            KeyBinding::new("ctrl-alt-down", SecondaryTestAction, Some("Editor")),
750        ]);
751        let highest = shadowed.highest_precedence_binding_for_action(&TestAction, &contexts);
752        assert!(
753            highest.is_none(),
754            "picker binding should be shadowed by the base editor binding"
755        );
756
757        // Re-binding the picker action in an overlay loaded after the base keymap
758        // restores it as the resolved binding.
759        let fixed = test_dispatch_tree(vec![
760            KeyBinding::new("ctrl-alt-down", TestAction, Some("Picker > Editor")),
761            KeyBinding::new("ctrl-alt-down", SecondaryTestAction, Some("Editor")),
762            // overlay loaded last:
763            KeyBinding::new("ctrl-alt-down", TestAction, Some("Picker > Editor")),
764        ]);
765        let highest = fixed.highest_precedence_binding_for_action(&TestAction, &contexts);
766        assert!(
767            highest.is_some_and(|binding| binding.action.partial_eq(&TestAction)),
768            "overlay loaded after base should restore the picker binding"
769        );
770
771        // Conversely, putting the override in the default keymap (i.e. before the
772        // base keymap) does NOT help: the later base binding still wins at equal
773        // depth. This is why the overlay must be loaded after the base keymap.
774        let override_before_base = test_dispatch_tree(vec![
775            KeyBinding::new("ctrl-alt-down", TestAction, Some("Picker > Editor")),
776            KeyBinding::new("ctrl-alt-down", TestAction, Some("Picker > Editor")),
777            KeyBinding::new("ctrl-alt-down", SecondaryTestAction, Some("Editor")),
778        ]);
779        let highest =
780            override_before_base.highest_precedence_binding_for_action(&TestAction, &contexts);
781        assert!(
782            highest.is_none(),
783            "an override loaded before the base binding cannot win the equal-depth tie"
784        );
785    }
786
787    #[test]
788    fn test_pending_has_binding_state() {
789        let bindings = vec![
790            KeyBinding::new("ctrl-b h", TestAction, None),
791            KeyBinding::new("space", TestAction, Some("ContextA")),
792            KeyBinding::new("space f g", TestAction, Some("ContextB")),
793        ];
794        let mut tree = test_dispatch_tree(bindings);
795
796        type DispatchPath = SmallVec<[super::DispatchNodeId; 32]>;
797        fn dispatch(
798            tree: &mut DispatchTree,
799            pending: SmallVec<[Keystroke; 1]>,
800            key: &str,
801            path: &DispatchPath,
802        ) -> DispatchResult {
803            tree.dispatch_key(pending, Keystroke::parse(key).unwrap(), path)
804        }
805
806        let dispatch_path: DispatchPath = SmallVec::new();
807        let result = dispatch(&mut tree, SmallVec::new(), "ctrl-b", &dispatch_path);
808        assert_eq!(result.pending.len(), 1);
809        assert!(!result.pending_has_binding);
810
811        let result = dispatch(&mut tree, result.pending, "h", &dispatch_path);
812        assert_eq!(result.pending.len(), 0);
813        assert_eq!(result.bindings.len(), 1);
814        assert!(!result.pending_has_binding);
815
816        let node_id = tree.push_node();
817        tree.set_key_context(KeyContext::parse("ContextB").unwrap());
818        tree.pop_node();
819
820        let dispatch_path = tree.dispatch_path(node_id);
821        let result = dispatch(&mut tree, SmallVec::new(), "space", &dispatch_path);
822
823        assert_eq!(result.pending.len(), 1);
824        assert!(!result.pending_has_binding);
825    }
826
827    #[crate::test]
828    fn test_pending_input_observers_notified_on_focus_change(cx: &mut TestAppContext) {
829        #[derive(Clone)]
830        struct CustomElement {
831            focus_handle: FocusHandle,
832            text: Rc<RefCell<String>>,
833        }
834
835        impl CustomElement {
836            fn new(cx: &mut Context<Self>) -> Self {
837                Self {
838                    focus_handle: cx.focus_handle(),
839                    text: Rc::default(),
840                }
841            }
842        }
843
844        impl Element for CustomElement {
845            type RequestLayoutState = ();
846
847            type PrepaintState = ();
848
849            fn id(&self) -> Option<ElementId> {
850                Some("custom".into())
851            }
852
853            fn source_location(&self) -> Option<&'static panic::Location<'static>> {
854                None
855            }
856
857            fn request_layout(
858                &mut self,
859                _: Option<&GlobalElementId>,
860                _: Option<&InspectorElementId>,
861                window: &mut Window,
862                cx: &mut App,
863            ) -> (LayoutId, Self::RequestLayoutState) {
864                (window.request_layout(Style::default(), [], cx), ())
865            }
866
867            fn prepaint(
868                &mut self,
869                _: Option<&GlobalElementId>,
870                _: Option<&InspectorElementId>,
871                _: Bounds<Pixels>,
872                _: &mut Self::RequestLayoutState,
873                window: &mut Window,
874                cx: &mut App,
875            ) -> Self::PrepaintState {
876                window.set_focus_handle(&self.focus_handle, cx);
877            }
878
879            fn paint(
880                &mut self,
881                _: Option<&GlobalElementId>,
882                _: Option<&InspectorElementId>,
883                _: Bounds<Pixels>,
884                _: &mut Self::RequestLayoutState,
885                _: &mut Self::PrepaintState,
886                window: &mut Window,
887                cx: &mut App,
888            ) {
889                let mut key_context = KeyContext::default();
890                key_context.add("Terminal");
891                window.set_key_context(key_context);
892                window.handle_input(&self.focus_handle, self.clone(), cx);
893                window.on_action(std::any::TypeId::of::<TestAction>(), |_, _, _, _| {});
894            }
895        }
896
897        impl IntoElement for CustomElement {
898            type Element = Self;
899
900            fn into_element(self) -> Self::Element {
901                self
902            }
903        }
904
905        impl InputHandler for CustomElement {
906            fn selected_text_range(
907                &mut self,
908                _: bool,
909                _: &mut Window,
910                _: &mut App,
911            ) -> Option<UTF16Selection> {
912                None
913            }
914
915            fn marked_text_range(&mut self, _: &mut Window, _: &mut App) -> Option<Range<usize>> {
916                None
917            }
918
919            fn text_for_range(
920                &mut self,
921                _: Range<usize>,
922                _: &mut Option<Range<usize>>,
923                _: &mut Window,
924                _: &mut App,
925            ) -> Option<String> {
926                None
927            }
928
929            fn replace_text_in_range(
930                &mut self,
931                replacement_range: Option<Range<usize>>,
932                text: &str,
933                _: &mut Window,
934                _: &mut App,
935            ) {
936                if replacement_range.is_some() {
937                    unimplemented!()
938                }
939                self.text.borrow_mut().push_str(text)
940            }
941
942            fn replace_and_mark_text_in_range(
943                &mut self,
944                replacement_range: Option<Range<usize>>,
945                new_text: &str,
946                _: Option<Range<usize>>,
947                _: &mut Window,
948                _: &mut App,
949            ) {
950                if replacement_range.is_some() {
951                    unimplemented!()
952                }
953                self.text.borrow_mut().push_str(new_text)
954            }
955
956            fn unmark_text(&mut self, _: &mut Window, _: &mut App) {}
957
958            fn bounds_for_range(
959                &mut self,
960                _: Range<usize>,
961                _: &mut Window,
962                _: &mut App,
963            ) -> Option<Bounds<Pixels>> {
964                None
965            }
966
967            fn character_index_for_point(
968                &mut self,
969                _: Point<Pixels>,
970                _: &mut Window,
971                _: &mut App,
972            ) -> Option<usize> {
973                None
974            }
975        }
976
977        impl Render for CustomElement {
978            fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
979                self.clone()
980            }
981        }
982
983        cx.update(|cx| {
984            cx.bind_keys([KeyBinding::new("ctrl-b", TestAction, Some("Terminal"))]);
985            cx.bind_keys([KeyBinding::new("ctrl-b h", TestAction, Some("Terminal"))]);
986        });
987
988        let (test, cx) = cx.add_window_view(|_, cx| CustomElement::new(cx));
989        let focus_handle = test.update(cx, |test, _| test.focus_handle.clone());
990
991        let pending_input_changed_count = Rc::new(RefCell::new(0usize));
992        let pending_input_changed_count_for_observer = pending_input_changed_count.clone();
993
994        struct PendingInputObserver {
995            _subscription: Subscription,
996        }
997
998        let _observer = cx.update(|window, cx| {
999            cx.new(|cx| PendingInputObserver {
1000                _subscription: cx.observe_pending_input(window, move |_, _, _| {
1001                    *pending_input_changed_count_for_observer.borrow_mut() += 1;
1002                }),
1003            })
1004        });
1005
1006        cx.update(|window, cx| {
1007            window.focus(&focus_handle, cx);
1008            window.activate_window();
1009        });
1010
1011        cx.simulate_keystrokes("ctrl-b");
1012
1013        let count_after_pending = Rc::new(RefCell::new(0usize));
1014        let count_after_pending_for_assertion = count_after_pending.clone();
1015
1016        cx.update(|window, cx| {
1017            assert!(window.has_pending_keystrokes());
1018            *count_after_pending.borrow_mut() = *pending_input_changed_count.borrow();
1019            assert!(*count_after_pending.borrow() > 0);
1020
1021            window.focus(&cx.focus_handle(), cx);
1022
1023            assert!(!window.has_pending_keystrokes());
1024        });
1025
1026        // Focus-triggered pending-input notifications are deferred to the end of the current
1027        // effect cycle, so the observer callback should run after the focus update completes.
1028        cx.update(|_, _| {
1029            let count_after_focus_change = *pending_input_changed_count.borrow();
1030            assert!(count_after_focus_change > *count_after_pending_for_assertion.borrow());
1031        });
1032    }
1033
1034    #[crate::test]
1035    fn test_input_handler_pending(cx: &mut TestAppContext) {
1036        #[derive(Clone)]
1037        struct CustomElement {
1038            focus_handle: FocusHandle,
1039            text: Rc<RefCell<String>>,
1040        }
1041        impl CustomElement {
1042            fn new(cx: &mut Context<Self>) -> Self {
1043                Self {
1044                    focus_handle: cx.focus_handle(),
1045                    text: Rc::default(),
1046                }
1047            }
1048        }
1049        impl Element for CustomElement {
1050            type RequestLayoutState = ();
1051
1052            type PrepaintState = ();
1053
1054            fn id(&self) -> Option<ElementId> {
1055                Some("custom".into())
1056            }
1057            fn source_location(&self) -> Option<&'static panic::Location<'static>> {
1058                None
1059            }
1060            fn request_layout(
1061                &mut self,
1062                _: Option<&GlobalElementId>,
1063                _: Option<&InspectorElementId>,
1064                window: &mut Window,
1065                cx: &mut App,
1066            ) -> (LayoutId, Self::RequestLayoutState) {
1067                (window.request_layout(Style::default(), [], cx), ())
1068            }
1069            fn prepaint(
1070                &mut self,
1071                _: Option<&GlobalElementId>,
1072                _: Option<&InspectorElementId>,
1073                _: Bounds<Pixels>,
1074                _: &mut Self::RequestLayoutState,
1075                window: &mut Window,
1076                cx: &mut App,
1077            ) -> Self::PrepaintState {
1078                window.set_focus_handle(&self.focus_handle, cx);
1079            }
1080            fn paint(
1081                &mut self,
1082                _: Option<&GlobalElementId>,
1083                _: Option<&InspectorElementId>,
1084                _: Bounds<Pixels>,
1085                _: &mut Self::RequestLayoutState,
1086                _: &mut Self::PrepaintState,
1087                window: &mut Window,
1088                cx: &mut App,
1089            ) {
1090                let mut key_context = KeyContext::default();
1091                key_context.add("Terminal");
1092                window.set_key_context(key_context);
1093                window.handle_input(&self.focus_handle, self.clone(), cx);
1094                window.on_action(std::any::TypeId::of::<TestAction>(), |_, _, _, _| {});
1095            }
1096        }
1097        impl IntoElement for CustomElement {
1098            type Element = Self;
1099
1100            fn into_element(self) -> Self::Element {
1101                self
1102            }
1103        }
1104
1105        impl InputHandler for CustomElement {
1106            fn selected_text_range(
1107                &mut self,
1108                _: bool,
1109                _: &mut Window,
1110                _: &mut App,
1111            ) -> Option<UTF16Selection> {
1112                None
1113            }
1114
1115            fn marked_text_range(&mut self, _: &mut Window, _: &mut App) -> Option<Range<usize>> {
1116                None
1117            }
1118
1119            fn text_for_range(
1120                &mut self,
1121                _: Range<usize>,
1122                _: &mut Option<Range<usize>>,
1123                _: &mut Window,
1124                _: &mut App,
1125            ) -> Option<String> {
1126                None
1127            }
1128
1129            fn replace_text_in_range(
1130                &mut self,
1131                replacement_range: Option<Range<usize>>,
1132                text: &str,
1133                _: &mut Window,
1134                _: &mut App,
1135            ) {
1136                if replacement_range.is_some() {
1137                    unimplemented!()
1138                }
1139                self.text.borrow_mut().push_str(text)
1140            }
1141
1142            fn replace_and_mark_text_in_range(
1143                &mut self,
1144                replacement_range: Option<Range<usize>>,
1145                new_text: &str,
1146                _: Option<Range<usize>>,
1147                _: &mut Window,
1148                _: &mut App,
1149            ) {
1150                if replacement_range.is_some() {
1151                    unimplemented!()
1152                }
1153                self.text.borrow_mut().push_str(new_text)
1154            }
1155
1156            fn unmark_text(&mut self, _: &mut Window, _: &mut App) {}
1157
1158            fn bounds_for_range(
1159                &mut self,
1160                _: Range<usize>,
1161                _: &mut Window,
1162                _: &mut App,
1163            ) -> Option<Bounds<Pixels>> {
1164                None
1165            }
1166
1167            fn character_index_for_point(
1168                &mut self,
1169                _: Point<Pixels>,
1170                _: &mut Window,
1171                _: &mut App,
1172            ) -> Option<usize> {
1173                None
1174            }
1175        }
1176        impl Render for CustomElement {
1177            fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
1178                self.clone()
1179            }
1180        }
1181
1182        cx.update(|cx| {
1183            cx.bind_keys([KeyBinding::new("ctrl-b", TestAction, Some("Terminal"))]);
1184            cx.bind_keys([KeyBinding::new("ctrl-b h", TestAction, Some("Terminal"))]);
1185        });
1186        let (test, cx) = cx.add_window_view(|_, cx| CustomElement::new(cx));
1187        let focus_handle = test.update(cx, |test, _| test.focus_handle.clone());
1188        cx.update(|window, cx| {
1189            window.focus(&focus_handle, cx);
1190            window.activate_window();
1191        });
1192        cx.simulate_keystrokes("ctrl-b [");
1193        test.update(cx, |test, _| assert_eq!(test.text.borrow().as_str(), "["))
1194    }
1195}
1196
Served at tenant.openagents/omega Member data and write actions are omitted.