1#![forbid(unsafe_code)]
2
3use web_time::{Duration, Instant};
52
53use crate::event::{KeyCode, KeyEvent, KeyEventKind, Modifiers};
54
55#[derive(Debug, Clone)]
61pub struct KeySequenceConfig {
62 pub sequence_timeout: Duration,
67
68 pub detect_double_escape: bool,
70}
71
72impl Default for KeySequenceConfig {
73 fn default() -> Self {
74 Self {
75 sequence_timeout: Duration::from_millis(250),
76 detect_double_escape: true,
77 }
78 }
79}
80
81impl KeySequenceConfig {
82 #[must_use]
84 pub fn with_timeout(timeout: Duration) -> Self {
85 Self {
86 sequence_timeout: timeout,
87 ..Default::default()
88 }
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
98pub enum KeySequenceKind {
99 DoubleEscape,
101}
102
103impl KeySequenceKind {
104 #[must_use]
106 pub const fn name(&self) -> &'static str {
107 match self {
108 Self::DoubleEscape => "Esc Esc",
109 }
110 }
111}
112
113#[derive(Debug, Clone, PartialEq)]
119pub enum KeySequenceAction {
120 Emit(KeyEvent),
122
123 EmitSequence {
125 kind: KeySequenceKind,
127 keys: Vec<KeyEvent>,
129 },
130
131 Pending,
136}
137
138impl KeySequenceAction {
139 #[must_use]
141 pub const fn is_pending(&self) -> bool {
142 matches!(self, Self::Pending)
143 }
144
145 #[must_use]
147 pub const fn is_sequence(&self) -> bool {
148 matches!(self, Self::EmitSequence { .. })
149 }
150}
151
152pub struct KeySequenceInterpreter {
161 config: KeySequenceConfig,
162
163 buffer: Vec<KeyEvent>,
165
166 buffer_start: Option<Instant>,
168}
169
170impl std::fmt::Debug for KeySequenceInterpreter {
171 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172 f.debug_struct("KeySequenceInterpreter")
173 .field("buffer_len", &self.buffer.len())
174 .field("has_pending", &self.buffer_start.is_some())
175 .finish()
176 }
177}
178
179impl KeySequenceInterpreter {
180 #[must_use]
182 pub fn new(config: KeySequenceConfig) -> Self {
183 Self {
184 config,
185 buffer: Vec::with_capacity(4),
186 buffer_start: None,
187 }
188 }
189
190 #[must_use]
192 pub fn with_defaults() -> Self {
193 Self::new(KeySequenceConfig::default())
194 }
195
196 pub fn feed(&mut self, event: &KeyEvent, now: Instant) -> KeySequenceAction {
215 if event.kind != KeyEventKind::Press {
217 return KeySequenceAction::Emit(*event);
218 }
219
220 match self.try_sequence(event, now) {
222 SequenceResult::Complete(kind) => {
223 self.buffer.push(*event);
225 let keys = std::mem::take(&mut self.buffer);
226 self.buffer_start = None;
227 KeySequenceAction::EmitSequence { kind, keys }
228 }
229 SequenceResult::Continue => {
230 if self.buffer.is_empty() {
231 self.buffer_start = Some(now);
232 }
233 self.buffer.push(*event);
234 KeySequenceAction::Pending
235 }
236 SequenceResult::NoMatch => {
237 if !self.buffer.is_empty() {
240 self.buffer.clear();
244 self.buffer_start = None;
245 }
246 KeySequenceAction::Emit(*event)
247 }
248 }
249 }
250
251 pub fn check_timeout(&mut self, now: Instant) -> Option<Vec<KeySequenceAction>> {
259 if let Some(start) = self.buffer_start {
260 if now.duration_since(start) >= self.config.sequence_timeout {
261 let actions: Vec<_> = self.buffer.drain(..).map(KeySequenceAction::Emit).collect();
263 self.buffer_start = None;
264 if actions.is_empty() {
265 None
266 } else {
267 Some(actions)
268 }
269 } else {
270 None
271 }
272 } else {
273 None
274 }
275 }
276
277 #[must_use]
279 pub fn has_pending(&self) -> bool {
280 self.buffer_start.is_some()
281 }
282
283 #[must_use]
287 pub fn time_until_timeout(&self, now: Instant) -> Option<Duration> {
288 self.buffer_start.map(|start| {
289 let elapsed = now.duration_since(start);
290 self.config.sequence_timeout.saturating_sub(elapsed)
291 })
292 }
293
294 pub fn reset(&mut self) {
296 self.buffer.clear();
297 self.buffer_start = None;
298 }
299
300 pub fn flush(&mut self) -> Vec<KeySequenceAction> {
305 let actions: Vec<_> = self.buffer.drain(..).map(KeySequenceAction::Emit).collect();
306 self.buffer_start = None;
307 actions
308 }
309
310 #[must_use]
312 pub fn config(&self) -> &KeySequenceConfig {
313 &self.config
314 }
315
316 pub fn set_config(&mut self, config: KeySequenceConfig) {
320 self.config = config;
321 }
322}
323
324#[derive(Debug, Clone, Copy)]
330enum SequenceResult {
331 Complete(KeySequenceKind),
333 Continue,
335 NoMatch,
337}
338
339impl KeySequenceInterpreter {
340 fn try_sequence(&self, event: &KeyEvent, _now: Instant) -> SequenceResult {
342 if self.config.detect_double_escape
344 && event.code == KeyCode::Escape
345 && event.modifiers == Modifiers::NONE
346 {
347 if self.buffer.len() == 1
349 && self.buffer[0].code == KeyCode::Escape
350 && self.buffer[0].modifiers == Modifiers::NONE
351 {
352 return SequenceResult::Complete(KeySequenceKind::DoubleEscape);
353 }
354 if self.buffer.is_empty() {
356 return SequenceResult::Continue;
357 }
358 }
359
360 SequenceResult::NoMatch
362 }
363}
364
365#[cfg(test)]
370mod tests {
371 use super::*;
372
373 fn now() -> Instant {
374 Instant::now()
375 }
376
377 fn esc() -> KeyEvent {
378 KeyEvent {
379 code: KeyCode::Escape,
380 modifiers: Modifiers::NONE,
381 kind: KeyEventKind::Press,
382 }
383 }
384
385 fn key(c: char) -> KeyEvent {
386 KeyEvent {
387 code: KeyCode::Char(c),
388 modifiers: Modifiers::NONE,
389 kind: KeyEventKind::Press,
390 }
391 }
392
393 fn key_release(c: char) -> KeyEvent {
394 KeyEvent {
395 code: KeyCode::Char(c),
396 modifiers: Modifiers::NONE,
397 kind: KeyEventKind::Release,
398 }
399 }
400
401 const MS_50: Duration = Duration::from_millis(50);
402 const MS_100: Duration = Duration::from_millis(100);
403 const MS_300: Duration = Duration::from_millis(300);
404
405 #[test]
408 fn double_escape_within_timeout() {
409 let mut interp = KeySequenceInterpreter::with_defaults();
410 let t = now();
411
412 let action = interp.feed(&esc(), t);
414 assert!(matches!(action, KeySequenceAction::Pending));
415 assert!(interp.has_pending());
416
417 let action = interp.feed(&esc(), t + MS_100);
419 assert!(matches!(
420 action,
421 KeySequenceAction::EmitSequence {
422 kind: KeySequenceKind::DoubleEscape,
423 ..
424 }
425 ));
426 assert!(!interp.has_pending());
427 }
428
429 #[test]
430 fn single_escape_timeout() {
431 let mut interp = KeySequenceInterpreter::with_defaults();
432 let t = now();
433
434 let action = interp.feed(&esc(), t);
436 assert!(matches!(action, KeySequenceAction::Pending));
437
438 let actions = interp.check_timeout(t + MS_300);
440 assert!(actions.is_some());
441 let actions = actions.unwrap();
442 assert_eq!(actions.len(), 1);
443 assert!(matches!(actions[0], KeySequenceAction::Emit(_)));
444 }
445
446 #[test]
447 fn escape_then_different_key() {
448 let mut interp = KeySequenceInterpreter::with_defaults();
449 let t = now();
450
451 let action = interp.feed(&esc(), t);
453 assert!(matches!(action, KeySequenceAction::Pending));
454
455 let action = interp.feed(&key('a'), t + MS_50);
457 assert!(matches!(action, KeySequenceAction::Emit(_)));
458 assert!(!interp.has_pending());
459 }
460
461 #[test]
462 fn non_escape_key_passes_through() {
463 let mut interp = KeySequenceInterpreter::with_defaults();
464 let t = now();
465
466 let action = interp.feed(&key('x'), t);
467 assert!(matches!(action, KeySequenceAction::Emit(_)));
468 assert!(!interp.has_pending());
469 }
470
471 #[test]
472 fn key_release_passes_through() {
473 let mut interp = KeySequenceInterpreter::with_defaults();
474 let t = now();
475
476 let action = interp.feed(&key_release('x'), t);
477 assert!(matches!(action, KeySequenceAction::Emit(_)));
478 assert!(!interp.has_pending());
479 }
480
481 #[test]
482 fn modified_escape_passes_through() {
483 let mut interp = KeySequenceInterpreter::with_defaults();
484 let t = now();
485
486 let ctrl_esc = KeyEvent {
488 code: KeyCode::Escape,
489 modifiers: Modifiers::CTRL,
490 kind: KeyEventKind::Press,
491 };
492
493 let action = interp.feed(&ctrl_esc, t);
494 assert!(matches!(action, KeySequenceAction::Emit(_)));
495 assert!(!interp.has_pending());
496 }
497
498 #[test]
501 fn custom_timeout() {
502 let config = KeySequenceConfig::with_timeout(Duration::from_millis(100));
503 let mut interp = KeySequenceInterpreter::new(config);
504 let t = now();
505
506 interp.feed(&esc(), t);
508 assert!(interp.has_pending());
509
510 assert!(interp.check_timeout(t + MS_50).is_none());
512
513 let actions = interp.check_timeout(t + Duration::from_millis(150));
515 assert!(actions.is_some());
516 }
517
518 #[test]
519 fn disabled_double_escape() {
520 let config = KeySequenceConfig {
521 detect_double_escape: false,
522 ..Default::default()
523 };
524 let mut interp = KeySequenceInterpreter::new(config);
525 let t = now();
526
527 let action = interp.feed(&esc(), t);
529 assert!(matches!(action, KeySequenceAction::Emit(_)));
530 assert!(!interp.has_pending());
531 }
532
533 #[test]
536 fn time_until_timeout() {
537 let mut interp = KeySequenceInterpreter::with_defaults();
538 let t = now();
539
540 assert!(interp.time_until_timeout(t).is_none());
541
542 interp.feed(&esc(), t);
543 let remaining = interp.time_until_timeout(t + MS_100);
544 assert!(remaining.is_some());
545 let remaining = remaining.unwrap();
546 assert!(remaining >= Duration::from_millis(140));
548 assert!(remaining <= Duration::from_millis(160));
549 }
550
551 #[test]
552 fn reset_clears_state() {
553 let mut interp = KeySequenceInterpreter::with_defaults();
554 let t = now();
555
556 interp.feed(&esc(), t);
557 assert!(interp.has_pending());
558
559 interp.reset();
560 assert!(!interp.has_pending());
561 }
562
563 #[test]
564 fn flush_returns_pending_keys() {
565 let mut interp = KeySequenceInterpreter::with_defaults();
566 let t = now();
567
568 interp.feed(&esc(), t);
569 assert!(interp.has_pending());
570
571 let actions = interp.flush();
572 assert_eq!(actions.len(), 1);
573 assert!(matches!(actions[0], KeySequenceAction::Emit(_)));
574 assert!(!interp.has_pending());
575 }
576
577 #[test]
578 fn flush_on_empty_returns_empty() {
579 let mut interp = KeySequenceInterpreter::with_defaults();
580 let actions = interp.flush();
581 assert!(actions.is_empty());
582 }
583
584 #[test]
585 fn config_getter_and_setter() {
586 let mut interp = KeySequenceInterpreter::with_defaults();
587
588 assert_eq!(interp.config().sequence_timeout, Duration::from_millis(250));
589
590 let new_config = KeySequenceConfig::with_timeout(Duration::from_millis(500));
591 interp.set_config(new_config);
592
593 assert_eq!(interp.config().sequence_timeout, Duration::from_millis(500));
594 }
595
596 #[test]
597 fn debug_format() {
598 let interp = KeySequenceInterpreter::with_defaults();
599 let dbg = format!("{:?}", interp);
600 assert!(dbg.contains("KeySequenceInterpreter"));
601 }
602
603 #[test]
606 fn action_is_pending() {
607 assert!(KeySequenceAction::Pending.is_pending());
608 assert!(!KeySequenceAction::Emit(esc()).is_pending());
609 assert!(
610 !KeySequenceAction::EmitSequence {
611 kind: KeySequenceKind::DoubleEscape,
612 keys: vec![],
613 }
614 .is_pending()
615 );
616 }
617
618 #[test]
619 fn action_is_sequence() {
620 assert!(!KeySequenceAction::Pending.is_sequence());
621 assert!(!KeySequenceAction::Emit(esc()).is_sequence());
622 assert!(
623 KeySequenceAction::EmitSequence {
624 kind: KeySequenceKind::DoubleEscape,
625 keys: vec![],
626 }
627 .is_sequence()
628 );
629 }
630
631 #[test]
634 fn sequence_kind_name() {
635 assert_eq!(KeySequenceKind::DoubleEscape.name(), "Esc Esc");
636 }
637
638 #[test]
641 fn default_config_values() {
642 let config = KeySequenceConfig::default();
643 assert_eq!(config.sequence_timeout, Duration::from_millis(250));
644 assert!(config.detect_double_escape);
645 }
646
647 #[test]
650 fn triple_escape_produces_sequence_then_pending() {
651 let mut interp = KeySequenceInterpreter::with_defaults();
652 let t = now();
653
654 let action = interp.feed(&esc(), t);
656 assert!(matches!(action, KeySequenceAction::Pending));
657
658 let action = interp.feed(&esc(), t + MS_50);
660 assert!(matches!(
661 action,
662 KeySequenceAction::EmitSequence {
663 kind: KeySequenceKind::DoubleEscape,
664 ..
665 }
666 ));
667
668 let action = interp.feed(&esc(), t + MS_100);
670 assert!(matches!(action, KeySequenceAction::Pending));
671 }
672
673 #[test]
674 fn sequence_keys_are_captured() {
675 let mut interp = KeySequenceInterpreter::with_defaults();
676 let t = now();
677
678 interp.feed(&esc(), t);
679 let action = interp.feed(&esc(), t + MS_50);
680
681 if let KeySequenceAction::EmitSequence { keys, kind } = action {
682 assert_eq!(keys.len(), 2, "Sequence should capture both keys");
684 assert_eq!(keys[0].code, KeyCode::Escape);
685 assert_eq!(keys[1].code, KeyCode::Escape);
686 assert_eq!(kind, KeySequenceKind::DoubleEscape);
687 } else {
688 panic!("Expected EmitSequence");
689 }
690 }
691
692 #[test]
693 fn rapid_non_escape_keys() {
694 let mut interp = KeySequenceInterpreter::with_defaults();
695 let t = now();
696
697 for (i, c) in "hello".chars().enumerate() {
699 let action = interp.feed(&key(c), t + Duration::from_millis(i as u64 * 10));
700 assert!(
701 matches!(action, KeySequenceAction::Emit(_)),
702 "Key '{}' should pass through",
703 c
704 );
705 }
706 assert!(!interp.has_pending());
707 }
708}