scarab_plugin_api/key_tables/
leader.rs1use super::{KeyCode, KeyCombo, KeyModifiers};
7use serde::{Deserialize, Serialize};
8use std::time::{Duration, Instant};
9
10#[derive(Clone, Debug)]
12pub struct LeaderKeyState {
13 pub key: KeyCombo,
15 is_active: bool,
17 activated_at: Option<Instant>,
19 timeout_ms: u64,
21 prefix_sequence: Vec<KeyCombo>,
23 sequence_progress: Vec<KeyCombo>,
25}
26
27impl LeaderKeyState {
28 pub fn new(key: KeyCombo, timeout_ms: u64) -> Self {
30 Self {
31 key,
32 is_active: false,
33 activated_at: None,
34 timeout_ms,
35 prefix_sequence: Vec::new(),
36 sequence_progress: Vec::new(),
37 }
38 }
39
40 pub fn with_sequence(sequence: Vec<KeyCombo>, timeout_ms: u64) -> Self {
42 let key = sequence
43 .first()
44 .cloned()
45 .unwrap_or_else(|| KeyCombo::new(KeyCode::KeyA, KeyModifiers::CTRL));
46
47 Self {
48 key,
49 is_active: false,
50 activated_at: None,
51 timeout_ms,
52 prefix_sequence: sequence,
53 sequence_progress: Vec::new(),
54 }
55 }
56
57 pub fn is_active(&self) -> bool {
59 self.is_active
60 }
61
62 pub fn timeout(&self) -> Duration {
64 Duration::from_millis(self.timeout_ms)
65 }
66
67 pub fn activate(&mut self) {
69 self.is_active = true;
70 self.activated_at = Some(Instant::now());
71 }
72
73 pub fn deactivate(&mut self) {
75 self.is_active = false;
76 self.activated_at = None;
77 self.sequence_progress.clear();
78 }
79
80 pub fn reset(&mut self) {
82 self.deactivate();
83 }
84
85 pub fn feed_key(&mut self, combo: &KeyCombo) -> bool {
89 if !self.prefix_sequence.is_empty() {
91 return self.feed_sequence_key(combo);
92 }
93
94 if combo == &self.key {
96 self.activate();
97 true
98 } else {
99 false
100 }
101 }
102
103 fn feed_sequence_key(&mut self, combo: &KeyCombo) -> bool {
105 self.sequence_progress.push(combo.clone());
107
108 if self.sequence_progress.len() <= self.prefix_sequence.len() {
110 let matches = self
111 .prefix_sequence
112 .iter()
113 .take(self.sequence_progress.len())
114 .zip(self.sequence_progress.iter())
115 .all(|(expected, actual)| expected == actual);
116
117 if !matches {
118 self.sequence_progress.clear();
120 return false;
121 }
122
123 if self.sequence_progress.len() == self.prefix_sequence.len() {
125 self.activate();
126 self.sequence_progress.clear();
127 return true;
128 }
129
130 false
132 } else {
133 self.sequence_progress.clear();
135 false
136 }
137 }
138
139 pub fn check_timeout(&mut self) -> bool {
143 if let Some(activated) = self.activated_at {
144 if activated.elapsed().as_millis() as u64 > self.timeout_ms {
145 self.deactivate();
146 return true;
147 }
148 }
149 false
150 }
151
152 pub fn time_remaining(&self) -> Option<Duration> {
154 if let Some(activated) = self.activated_at {
155 let elapsed = activated.elapsed();
156 let timeout = Duration::from_millis(self.timeout_ms);
157 timeout.checked_sub(elapsed)
158 } else {
159 None
160 }
161 }
162
163 pub fn timeout_at(&self) -> Option<Instant> {
165 self.activated_at
166 .map(|t| t + Duration::from_millis(self.timeout_ms))
167 }
168
169 pub fn is_sequence_in_progress(&self) -> bool {
171 !self.sequence_progress.is_empty()
172 }
173
174 pub fn sequence_progress(&self) -> &[KeyCombo] {
176 &self.sequence_progress
177 }
178
179 pub fn update_config(&mut self, key: KeyCombo, timeout_ms: u64) {
181 self.key = key;
182 self.timeout_ms = timeout_ms;
183 self.deactivate();
184 }
185}
186
187#[derive(Clone, Debug, Serialize, Deserialize)]
189pub struct LeaderKeyConfig {
190 pub key: KeyCombo,
192 pub timeout_ms: u64,
194 pub sequence: Option<Vec<KeyCombo>>,
196}
197
198impl Default for LeaderKeyConfig {
199 fn default() -> Self {
200 Self {
201 key: KeyCombo::new(KeyCode::KeyA, KeyModifiers::CTRL),
202 timeout_ms: 1000,
203 sequence: None,
204 }
205 }
206}
207
208impl From<LeaderKeyConfig> for LeaderKeyState {
209 fn from(config: LeaderKeyConfig) -> Self {
210 if let Some(sequence) = config.sequence {
211 LeaderKeyState::with_sequence(sequence, config.timeout_ms)
212 } else {
213 LeaderKeyState::new(config.key, config.timeout_ms)
214 }
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221
222 #[test]
223 fn test_leader_activation() {
224 let mut leader =
225 LeaderKeyState::new(KeyCombo::new(KeyCode::KeyA, KeyModifiers::CTRL), 1000);
226
227 assert!(!leader.is_active());
228
229 leader.activate();
230 assert!(leader.is_active());
231
232 leader.deactivate();
233 assert!(!leader.is_active());
234 }
235
236 #[test]
237 fn test_leader_timeout() {
238 let mut leader = LeaderKeyState::new(
239 KeyCombo::new(KeyCode::KeyA, KeyModifiers::CTRL),
240 100, );
242
243 leader.activate();
244 assert!(leader.is_active());
245
246 assert!(!leader.check_timeout());
248 assert!(leader.is_active());
249
250 std::thread::sleep(Duration::from_millis(150));
252
253 assert!(leader.check_timeout());
255 assert!(!leader.is_active());
256 }
257
258 #[test]
259 fn test_leader_feed_key() {
260 let mut leader =
261 LeaderKeyState::new(KeyCombo::new(KeyCode::KeyA, KeyModifiers::CTRL), 1000);
262
263 let correct_key = KeyCombo::new(KeyCode::KeyA, KeyModifiers::CTRL);
264 let wrong_key = KeyCombo::new(KeyCode::KeyB, KeyModifiers::CTRL);
265
266 assert!(leader.feed_key(&correct_key));
268 assert!(leader.is_active());
269
270 leader.deactivate();
271
272 assert!(!leader.feed_key(&wrong_key));
274 assert!(!leader.is_active());
275 }
276
277 #[test]
278 fn test_leader_sequence() {
279 let sequence = vec![
280 KeyCombo::new(KeyCode::KeyA, KeyModifiers::CTRL),
281 KeyCombo::new(KeyCode::KeyA, KeyModifiers::NONE),
282 ];
283
284 let mut leader = LeaderKeyState::with_sequence(sequence, 1000);
285
286 let key1 = KeyCombo::new(KeyCode::KeyA, KeyModifiers::CTRL);
288 assert!(!leader.feed_key(&key1)); assert!(!leader.is_active());
290
291 let key2 = KeyCombo::new(KeyCode::KeyA, KeyModifiers::NONE);
293 assert!(leader.feed_key(&key2));
294 assert!(leader.is_active());
295 }
296
297 #[test]
298 fn test_leader_sequence_mismatch() {
299 let sequence = vec![
300 KeyCombo::new(KeyCode::KeyA, KeyModifiers::CTRL),
301 KeyCombo::new(KeyCode::KeyA, KeyModifiers::NONE),
302 ];
303
304 let mut leader = LeaderKeyState::with_sequence(sequence, 1000);
305
306 let key1 = KeyCombo::new(KeyCode::KeyA, KeyModifiers::CTRL);
308 assert!(!leader.feed_key(&key1));
309
310 let key2 = KeyCombo::new(KeyCode::KeyB, KeyModifiers::NONE);
312 assert!(!leader.feed_key(&key2));
313 assert!(!leader.is_active());
314
315 assert!(!leader.is_sequence_in_progress());
317 }
318
319 #[test]
320 fn test_time_remaining() {
321 let mut leader =
322 LeaderKeyState::new(KeyCombo::new(KeyCode::KeyA, KeyModifiers::CTRL), 1000);
323
324 assert!(leader.time_remaining().is_none());
326
327 leader.activate();
328
329 let remaining = leader.time_remaining();
331 assert!(remaining.is_some());
332 assert!(remaining.unwrap().as_millis() > 0);
333 assert!(remaining.unwrap().as_millis() <= 1000);
334 }
335
336 #[test]
337 fn test_reset() {
338 let mut leader =
339 LeaderKeyState::new(KeyCombo::new(KeyCode::KeyA, KeyModifiers::CTRL), 1000);
340
341 leader.activate();
342 assert!(leader.is_active());
343
344 leader.reset();
345 assert!(!leader.is_active());
346 assert!(leader.activated_at.is_none());
347 }
348
349 #[test]
350 fn test_update_config() {
351 let mut leader =
352 LeaderKeyState::new(KeyCombo::new(KeyCode::KeyA, KeyModifiers::CTRL), 1000);
353
354 leader.activate();
355 assert!(leader.is_active());
356
357 let new_key = KeyCombo::new(KeyCode::KeyB, KeyModifiers::CTRL);
359 leader.update_config(new_key.clone(), 2000);
360
361 assert!(!leader.is_active());
362 assert_eq!(leader.key, new_key);
363 assert_eq!(leader.timeout_ms, 2000);
364 }
365
366 #[test]
367 fn test_leader_config_default() {
368 let config = LeaderKeyConfig::default();
369 assert_eq!(config.timeout_ms, 1000);
370 assert_eq!(config.key.key, KeyCode::KeyA);
371 assert!(config.key.mods.ctrl());
372 }
373
374 #[test]
375 fn test_leader_from_config() {
376 let config = LeaderKeyConfig {
377 key: KeyCombo::new(KeyCode::KeyB, KeyModifiers::ALT),
378 timeout_ms: 500,
379 sequence: None,
380 };
381
382 let leader: LeaderKeyState = config.into();
383 assert_eq!(leader.key.key, KeyCode::KeyB);
384 assert!(leader.key.mods.alt());
385 assert_eq!(leader.timeout_ms, 500);
386 }
387}