1use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
2use std::collections::HashMap;
3
4#[derive(Copy, Clone, Debug, PartialEq)]
5pub enum Action {
6 Nav(NavAction),
7 File(FileAction),
8 System(SystemAction),
9}
10
11#[derive(Copy, Clone, Debug, PartialEq)]
12pub enum NavAction {
13 GoParent,
14 GoIntoDir,
15 GoUp,
16 GoDown,
17 ToggleMarker,
18}
19
20#[derive(Copy, Clone, Debug, PartialEq)]
21pub enum FileAction {
22 Delete,
23 Copy,
24 Open,
25 Paste,
26 Rename,
27 Create,
28 CreateDirectory,
29 Filter,
30}
31
32#[derive(Copy, Clone, Debug, PartialEq)]
33pub enum SystemAction {
34 Quit,
35}
36
37#[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)]
38pub struct Key {
39 pub code: KeyCode,
40 pub modifiers: KeyModifiers,
41}
42
43pub struct Keymap {
44 map: HashMap<Key, Action>,
45}
46
47impl Keymap {
48 pub fn from_config(config: &crate::config::Config) -> Self {
49 let mut map = HashMap::new();
50 let keys = config.keys();
51
52 let parse_key = |s: &str| -> Option<Key> {
53 let mut modifiers = KeyModifiers::NONE;
54 let mut code: Option<KeyCode> = None;
55
56 for part in s.split('+') {
57 match part {
58 "Ctrl" | "Control" => modifiers |= KeyModifiers::CONTROL,
59 "Shift" => modifiers |= KeyModifiers::SHIFT,
60 "Alt" => modifiers |= KeyModifiers::ALT,
61
62 "Up" => code = Some(KeyCode::Up),
63 "Down" => code = Some(KeyCode::Down),
64 "Left" => code = Some(KeyCode::Left),
65 "Right" => code = Some(KeyCode::Right),
66 "Enter" => code = Some(KeyCode::Enter),
67 "Esc" => code = Some(KeyCode::Esc),
68 "Backspace" => code = Some(KeyCode::Backspace),
69 "Tab" => code = Some(KeyCode::Tab),
70
71 p if p.starts_with('F') => {
72 let n = p[1..].parse().ok()?;
73 code = Some(KeyCode::F(n));
74 }
75
76 p if p.len() == 1 => {
77 let mut char = p.chars().next()?;
78 if modifiers.contains(KeyModifiers::SHIFT) {
79 char = char.to_ascii_uppercase();
80 }
81 code = Some(KeyCode::Char(char));
82 }
83
84 _ => return None,
85 }
86 }
87
88 Some(Key {
89 code: code?,
90 modifiers,
91 })
92 };
93
94 let mut bind = |key_list: &[String], action: Action| {
95 for k in key_list {
96 if let Some(key) = parse_key(k) {
97 map.insert(key, action);
98 }
99 }
100 };
101
102 bind(keys.go_parent(), Action::Nav(NavAction::GoParent));
103 bind(keys.go_into_dir(), Action::Nav(NavAction::GoIntoDir));
104 bind(keys.go_up(), Action::Nav(NavAction::GoUp));
105 bind(keys.go_down(), Action::Nav(NavAction::GoDown));
106 bind(keys.toggle_marker(), Action::Nav(NavAction::ToggleMarker));
107 bind(keys.open_file(), Action::File(FileAction::Open));
108 bind(keys.delete(), Action::File(FileAction::Delete));
109 bind(keys.copy(), Action::File(FileAction::Copy));
110 bind(keys.paste(), Action::File(FileAction::Paste));
111 bind(keys.rename(), Action::File(FileAction::Rename));
112 bind(keys.create(), Action::File(FileAction::Create));
113 bind(
114 keys.create_directory(),
115 Action::File(FileAction::CreateDirectory),
116 );
117 bind(keys.filter(), Action::File(FileAction::Filter));
118 bind(keys.quit(), Action::System(SystemAction::Quit));
119
120 Keymap { map }
121 }
122
123 pub fn lookup(&self, key: KeyEvent) -> Option<Action> {
124 let k = Key {
125 code: key.code,
126 modifiers: key.modifiers,
127 };
128 self.map.get(&k).copied()
129 }
130}