brush_parser/
readline_binding.rs1use crate::error;
4
5#[derive(Debug, Clone, PartialEq)]
7pub struct KeySequenceBinding {
8 pub seq: KeySequence,
10 pub command: String,
12}
13
14#[derive(Debug, Clone, PartialEq)]
16pub struct KeySequence(pub Vec<KeySequenceItem>);
17
18#[derive(Debug, Clone, PartialEq)]
20pub enum KeySequenceItem {
21 Control,
23 Meta,
25 Byte(u8),
27}
28
29#[derive(Debug, Default, Clone, PartialEq)]
31pub struct KeyStroke {
32 pub meta: bool,
34 pub control: bool,
36 pub key_code: Vec<u8>,
38}
39
40pub fn parse_key_sequence_binding(
46 input: &str,
47) -> Result<KeySequenceBinding, error::BindingParseError> {
48 readline_binding::key_sequence_binding(input)
49 .map_err(|_err| error::BindingParseError::Unknown(input.to_owned()))
50}
51
52pub fn key_sequence_to_strokes(
58 seq: &KeySequence,
59) -> Result<Vec<KeyStroke>, error::BindingParseError> {
60 let mut strokes = vec![];
61 let mut current_stroke = KeyStroke::default();
62
63 for item in &seq.0 {
64 if matches!(item, KeySequenceItem::Control | KeySequenceItem::Meta)
65 && !current_stroke.key_code.is_empty()
66 {
67 strokes.push(current_stroke);
68 current_stroke = KeyStroke::default();
69 }
70
71 match item {
72 KeySequenceItem::Control => current_stroke.control = true,
73 KeySequenceItem::Meta => current_stroke.meta = true,
74 KeySequenceItem::Byte(b) => current_stroke.key_code.push(*b),
75 }
76 }
77
78 if current_stroke.key_code.is_empty() {
79 if current_stroke.control || current_stroke.meta {
80 return Err(error::BindingParseError::MissingKeyCode);
81 }
82 } else {
83 strokes.push(current_stroke);
84 }
85
86 Ok(strokes)
87}
88
89peg::parser! {
90 grammar readline_binding() for str {
91 rule _() = [' ' | '\t' | '\n']*
92
93 pub rule key_sequence_binding() -> KeySequenceBinding =
94 _ "\"" seq:key_sequence() "\"" _ ":" _ command:cmd() _ { KeySequenceBinding { seq, command } }
95
96 rule cmd() -> String = s:$([_]*) { s.to_string() }
97
98 rule key_sequence() -> KeySequence =
100 items:key_sequence_item()* { KeySequence(items) }
101
102 rule key_sequence_item() -> KeySequenceItem =
103 "\\C-" { KeySequenceItem::Control } /
104 "\\M-" { KeySequenceItem::Meta } /
105 "\\e" { KeySequenceItem::Byte(b'\x1b') } /
106 "\\\\" { KeySequenceItem::Byte(b'\\') } /
107 "\\\"" { KeySequenceItem::Byte(b'"') } /
108 "\\'" { KeySequenceItem::Byte(b'\'') } /
109 "\\a" { KeySequenceItem::Byte(b'\x07') } /
110 "\\b" { KeySequenceItem::Byte(b'\x08') } /
111 "\\d" { KeySequenceItem::Byte(b'\x7f') } /
112 "\\f" { KeySequenceItem::Byte(b'\x0c') } /
113 "\\n" { KeySequenceItem::Byte(b'\n') } /
114 "\\r" { KeySequenceItem::Byte(b'\r') } /
115 "\\t" { KeySequenceItem::Byte(b'\t') } /
116 "\\v" { KeySequenceItem::Byte(b'\x0b') } /
117 "\\" n:octal_number() { KeySequenceItem::Byte(n) } /
118 "\\" n:hex_number() { KeySequenceItem::Byte(n) } /
119 [c if c != '"'] { KeySequenceItem::Byte(c as u8) }
120
121 rule octal_number() -> u8 =
122 s:$(['0'..='7']*<1,3>) {? u8::from_str_radix(s, 8).or(Err("invalid octal number")) }
123
124 rule hex_number() -> u8 =
125 s:$(['0'..='9' | 'a'..='f' | 'A'..='F']*<1,2>) {? u8::from_str_radix(s, 16).or(Err("invalid hex number")) }
126 }
127}
128
129#[cfg(test)]
130#[allow(clippy::unnecessary_wraps)]
131#[allow(clippy::panic_in_result_fn)]
132mod tests {
133 use super::*;
134 use anyhow::Result;
135
136 #[test]
137 fn test_basic_parse() -> Result<()> {
138 let binding = parse_key_sequence_binding(r#""\C-k": xyz"#)?;
139 assert_eq!(
140 binding.seq.0,
141 [KeySequenceItem::Control, KeySequenceItem::Byte(b'k')]
142 );
143 assert_eq!(binding.command, "xyz");
144
145 Ok(())
146 }
147}