1use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
9
10pub fn is_alt(key: &KeyEvent) -> bool {
16 key.modifiers
17 .intersects(KeyModifiers::ALT | KeyModifiers::META | KeyModifiers::SUPER)
18}
19
20fn is_ctrl(key: &KeyEvent) -> bool {
21 key.modifiers.contains(KeyModifiers::CONTROL)
22}
23
24pub fn is_word_left(key: &KeyEvent) -> bool {
26 match key.code {
27 KeyCode::Left => is_alt(key) || is_ctrl(key),
28 KeyCode::Char('b' | 'B') => is_alt(key) && !is_ctrl(key),
29 _ => false,
30 }
31}
32
33pub fn is_word_right(key: &KeyEvent) -> bool {
35 match key.code {
36 KeyCode::Right => is_alt(key) || is_ctrl(key),
37 KeyCode::Char('f' | 'F') => is_alt(key) && !is_ctrl(key),
38 _ => false,
39 }
40}
41
42pub fn is_delete_word_left(key: &KeyEvent) -> bool {
44 matches!(key.code, KeyCode::Backspace) && is_alt(key)
45}
46
47pub fn is_typed(key: &KeyEvent) -> bool {
49 !is_alt(key) && !is_ctrl(key)
50}
51
52pub fn word_left(chars: &[char], cursor: usize) -> usize {
54 let mut i = cursor.min(chars.len());
55 while i > 0 && chars[i - 1].is_whitespace() {
56 i -= 1;
57 }
58 while i > 0 && !chars[i - 1].is_whitespace() {
59 i -= 1;
60 }
61 i
62}
63
64pub fn word_right(chars: &[char], cursor: usize) -> usize {
66 let mut i = cursor.min(chars.len());
67 while i < chars.len() && chars[i].is_whitespace() {
68 i += 1;
69 }
70 while i < chars.len() && !chars[i].is_whitespace() {
71 i += 1;
72 }
73 i
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 fn chars(s: &str) -> Vec<char> {
81 s.chars().collect()
82 }
83
84 #[test]
85 fn crosses_one_word_at_a_time() {
86 let c = chars(" the payout failed");
87 assert_eq!(word_left(&c, c.len()), 13);
88 assert_eq!(word_left(&c, 13), 6);
89 assert_eq!(word_left(&c, 6), 2);
90 assert_eq!(word_left(&c, 2), 0);
91 assert_eq!(word_left(&c, 0), 0);
92
93 assert_eq!(word_right(&c, 0), 5);
94 assert_eq!(word_right(&c, 5), 12);
95 assert_eq!(word_right(&c, 12), c.len());
96 assert_eq!(word_right(&c, c.len()), c.len());
97 }
98
99 #[test]
100 fn answers_to_every_spelling_of_alt_arrow() {
101 let left = |m| KeyEvent::new(KeyCode::Left, m);
102 assert!(is_word_left(&left(KeyModifiers::ALT)));
103 assert!(is_word_left(&left(KeyModifiers::CONTROL)));
104 assert!(is_word_left(&left(KeyModifiers::SUPER)));
106 assert!(is_word_left(&left(KeyModifiers::META)));
107 assert!(is_word_left(&KeyEvent::new(
108 KeyCode::Char('b'),
109 KeyModifiers::ALT
110 )));
111 assert!(is_word_right(&KeyEvent::new(
112 KeyCode::Right,
113 KeyModifiers::ALT
114 )));
115 assert!(is_word_right(&KeyEvent::new(
116 KeyCode::Char('f'),
117 KeyModifiers::ALT
118 )));
119 assert!(is_delete_word_left(&KeyEvent::new(
120 KeyCode::Backspace,
121 KeyModifiers::ALT
122 )));
123
124 assert!(!is_word_left(&left(KeyModifiers::NONE)));
125 assert!(!is_word_right(&KeyEvent::new(
126 KeyCode::Char('f'),
127 KeyModifiers::NONE
128 )));
129 assert!(!is_delete_word_left(&KeyEvent::new(
130 KeyCode::Backspace,
131 KeyModifiers::NONE
132 )));
133 assert!(is_typed(&KeyEvent::new(
134 KeyCode::Char('f'),
135 KeyModifiers::NONE
136 )));
137 assert!(!is_typed(&KeyEvent::new(
138 KeyCode::Char('f'),
139 KeyModifiers::ALT
140 )));
141 }
142}