lcode_config/
keymap.rs

1use std::{collections::HashSet, fmt, hash::Hash};
2
3use crossterm::event::KeyCode;
4use key_parse::keymap::*;
5use serde::{Deserialize, Serialize};
6
7// toml can't serialize enum
8macro_rules! actions {
9    ($( ($key:ident, $val:literal) ); *) => {
10        $(pub const $key: &str = $val;)*
11    };
12}
13actions!(
14    (PANEL_UP,          "panel_up");
15    (PANEL_DOWN,        "panel_down");
16    (PANEL_RIGHT,       "panel_right");
17    (PANEL_LEFT,        "panel_left");
18
19    (UP,                "up");
20    (DOWN,              "down");
21    (RIGHT,             "right");
22    (LEFT,              "left");
23
24    (TOGGLE_CURSOR,     "toggle");
25
26    (TOP,               "top");
27    (BOTTOM,            "bottom");
28
29    (REDRAW,            "redraw");
30    (EXIT,              "exit");
31
32    (EDIT_CODE_EDITOR,  "edit_code");
33    (EDIT_IN_TUI,       "edit_code_tui");
34
35    (TOGGLE_SUBMIT_RES, "toggle_submit_res");
36    (TOGGLE_TEST_RES,   "toggle_test_res");
37    (TOGGLE_MENU,       "toggle_menu");
38
39    (RE_QS_DETAIL,      "re_get_qs");
40
41    (NEXT_TAB,          "next_tab");
42    (PREV_TAB,          "prev_tab");
43
44    (HEAD,              "head");
45    (TAIL,              "tail");
46
47    (SYNC_INDEX,        "sync_index");
48
49    (ESCAPE,            "escape");
50
51    (ADD_TEST_CASE,     "add_test_case")
52);
53
54#[derive(Clone)]
55#[derive(Debug)]
56#[derive(Default)]
57#[derive(Eq)]
58#[derive(Serialize, Deserialize)]
59pub struct KeyMap {
60    pub keys: Keys,
61    pub action: String,
62    #[serde(default)]
63    pub desc: String,
64}
65
66impl fmt::Display for KeyMap {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        let res = toml::to_string(self).unwrap_or_else(|_| "unknown keymap\n\n".to_owned());
69        let mut a = res.split('\n');
70        format!(
71            "{:20}, {:30}, {}",
72            a.next().unwrap_or_default(),
73            a.next().unwrap_or_default(),
74            a.next().unwrap_or_default()
75        )
76        .fmt(f)
77    }
78}
79
80impl PartialEq for KeyMap {
81    fn eq(&self, other: &Self) -> bool {
82        self.action == other.action
83    }
84}
85
86impl Hash for KeyMap {
87    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
88        self.action.hash(state);
89    }
90}
91
92#[derive(Serialize, Deserialize)]
93#[derive(PartialEq, Eq)]
94#[derive(Clone)]
95#[derive(Debug)]
96pub struct TuiKeyMap {
97    #[serde(default)]
98    #[serde(rename = "keymap")]
99    pub map_set: HashSet<KeyMap>,
100}
101
102impl TuiKeyMap {
103    /// Add extra keymap
104    pub fn add_keymap(&mut self, add: HashSet<KeyMap>) {
105        for ele in add {
106            self.map_set.replace(ele);
107        }
108    }
109}
110
111macro_rules! keymaps {
112    ($( ($keys:expr, $action:expr, $desc:literal) ); *) => {
113        [
114            $(
115                KeyMap {
116                    keys:   Keys($keys),
117                    action: $action.to_owned(),
118                    desc:   $desc.to_owned(),
119                },
120            )*
121        ]
122    };
123}
124
125const fn kc(ch: char) -> KeyCode {
126    KeyCode::Char(ch)
127}
128
129impl Default for TuiKeyMap {
130    fn default() -> Self {
131        let (tab, backtab) = (KeyCode::Tab, KeyCode::BackTab);
132        let esc = KeyCode::Esc;
133        let enter = KeyCode::Enter;
134
135        let g = Key::new(NO_CONTROL, kc('g'));
136
137        let mps = keymaps!(
138            (vec![g, g],                          TOP,               "Go to top");
139            (vec![Key::new(SHIFT,      kc('G'))], BOTTOM,            "Go to bottom");
140
141            (vec![Key::new(SHIFT,      kc('H'))], HEAD,              "To the first column of the panel content.");
142            (vec![Key::new(SHIFT,      kc('L'))], TAIL,              "To the last column of the panel content.");
143
144            (vec![Key::new(NO_CONTROL, kc('h'))], LEFT,              "Panel content move left");
145            (vec![Key::new(NO_CONTROL, kc('j'))], DOWN,              "Panel content move down");
146            (vec![Key::new(NO_CONTROL, kc('k'))], UP,                "Panel content move up");
147            (vec![Key::new(NO_CONTROL, kc('l'))], RIGHT,             "Panel content move right");
148
149            (vec![Key::new(ALT,        kc('h'))], PANEL_LEFT,        "Switch to left panel(topic tags)");
150            (vec![Key::new(ALT,        kc('j'))], PANEL_DOWN,        "Switch to down panel(topic tags)");
151            (vec![Key::new(ALT,        kc('k'))], PANEL_UP,          "Switch to up panel(topic tags)");
152            (vec![Key::new(ALT,        kc('l'))], PANEL_RIGHT,       "Switch to right panel(topic tags)");
153
154            (vec![Key::new(NO_CONTROL,     tab)], NEXT_TAB,          "Next tab");
155            (vec![Key::new(SHIFT,      backtab)], PREV_TAB,          "Prev tab");
156
157            (vec![Key::new(NO_CONTROL, kc('o'))], EDIT_CODE_EDITOR,  "Edit cursor question(or current question) with your editor");
158            (vec![Key::new(NO_CONTROL, kc('e'))], EDIT_IN_TUI,       "Enter input block");
159
160            (vec![Key::new(NO_CONTROL,     esc)], ESCAPE,            "Close some float panel");
161            (vec![Key::new(CTRL,       kc('l'))], REDRAW,            "Redraw ui");
162            (vec![Key::new(CTRL,       kc('q'))], EXIT,              "Exit lcode");
163            (vec![Key::new(SHIFT,      kc('S'))], SYNC_INDEX,        "Sync question index (select tab and topic_tags tab)");
164            (vec![Key::new(CTRL,       kc('r'))], RE_QS_DETAIL,      "Re get question detail (reference tab0/select cursor question info)");
165
166            (vec![Key::new(CTRL,       kc('p'))], TOGGLE_MENU,       "Show or hide menu(only edit)");
167            (vec![Key::new(CTRL,       kc('t'))], TOGGLE_TEST_RES,   "Show or hide test result (only tab1/edit)");
168            (vec![Key::new(CTRL,       kc('s'))], TOGGLE_SUBMIT_RES, "Show or hide submit result (only tab1/edit)");
169            (vec![Key::new(NO_CONTROL,   enter)], TOGGLE_CURSOR,     "Trigger cursor item, in edit pop menu will active button (add or rm topic_tags, or goto tab1/edit)");
170
171            (vec![Key::new(NO_CONTROL, kc('a'))], ADD_TEST_CASE,     "When submit error can add test case. (tab1/edit)")
172
173        );
174        let keymap = HashSet::from(mps);
175
176        Self { map_set: keymap }
177    }
178}