1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
use crossterm::input::KeyEvent;
use regex::Regex;
use crate::errors::ConfError;
/// what's needed to handle a verb
#[derive(Debug)]
pub struct VerbConf {
pub shortcut: Option<String>,
pub invocation: String,
pub key: Option<KeyEvent>,
pub execution: String,
pub description: Option<String>,
pub from_shell: Option<bool>,
pub leave_broot: Option<bool>,
pub confirm: Option<bool>,
}
fn bad_key(raw: &str) -> Result<KeyEvent, ConfError> {
Err(ConfError::InvalidKey {
raw: raw.to_owned(),
})
}
/// parse a string as a keyboard key definition.
///
/// Note that some mappings allowed by crossterm aren't
/// parsed because we don't want to let the user override
/// the related behaviors.
pub fn parse_key(raw: &str) -> Result<KeyEvent, ConfError> {
let key_regex = regex!(
r"(?x)
^
(?P<major>([a-zA-Z]+|\^))
(?:\W?(?P<minor>\w\d?)\W?)?
$
"
);
match key_regex.captures(raw) {
Some(c) => Ok(
match (
c.name("major")
.unwrap()
.as_str()
.to_ascii_lowercase()
.as_ref(),
c.name("minor"),
) {
("left", None) => KeyEvent::Left,
("right", None) => KeyEvent::Right,
("up", None) => KeyEvent::Up,
("down", None) => KeyEvent::Down,
("home", None) => KeyEvent::Home,
("end", None) => KeyEvent::End,
("pageup", None) => KeyEvent::PageUp,
("pagedown", None) => KeyEvent::PageDown,
("backtab", None) => KeyEvent::BackTab,
("delete", None) => KeyEvent::Delete,
("insert", None) => KeyEvent::Insert,
("f", Some(minor)) => match minor.as_str().parse() {
Ok(digit) => KeyEvent::F(digit),
_ => bad_key(raw)?,
},
("alt", Some(minor)) => {
KeyEvent::Alt(minor.as_str().chars().next().unwrap().to_ascii_lowercase())
}
("ctrl", Some(minor)) | ("^", Some(minor)) => {
KeyEvent::Ctrl(minor.as_str().chars().next().unwrap().to_ascii_lowercase())
}
// other possible mappings are disabled as they would break basic behaviors of broot
_ => bad_key(raw)?,
},
),
None => bad_key(raw),
}
}
#[cfg(test)]
mod key_parsing_tests {
use crossterm::input::KeyEvent::*;
use crate::verb_conf::*;
fn check_ok(raw: &str, key: KeyEvent) {
let parsed = parse_key(raw);
assert!(parsed.is_ok(), "failed to parse {:?} as key", raw);
assert_eq!(parsed.unwrap(), key);
}
#[test]
fn check_key_parsing() {
check_ok("left", Left);
check_ok("right", Right);
check_ok("Home", Home);
check_ok("f1", F(1));
check_ok("F2", F(2));
check_ok("F-3", F(3));
check_ok("F(4)", F(4));
check_ok("F 12", F(12));
check_ok("F(12)", F(12));
assert!(parse_key("F(a)").is_err(), "should not have parsed F(a)");
check_ok("Up", Up);
check_ok("down", Down);
check_ok("insert", Insert);
check_ok("alt(4)", Alt('4'));
check_ok("alt-D", Alt('d'));
check_ok("ctrl-q", Ctrl('q'));
check_ok("ctrl-Q", Ctrl('q'));
check_ok("ctrl Q", Ctrl('q'));
check_ok("^Q", Ctrl('q'));
}
}
|