gpuikit_keymap/
extensions.rs

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}