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/// ```rust
9/// actions!(editor,[Undo, Redo]);;
10///
11/// impl Editor {
12/// fn undo(&mut self, _: &Undo, _cx: &mut ViewContext<Self>) { ... }
13/// fn redo(&mut self, _: &Redo, _cx: &mut ViewContext<Self>) { ... }
14/// }
15///
16/// impl Render for Editor {
17/// fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
18/// div()
19/// .track_focus(&self.focus_handle)
20/// .keymap_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.json).
31///
32/// ```rust
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, DispatchPhase, ElementContext, EntityId, FocusId, KeyBinding,
54 KeyContext, Keymap, KeymatchResult, Keystroke, KeystrokeMatcher, WindowContext,
55};
56use collections::FxHashMap;
57use smallvec::{smallvec, SmallVec};
58use std::{
59 any::{Any, TypeId},
60 cell::RefCell,
61 mem,
62 rc::Rc,
63};
64
65/// KeymatchMode controls how keybindings are resolved in the case of conflicting pending keystrokes.
66/// When `Sequenced`, gpui will wait for 1s for sequences to complete.
67/// When `Immediate`, gpui will immediately resolve the keybinding.
68#[derive(Default, PartialEq)]
69pub enum KeymatchMode {
70 #[default]
71 Sequenced,
72 Immediate,
73}
74
75#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
76pub(crate) struct DispatchNodeId(usize);
77
78pub(crate) struct DispatchTree {
79 node_stack: Vec<DispatchNodeId>,
80 pub(crate) context_stack: Vec<KeyContext>,
81 nodes: Vec<DispatchNode>,
82 focusable_node_ids: FxHashMap<FocusId, DispatchNodeId>,
83 view_node_ids: FxHashMap<EntityId, DispatchNodeId>,
84 keystroke_matchers: FxHashMap<SmallVec<[KeyContext; 4]>, KeystrokeMatcher>,
85 keymap: Rc<RefCell<Keymap>>,
86 action_registry: Rc<ActionRegistry>,
87 pub(crate) keymatch_mode: KeymatchMode,
88}
89
90#[derive(Default)]
91pub(crate) struct DispatchNode {
92 pub key_listeners: Vec<KeyListener>,
93 pub action_listeners: Vec<DispatchActionListener>,
94 pub context: Option<KeyContext>,
95 focus_id: Option<FocusId>,
96 view_id: Option<EntityId>,
97 parent: Option<DispatchNodeId>,
98}
99
100type KeyListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut ElementContext)>;
101
102#[derive(Clone)]
103pub(crate) struct DispatchActionListener {
104 pub(crate) action_type: TypeId,
105 pub(crate) listener: Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>,
106}
107
108impl DispatchTree {
109 pub fn new(keymap: Rc<RefCell<Keymap>>, action_registry: Rc<ActionRegistry>) -> Self {
110 Self {
111 node_stack: Vec::new(),
112 context_stack: Vec::new(),
113 nodes: Vec::new(),
114 focusable_node_ids: FxHashMap::default(),
115 view_node_ids: FxHashMap::default(),
116 keystroke_matchers: FxHashMap::default(),
117 keymap,
118 action_registry,
119 keymatch_mode: KeymatchMode::Sequenced,
120 }
121 }
122
123 pub fn clear(&mut self) {
124 self.node_stack.clear();
125 self.context_stack.clear();
126 self.nodes.clear();
127 self.focusable_node_ids.clear();
128 self.view_node_ids.clear();
129 self.keystroke_matchers.clear();
130 self.keymatch_mode = KeymatchMode::Sequenced;
131 }
132
133 pub fn push_node(
134 &mut self,
135 context: Option<KeyContext>,
136 focus_id: Option<FocusId>,
137 view_id: Option<EntityId>,
138 ) {
139 let parent = self.node_stack.last().copied();
140 let node_id = DispatchNodeId(self.nodes.len());
141 self.nodes.push(DispatchNode {
142 parent,
143 focus_id,
144 view_id,
145 ..Default::default()
146 });
147 self.node_stack.push(node_id);
148
149 if let Some(context) = context {
150 self.active_node().context = Some(context.clone());
151 self.context_stack.push(context);
152 }
153
154 if let Some(focus_id) = focus_id {
155 self.focusable_node_ids.insert(focus_id, node_id);
156 }
157
158 if let Some(view_id) = view_id {
159 self.view_node_ids.insert(view_id, node_id);
160 }
161 }
162
163 pub fn pop_node(&mut self) {
164 let node = &self.nodes[self.active_node_id().0];
165 if node.context.is_some() {
166 self.context_stack.pop();
167 }
168 self.node_stack.pop();
169 }
170
171 fn move_node(&mut self, source: &mut DispatchNode) {
172 self.push_node(source.context.take(), source.focus_id, source.view_id);
173 let target = self.active_node();
174 target.key_listeners = mem::take(&mut source.key_listeners);
175 target.action_listeners = mem::take(&mut source.action_listeners);
176 }
177
178 pub fn reuse_view(&mut self, view_id: EntityId, source: &mut Self) -> SmallVec<[EntityId; 8]> {
179 let view_source_node_id = source
180 .view_node_ids
181 .get(&view_id)
182 .expect("view should exist in previous dispatch tree");
183 let view_source_node = &mut source.nodes[view_source_node_id.0];
184 self.move_node(view_source_node);
185
186 let mut grafted_view_ids = smallvec![view_id];
187 let mut source_stack = vec![*view_source_node_id];
188 for (source_node_id, source_node) in source
189 .nodes
190 .iter_mut()
191 .enumerate()
192 .skip(view_source_node_id.0 + 1)
193 {
194 let source_node_id = DispatchNodeId(source_node_id);
195 while let Some(source_ancestor) = source_stack.last() {
196 if source_node.parent != Some(*source_ancestor) {
197 source_stack.pop();
198 self.pop_node();
199 } else {
200 break;
201 }
202 }
203
204 if source_stack.is_empty() {
205 break;
206 } else {
207 source_stack.push(source_node_id);
208 self.move_node(source_node);
209 if let Some(view_id) = source_node.view_id {
210 grafted_view_ids.push(view_id);
211 }
212 }
213 }
214
215 while !source_stack.is_empty() {
216 source_stack.pop();
217 self.pop_node();
218 }
219
220 grafted_view_ids
221 }
222
223 pub fn clear_pending_keystrokes(&mut self) {
224 self.keystroke_matchers.clear();
225 }
226
227 /// Preserve keystroke matchers from previous frames to support multi-stroke
228 /// bindings across multiple frames.
229 pub fn preserve_pending_keystrokes(&mut self, old_tree: &mut Self, focus_id: Option<FocusId>) {
230 if let Some(node_id) = focus_id.and_then(|focus_id| self.focusable_node_id(focus_id)) {
231 let dispatch_path = self.dispatch_path(node_id);
232
233 self.context_stack.clear();
234 for node_id in dispatch_path {
235 let node = self.node(node_id);
236 if let Some(context) = node.context.clone() {
237 self.context_stack.push(context);
238 }
239
240 if let Some((context_stack, matcher)) = old_tree
241 .keystroke_matchers
242 .remove_entry(self.context_stack.as_slice())
243 {
244 self.keystroke_matchers.insert(context_stack, matcher);
245 }
246 }
247 }
248 }
249
250 pub fn on_key_event(&mut self, listener: KeyListener) {
251 self.active_node().key_listeners.push(listener);
252 }
253
254 pub fn on_action(
255 &mut self,
256 action_type: TypeId,
257 listener: Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>,
258 ) {
259 self.active_node()
260 .action_listeners
261 .push(DispatchActionListener {
262 action_type,
263 listener,
264 });
265 }
266
267 pub fn focus_contains(&self, parent: FocusId, child: FocusId) -> bool {
268 if parent == child {
269 return true;
270 }
271
272 if let Some(parent_node_id) = self.focusable_node_ids.get(&parent) {
273 let mut current_node_id = self.focusable_node_ids.get(&child).copied();
274 while let Some(node_id) = current_node_id {
275 if node_id == *parent_node_id {
276 return true;
277 }
278 current_node_id = self.nodes[node_id.0].parent;
279 }
280 }
281 false
282 }
283
284 pub fn available_actions(&self, target: DispatchNodeId) -> Vec<Box<dyn Action>> {
285 let mut actions = Vec::<Box<dyn Action>>::new();
286 for node_id in self.dispatch_path(target) {
287 let node = &self.nodes[node_id.0];
288 for DispatchActionListener { action_type, .. } in &node.action_listeners {
289 if let Err(ix) = actions.binary_search_by_key(action_type, |a| a.as_any().type_id())
290 {
291 // Intentionally silence these errors without logging.
292 // If an action cannot be built by default, it's not available.
293 let action = self.action_registry.build_action_type(action_type).ok();
294 if let Some(action) = action {
295 actions.insert(ix, action);
296 }
297 }
298 }
299 }
300 actions
301 }
302
303 pub fn is_action_available(&self, action: &dyn Action, target: DispatchNodeId) -> bool {
304 for node_id in self.dispatch_path(target) {
305 let node = &self.nodes[node_id.0];
306 if node
307 .action_listeners
308 .iter()
309 .any(|listener| listener.action_type == action.as_any().type_id())
310 {
311 return true;
312 }
313 }
314 false
315 }
316
317 pub fn bindings_for_action(
318 &self,
319 action: &dyn Action,
320 context_stack: &Vec<KeyContext>,
321 ) -> Vec<KeyBinding> {
322 let keymap = self.keymap.borrow();
323 keymap
324 .bindings_for_action(action)
325 .filter(|binding| {
326 for i in 0..context_stack.len() {
327 let context = &context_stack[0..=i];
328 if keymap.binding_enabled(binding, context) {
329 return true;
330 }
331 }
332 false
333 })
334 .cloned()
335 .collect()
336 }
337
338 // dispatch_key pushses the next keystroke into any key binding matchers.
339 // any matching bindings are returned in the order that they should be dispatched:
340 // * First by length of binding (so if you have a binding for "b" and "ab", the "ab" binding fires first)
341 // * Secondly by depth in the tree (so if Editor has a binding for "b" and workspace a
342 // binding for "b", the Editor action fires first).
343 pub fn dispatch_key(
344 &mut self,
345 keystroke: &Keystroke,
346 dispatch_path: &SmallVec<[DispatchNodeId; 32]>,
347 ) -> KeymatchResult {
348 let mut bindings = SmallVec::<[KeyBinding; 1]>::new();
349 let mut pending = false;
350
351 let mut context_stack: SmallVec<[KeyContext; 4]> = SmallVec::new();
352 for node_id in dispatch_path {
353 let node = self.node(*node_id);
354
355 if let Some(context) = node.context.clone() {
356 context_stack.push(context);
357 }
358 }
359
360 while !context_stack.is_empty() {
361 let keystroke_matcher = self
362 .keystroke_matchers
363 .entry(context_stack.clone())
364 .or_insert_with(|| KeystrokeMatcher::new(self.keymap.clone()));
365
366 let result = keystroke_matcher.match_keystroke(keystroke, &context_stack);
367 pending = result.pending || pending;
368 for new_binding in result.bindings {
369 match bindings
370 .iter()
371 .position(|el| el.keystrokes.len() < new_binding.keystrokes.len())
372 {
373 Some(idx) => {
374 bindings.insert(idx, new_binding);
375 }
376 None => bindings.push(new_binding),
377 }
378 }
379 context_stack.pop();
380 }
381
382 KeymatchResult { bindings, pending }
383 }
384
385 pub fn has_pending_keystrokes(&self) -> bool {
386 self.keystroke_matchers
387 .iter()
388 .any(|(_, matcher)| matcher.has_pending_keystrokes())
389 }
390
391 pub fn dispatch_path(&self, target: DispatchNodeId) -> SmallVec<[DispatchNodeId; 32]> {
392 let mut dispatch_path: SmallVec<[DispatchNodeId; 32]> = SmallVec::new();
393 let mut current_node_id = Some(target);
394 while let Some(node_id) = current_node_id {
395 dispatch_path.push(node_id);
396 current_node_id = self.nodes[node_id.0].parent;
397 }
398 dispatch_path.reverse(); // Reverse the path so it goes from the root to the focused node.
399 dispatch_path
400 }
401
402 pub fn focus_path(&self, focus_id: FocusId) -> SmallVec<[FocusId; 8]> {
403 let mut focus_path: SmallVec<[FocusId; 8]> = SmallVec::new();
404 let mut current_node_id = self.focusable_node_ids.get(&focus_id).copied();
405 while let Some(node_id) = current_node_id {
406 let node = self.node(node_id);
407 if let Some(focus_id) = node.focus_id {
408 focus_path.push(focus_id);
409 }
410 current_node_id = node.parent;
411 }
412 focus_path.reverse(); // Reverse the path so it goes from the root to the focused node.
413 focus_path
414 }
415
416 pub fn view_path(&self, view_id: EntityId) -> SmallVec<[EntityId; 8]> {
417 let mut view_path: SmallVec<[EntityId; 8]> = SmallVec::new();
418 let mut current_node_id = self.view_node_ids.get(&view_id).copied();
419 while let Some(node_id) = current_node_id {
420 let node = self.node(node_id);
421 if let Some(view_id) = node.view_id {
422 view_path.push(view_id);
423 }
424 current_node_id = node.parent;
425 }
426 view_path.reverse(); // Reverse the path so it goes from the root to the view node.
427 view_path
428 }
429
430 pub fn node(&self, node_id: DispatchNodeId) -> &DispatchNode {
431 &self.nodes[node_id.0]
432 }
433
434 fn active_node(&mut self) -> &mut DispatchNode {
435 let active_node_id = self.active_node_id();
436 &mut self.nodes[active_node_id.0]
437 }
438
439 pub fn focusable_node_id(&self, target: FocusId) -> Option<DispatchNodeId> {
440 self.focusable_node_ids.get(&target).copied()
441 }
442
443 pub fn root_node_id(&self) -> DispatchNodeId {
444 debug_assert!(!self.nodes.is_empty());
445 DispatchNodeId(0)
446 }
447
448 fn active_node_id(&self) -> DispatchNodeId {
449 *self.node_stack.last().unwrap()
450 }
451}
452
453#[cfg(test)]
454mod tests {
455 use std::{cell::RefCell, rc::Rc};
456
457 use crate::{Action, ActionRegistry, DispatchTree, KeyBinding, KeyContext, Keymap};
458
459 #[derive(PartialEq, Eq)]
460 struct TestAction;
461
462 impl Action for TestAction {
463 fn name(&self) -> &'static str {
464 "test::TestAction"
465 }
466
467 fn debug_name() -> &'static str
468 where
469 Self: ::std::marker::Sized,
470 {
471 "test::TestAction"
472 }
473
474 fn partial_eq(&self, action: &dyn Action) -> bool {
475 action
476 .as_any()
477 .downcast_ref::<Self>()
478 .map_or(false, |a| self == a)
479 }
480
481 fn boxed_clone(&self) -> std::boxed::Box<dyn Action> {
482 Box::new(TestAction)
483 }
484
485 fn as_any(&self) -> &dyn ::std::any::Any {
486 self
487 }
488
489 fn build(_value: serde_json::Value) -> anyhow::Result<Box<dyn Action>>
490 where
491 Self: Sized,
492 {
493 Ok(Box::new(TestAction))
494 }
495 }
496
497 #[test]
498 fn test_keybinding_for_action_bounds() {
499 let keymap = Keymap::new(vec![KeyBinding::new(
500 "cmd-n",
501 TestAction,
502 Some("ProjectPanel"),
503 )]);
504
505 let mut registry = ActionRegistry::default();
506
507 registry.load_action::<TestAction>();
508
509 let keymap = Rc::new(RefCell::new(keymap));
510
511 let tree = DispatchTree::new(keymap, Rc::new(registry));
512
513 let contexts = vec![
514 KeyContext::parse("Workspace").unwrap(),
515 KeyContext::parse("ProjectPanel").unwrap(),
516 ];
517
518 let keybinding = tree.bindings_for_action(&TestAction, &contexts);
519
520 assert!(keybinding[0].action.partial_eq(&TestAction))
521 }
522}