runa_tui/
keymap.rs

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}