par_term_keybindings/
lib.rs1mod matcher;
12pub mod parser;
13
14pub use matcher::KeybindingMatcher;
15pub use parser::KeyCombo;
16#[allow(unused_imports)]
18pub use parser::ParseError;
19pub use parser::{key_combo_to_bytes, parse_key_sequence};
20
21use par_term_config::{KeyBinding, ModifierRemapping};
22use std::collections::HashMap;
23
24#[derive(Debug, Default)]
26pub struct KeybindingRegistry {
27 bindings: HashMap<KeyCombo, String>,
29}
30
31impl KeybindingRegistry {
32 pub fn new() -> Self {
34 Self::default()
35 }
36
37 pub fn from_config(keybindings: &[KeyBinding]) -> Self {
41 let mut registry = Self::new();
42
43 log::info!(
44 "Building keybinding registry from {} config keybindings",
45 keybindings.len()
46 );
47 for binding in keybindings {
48 match parser::parse_key_combo(&binding.key) {
49 Ok(combo) => {
50 log::info!(
51 "Registered keybinding: {} -> {} (parsed as: {:?})",
52 binding.key,
53 binding.action,
54 combo
55 );
56 registry.bindings.insert(combo, binding.action.clone());
57 }
58 Err(e) => {
59 log::warn!(
60 "Invalid keybinding '{}' for action '{}': {}",
61 binding.key,
62 binding.action,
63 e
64 );
65 }
66 }
67 }
68
69 log::info!(
70 "Keybinding registry initialized with {} bindings",
71 registry.bindings.len()
72 );
73 registry
74 }
75
76 pub fn lookup(
80 &self,
81 event: &winit::event::KeyEvent,
82 modifiers: &winit::event::Modifiers,
83 ) -> Option<&str> {
84 self.lookup_with_options(event, modifiers, &ModifierRemapping::default(), false)
85 }
86
87 pub fn lookup_with_options(
98 &self,
99 event: &winit::event::KeyEvent,
100 modifiers: &winit::event::Modifiers,
101 remapping: &ModifierRemapping,
102 use_physical_keys: bool,
103 ) -> Option<&str> {
104 let matcher = KeybindingMatcher::from_event_with_remapping(event, modifiers, remapping);
105
106 for (combo, action) in &self.bindings {
107 if matcher.matches_with_physical_preference(combo, use_physical_keys) {
108 return Some(action.as_str());
109 }
110 }
111
112 None
113 }
114
115 #[allow(dead_code)]
117 pub fn is_empty(&self) -> bool {
118 self.bindings.is_empty()
119 }
120
121 #[allow(dead_code)]
123 pub fn len(&self) -> usize {
124 self.bindings.len()
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn test_empty_registry() {
134 let registry = KeybindingRegistry::new();
135 assert!(registry.is_empty());
136 assert_eq!(registry.len(), 0);
137 }
138
139 #[test]
140 fn test_from_config() {
141 let bindings = vec![
142 KeyBinding {
143 key: "Ctrl+Shift+B".to_string(),
144 action: "toggle_background_shader".to_string(),
145 },
146 KeyBinding {
147 key: "Ctrl+Shift+U".to_string(),
148 action: "toggle_cursor_shader".to_string(),
149 },
150 ];
151
152 let registry = KeybindingRegistry::from_config(&bindings);
153 assert_eq!(registry.len(), 2);
154 }
155
156 #[test]
157 fn test_invalid_keybinding_skipped() {
158 let bindings = vec![
159 KeyBinding {
160 key: "InvalidKey".to_string(),
161 action: "some_action".to_string(),
162 },
163 KeyBinding {
164 key: "Ctrl+A".to_string(),
165 action: "valid_action".to_string(),
166 },
167 ];
168
169 let registry = KeybindingRegistry::from_config(&bindings);
170 assert_eq!(registry.len(), 1);
172 }
173}