Skip to main content

indigo_core/
key.rs

1use bitflags::bitflags;
2use std::{
3    fmt::{self, Display, Formatter},
4    hash::Hash,
5    str::FromStr,
6};
7use winnow::{
8    ascii::{multispace0, till_line_ending},
9    combinator::{alt, cut_err, delimited, fail, opt, preceded, repeat, terminated},
10    error::StrContext,
11    prelude::*,
12    token::one_of,
13};
14
15#[cfg(feature = "arbitrary")]
16use arbitrary::Arbitrary;
17
18#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
19#[derive(Clone, Debug)]
20pub struct Keys<K = Vec<Key>>(pub K);
21
22impl FromStr for Keys {
23    type Err = anyhow::Error;
24    fn from_str(s: &str) -> Result<Self, Self::Err> {
25        keys.parse(s).map_err(|e| anyhow::format_err!("{e}"))
26    }
27}
28
29impl<K: AsRef<[Key]>> Display for Keys<K> {
30    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
31        for key in self.0.as_ref() {
32            write!(f, "{key}")?;
33        }
34        Ok(())
35    }
36}
37
38fn comment(input: &mut &str) -> ModalResult<()> {
39    ('#', till_line_ending).void().parse_next(input)
40}
41
42fn not_key(input: &mut &str) -> ModalResult<()> {
43    multispace0.parse_next(input)?;
44    while comment.parse_next(input).is_ok() {
45        multispace0.parse_next(input)?;
46    }
47    Ok(())
48}
49
50fn keys(input: &mut &str) -> ModalResult<Keys> {
51    not_key.parse_next(input)?;
52    repeat(0.., terminated(key, not_key))
53        .map(Keys)
54        .parse_next(input)
55}
56
57#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
58#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
59pub struct Key {
60    pub modifiers: KeyModifiers,
61    pub code: KeyCode,
62}
63
64impl Key {
65    pub fn normalize(&mut self) {
66        if !self.modifiers.contains(KeyModifiers::SHIFT) {
67            return;
68        }
69        if let KeyCode::Char(c @ (b'a'..=b'z' | b'A'..=b'Z')) = self.code {
70            self.modifiers.remove(KeyModifiers::SHIFT);
71            self.code = KeyCode::Char(c.to_ascii_uppercase());
72        }
73    }
74}
75
76impl FromStr for Key {
77    type Err = anyhow::Error;
78    fn from_str(s: &str) -> Result<Self, Self::Err> {
79        key.parse(s).map_err(|e| anyhow::format_err!("{e}"))
80    }
81}
82
83impl Display for Key {
84    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
85        if self.modifiers.is_empty()
86            && let KeyCode::Char(_) = self.code
87        {
88            write!(f, "{}", self.code)?;
89        } else {
90            write!(f, "<{}{}>", self.modifiers, self.code)?;
91        }
92        Ok(())
93    }
94}
95
96fn key(input: &mut &str) -> ModalResult<Key> {
97    alt((key_wrapped, key_bare_unmodified)).parse_next(input)
98}
99
100fn key_wrapped(input: &mut &str) -> ModalResult<Key> {
101    let k = |input: &mut &str| {
102        let modifiers = key_modifiers
103            .context(StrContext::Label("key modifiers"))
104            .parse_next(input)?;
105        let allow_bare = !modifiers.is_empty();
106        let code = alt((key_code_wrapped, key_code_bare.verify(|_| allow_bare)))
107            .context(StrContext::Label("key code"))
108            .parse_next(input)?;
109        Ok(Key { modifiers, code })
110    };
111    delimited("<", cut_err(k), ">").parse_next(input)
112}
113
114fn key_bare_unmodified(input: &mut &str) -> ModalResult<Key> {
115    let modifiers = KeyModifiers::empty();
116    let code = key_code_bare.parse_next(input)?;
117    Ok(Key { modifiers, code })
118}
119
120bitflags! {
121    #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
122    #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
123    pub struct KeyModifiers: u8 {
124        const CONTROL = 1 << 0;
125        const ALT = 1 << 1;
126        const SHIFT = 1 << 2;
127    }
128}
129
130impl FromStr for KeyModifiers {
131    type Err = anyhow::Error;
132    fn from_str(s: &str) -> Result<Self, Self::Err> {
133        key_modifiers
134            .parse(s)
135            .map_err(|e| anyhow::format_err!("{e}"))
136    }
137}
138
139impl Display for KeyModifiers {
140    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
141        if self.contains(Self::CONTROL) {
142            write!(f, "c-")?;
143        }
144        if self.contains(Self::ALT) {
145            write!(f, "a-")?;
146        }
147        if self.contains(Self::SHIFT) {
148            write!(f, "s-")?;
149        }
150        Ok(())
151    }
152}
153
154fn key_modifiers(input: &mut &str) -> ModalResult<KeyModifiers> {
155    let mut modifiers = KeyModifiers::empty();
156    while !modifiers.is_all() {
157        let Some(modifier) = opt(alt((
158            "c-".value(KeyModifiers::CONTROL),
159            "a-".value(KeyModifiers::ALT),
160            "s-".value(KeyModifiers::SHIFT),
161        )))
162        .parse_next(input)?
163        else {
164            break;
165        };
166        if modifiers.contains(modifier) {
167            return fail.parse_next(input);
168        }
169        modifiers.insert(modifier);
170    }
171    Ok(modifiers)
172}
173
174#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
175pub enum KeyCode {
176    Backspace,
177    Delete,
178    Return,
179    Left,
180    Right,
181    Up,
182    Down,
183    Tab,
184    Escape,
185    // NOTE: Replace `u8` with `AsciiChar` once it stabilizes.
186    // https://github.com/rust-lang/rust/issues/110998
187    Char(u8),
188}
189
190#[cfg(feature = "arbitrary")]
191impl<'a> Arbitrary<'a> for KeyCode {
192    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
193        if u.ratio(8, 10)? {
194            loop {
195                let c = u.int_in_range(b' '..=b'~')?;
196                if !b" #<>\\".contains(&c) {
197                    return Ok(Self::Char(c));
198                }
199            }
200        }
201        Ok(*u.choose(&[
202            Self::Backspace,
203            Self::Delete,
204            Self::Return,
205            Self::Left,
206            Self::Right,
207            Self::Up,
208            Self::Down,
209            Self::Tab,
210            Self::Escape,
211        ])?)
212    }
213}
214
215impl FromStr for KeyCode {
216    type Err = anyhow::Error;
217    fn from_str(s: &str) -> Result<Self, Self::Err> {
218        key_code.parse(s).map_err(|e| anyhow::format_err!("{e}"))
219    }
220}
221
222impl Display for KeyCode {
223    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
224        match self {
225            Self::Backspace => write!(f, "bs")?,
226            Self::Delete => write!(f, "del")?,
227            Self::Return => write!(f, "ret")?,
228            Self::Left => write!(f, "left")?,
229            Self::Right => write!(f, "right")?,
230            Self::Up => write!(f, "up")?,
231            Self::Down => write!(f, "down")?,
232            Self::Tab => write!(f, "tab")?,
233            Self::Escape => write!(f, "esc")?,
234            Self::Char(b' ') => write!(f, "\\ ")?,
235            Self::Char(b'#') => write!(f, "\\#")?,
236            Self::Char(b'<') => write!(f, "\\<")?,
237            Self::Char(b'>') => write!(f, "\\>")?,
238            Self::Char(b'\\') => write!(f, "\\\\")?,
239            Self::Char(b'\t') => write!(f, "\\t")?,
240            Self::Char(b'\n') => write!(f, "\\n")?,
241            Self::Char(b'\r') => write!(f, "\\r")?,
242            Self::Char(c) => write!(f, "{}", char::from(*c))?,
243        }
244        Ok(())
245    }
246}
247
248fn key_code(input: &mut &str) -> ModalResult<KeyCode> {
249    alt((key_code_wrapped, key_code_bare)).parse_next(input)
250}
251
252fn key_code_wrapped(input: &mut &str) -> ModalResult<KeyCode> {
253    alt((
254        "bs".value(KeyCode::Backspace),
255        "del".value(KeyCode::Delete),
256        "ret".value(KeyCode::Return),
257        "left".value(KeyCode::Left),
258        "right".value(KeyCode::Right),
259        "up".value(KeyCode::Up),
260        "down".value(KeyCode::Down),
261        "tab".value(KeyCode::Tab),
262        "esc".value(KeyCode::Escape),
263    ))
264    .parse_next(input)
265}
266
267fn key_code_bare(input: &mut &str) -> ModalResult<KeyCode> {
268    alt((
269        one_of(b' '..=b'~')
270            .verify(|c| !" #<>\\".contains(*c))
271            .map(|c| KeyCode::Char(u8::try_from(c).expect("Only parsing ASCII"))),
272        key_code_escaped,
273    ))
274    .parse_next(input)
275}
276
277fn key_code_escaped(input: &mut &str) -> ModalResult<KeyCode> {
278    preceded(
279        '\\',
280        cut_err(alt((
281            ' '.value(KeyCode::Char(b' ')),
282            '#'.value(KeyCode::Char(b'#')),
283            '<'.value(KeyCode::Char(b'<')),
284            '>'.value(KeyCode::Char(b'>')),
285            '\\'.value(KeyCode::Char(b'\\')),
286            't'.value(KeyCode::Char(b'\t')),
287            'n'.value(KeyCode::Char(b'\n')),
288            'r'.value(KeyCode::Char(b'\r')),
289        ))),
290    )
291    .parse_next(input)
292}
293
294#[must_use]
295pub fn is(x: &Key, y: &str) -> bool {
296    let mut x = *x;
297    let mut y = y.parse::<Key>().unwrap();
298    x.normalize();
299    y.normalize();
300    x == y
301}
302
303#[cfg(test)]
304mod tests {
305    use super::*;
306
307    #[test]
308    fn random() {
309        let input = "
310            # comment
311            # comment
312            abc a b c # comment
313
314            \\t \\n <s-\\n> <c-->
315
316            # comment
317            <c-a-del> <a-a> <tab> <s-tab> <a-esc>
318        ";
319        let expected = "abcabc\\t\\n<s-\\n><c--><c-a-del><a-a><tab><s-tab><a-esc>";
320        let actual = input.parse::<Keys>().unwrap().to_string();
321        assert_eq!(expected, actual);
322    }
323
324    #[test]
325    fn weird() {
326        assert_eq!("<c-->".parse::<Key>().unwrap().to_string(), "<c-->");
327        assert_eq!("<c-\\>>".parse::<Key>().unwrap().to_string(), "<c-\\>>");
328    }
329
330    #[test]
331    fn escaped() {
332        assert_eq!("\\n".parse::<Key>().unwrap().to_string(), "\\n");
333        assert_eq!("\\t".parse::<Key>().unwrap().to_string(), "\\t");
334        assert_eq!("\\\\".parse::<Key>().unwrap().to_string(), "\\\\");
335    }
336
337    #[test]
338    fn no_wrapped_bare() {
339        assert_eq!("a".parse::<Key>().unwrap().to_string(), "a");
340        assert!("<a>".parse::<Key>().is_err());
341    }
342
343    #[test]
344    fn modifier_ordering() {
345        assert_eq!("<c-a-s-a>".parse::<Key>().unwrap().to_string(), "<c-a-s-a>");
346        assert_eq!("<s-c-a>".parse::<Key>().unwrap().to_string(), "<c-s-a>");
347        assert_eq!("<s-c-a-a>".parse::<Key>().unwrap().to_string(), "<c-a-s-a>");
348        assert_eq!("<a-s-c-a>".parse::<Key>().unwrap().to_string(), "<c-a-s-a>");
349    }
350
351    #[test]
352    fn no_duplicate_modifiers() {
353        assert!("<c-c-a>".parse::<Key>().is_err());
354        assert!("<s-c-a-c-a>".parse::<Key>().is_err());
355    }
356
357    #[test]
358    fn key_roundtrip() {
359        let mut codes: Vec<KeyCode> = (b' '..=b'~')
360            .filter(|c| !b" #<>\\".contains(c))
361            .map(KeyCode::Char)
362            .collect();
363        codes.extend([
364            KeyCode::Backspace,
365            KeyCode::Delete,
366            KeyCode::Return,
367            KeyCode::Left,
368            KeyCode::Right,
369            KeyCode::Up,
370            KeyCode::Down,
371            KeyCode::Tab,
372            KeyCode::Escape,
373        ]);
374        for bits in 0..=KeyModifiers::all().bits() {
375            let modifiers = KeyModifiers::from_bits_truncate(bits);
376            for &code in &codes {
377                let key = Key { modifiers, code };
378                match key.to_string().parse::<Key>() {
379                    Ok(parsed_key) => assert_eq!(key, parsed_key),
380                    Err(e) => panic!("Failed to parse `{key:?}` printed as `{key}`:\n{e}"),
381                }
382            }
383        }
384    }
385
386    #[test]
387    fn normalize() {
388        let cases = [
389            (KeyModifiers::SHIFT, b'a', KeyModifiers::empty(), b'A'),
390            (KeyModifiers::SHIFT, b'Z', KeyModifiers::empty(), b'Z'),
391            (KeyModifiers::empty(), b'a', KeyModifiers::empty(), b'a'),
392            (KeyModifiers::SHIFT, b'1', KeyModifiers::SHIFT, b'1'),
393        ];
394
395        for (input_modifiers, input_code, expected_modifiers, expected_code) in cases {
396            let mut key = Key {
397                modifiers: input_modifiers,
398                code: KeyCode::Char(input_code),
399            };
400            key.normalize();
401            assert_eq!(key.modifiers, expected_modifiers);
402            assert_eq!(key.code, KeyCode::Char(expected_code));
403        }
404    }
405}