1//! Extension traits and helpers for GPUI keymap integration
2
3use gpui::{Action, KeyBinding};
4
5/// Helper to create a list of key bindings from action-keystroke pairs
6pub fn create_bindings<A: Action + Clone>(
7 mappings: &[(&str, A)],
8 context: Option<&str>,
9) -> Vec<KeyBinding> {
10 mappings
11 .iter()
12 .map(|(keystrokes, action)| KeyBinding::new(keystrokes, action.clone(), context))
13 .collect()
14}
15
16/// Helper to create a single binding
17pub fn bind<A: Action>(keystrokes: &str, action: A, context: Option<&str>) -> KeyBinding {
18 KeyBinding::new(keystrokes, action, context)
19}
20
21/// Helper to create multiple bindings with the same context
22pub struct BindingBuilder {
23 context: Option<String>,
24 bindings: Vec<KeyBinding>,
25}
26
27impl BindingBuilder {
28 /// Create a new binding builder
29 pub fn new() -> Self {
30 Self {
31 context: None,
32 bindings: Vec::new(),
33 }
34 }
35
36 /// Create a new binding builder with a context
37 pub fn with_context(context: impl Into<String>) -> Self {
38 Self {
39 context: Some(context.into()),
40 bindings: Vec::new(),
41 }
42 }
43
44 /// Add a binding to the builder
45 pub fn bind<A: Action>(mut self, keystrokes: &str, action: A) -> Self {
46 let binding = KeyBinding::new(keystrokes, action, self.context.as_deref());
47 self.bindings.push(binding);
48 self
49 }
50
51 /// Build the list of bindings
52 pub fn build(self) -> Vec<KeyBinding> {
53 self.bindings
54 }
55}
56
57impl Default for BindingBuilder {
58 fn default() -> Self {
59 Self::new()
60 }
61}
62
63/// Macro to simplify creating multiple bindings
64#[macro_export]
65macro_rules! bindings {
66 // With context
67 (context: $context:expr, $($key:expr => $action:expr),* $(,)?) => {
68 {
69 vec![
70 $(
71 $crate::keymap_ext::bind($key, $action, Some($context))
72 ),*
73 ]
74 }
75 };
76
77 // Without context
78 ($($key:expr => $action:expr),* $(,)?) => {
79 {
80 vec![
81 $(
82 $crate::keymap_ext::bind($key, $action, None)
83 ),*
84 ]
85 }
86 };
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use gpui::actions;
93
94 actions!(test, [TestAction, AnotherAction]);
95
96 #[test]
97 fn test_binding_builder() {
98 let bindings = BindingBuilder::new()
99 .bind("cmd-s", TestAction)
100 .bind("cmd-z", AnotherAction)
101 .build();
102
103 assert_eq!(bindings.len(), 2);
104 }
105
106 #[test]
107 fn test_binding_builder_with_context() {
108 let builder = BindingBuilder::with_context("Editor");
109 assert_eq!(builder.context, Some("Editor".to_string()));
110 }
111
112 #[test]
113 fn test_create_bindings() {
114 let mappings = [
115 ("cmd-s", TestAction),
116 ("cmd-z", TestAction),
117 ("cmd-y", TestAction),
118 ];
119
120 let bindings = create_bindings(&mappings, Some("Editor"));
121 assert_eq!(bindings.len(), 3);
122 }
123
124 #[test]
125 fn test_bind() {
126 let _binding = bind("cmd-s", TestAction, Some("Editor"));
127 // Just checking it compiles
128 }
129}