1use std::collections::HashMap;
8
9use crate::commands::{
10 caret_left, caret_line_end, caret_line_start, caret_right, chain, delete_backward,
11 delete_forward, delete_selection, join_backward, join_forward, select_all, split_block,
12 Command, Dispatch,
13};
14use crate::state::EditorState;
15
16#[derive(Debug, Clone)]
18pub struct KeyPress {
19 pub key: String,
22 pub ctrl: bool,
24 pub alt: bool,
26 pub shift: bool,
28 pub meta: bool,
30}
31
32impl KeyPress {
33 pub fn key(name: &str) -> Self {
35 KeyPress {
36 key: name.to_string(),
37 ctrl: false,
38 alt: false,
39 shift: false,
40 meta: false,
41 }
42 }
43
44 pub fn ctrl(mut self) -> Self {
46 self.ctrl = true;
47 self
48 }
49 pub fn alt(mut self) -> Self {
51 self.alt = true;
52 self
53 }
54 pub fn shift(mut self) -> Self {
56 self.shift = true;
57 self
58 }
59 pub fn meta(mut self) -> Self {
61 self.meta = true;
62 self
63 }
64
65 fn canonical(&self) -> String {
66 let mut s = String::new();
67 if self.alt {
68 s.push_str("Alt-");
69 }
70 if self.ctrl {
71 s.push_str("Ctrl-");
72 }
73 if self.meta {
74 s.push_str("Meta-");
75 }
76 if self.shift {
77 s.push_str("Shift-");
78 }
79 s.push_str(&self.key);
80 s
81 }
82}
83
84pub struct Keymap {
86 mac: bool,
87 bindings: HashMap<String, Command>,
88}
89
90impl Keymap {
91 pub fn new(mac: bool, bindings: Vec<(&str, Command)>) -> Self {
95 let mut map = HashMap::new();
96 let mut km = Keymap {
97 mac,
98 bindings: HashMap::new(),
99 };
100 for (spec, cmd) in bindings {
101 map.insert(km.normalize_spec(spec), cmd);
102 }
103 km.bindings = map;
104 km
105 }
106
107 fn normalize_spec(&self, spec: &str) -> String {
108 let parts: Vec<&str> = spec.split('-').collect();
109 let (mods, key) = parts.split_at(parts.len() - 1);
110 let (mut alt, mut ctrl, mut shift, mut meta) = (false, false, false, false);
111 for m in mods {
112 match *m {
113 "Mod" => {
114 if self.mac {
115 meta = true;
116 } else {
117 ctrl = true;
118 }
119 }
120 "Cmd" | "Meta" => meta = true,
121 "Ctrl" | "Control" => ctrl = true,
122 "Alt" | "Option" => alt = true,
123 "Shift" => shift = true,
124 other => panic!("unknown key modifier `{other}`"),
125 }
126 }
127 KeyPress {
128 key: key[0].to_string(),
129 ctrl,
130 alt,
131 shift,
132 meta,
133 }
134 .canonical()
135 }
136
137 pub fn handle(
146 &self,
147 state: &EditorState,
148 press: &KeyPress,
149 mut dispatch: Option<&mut Dispatch<'_>>,
150 ) -> bool {
151 if let Some(cmd) = self.bindings.get(&press.canonical()) {
152 return cmd(state, dispatch.as_deref_mut());
153 }
154 if press.shift && shift_is_implicit(&press.key) {
155 let mut alt = press.clone();
156 alt.shift = false;
157 if let Some(cmd) = self.bindings.get(&alt.canonical()) {
158 return cmd(state, dispatch);
159 }
160 }
161 false
162 }
163
164 pub fn add(&mut self, spec: &str, command: Command) {
168 let canonical = self.normalize_spec(spec);
169 self.bindings.insert(canonical, command);
170 }
171
172 pub fn add_chained(&mut self, spec: &str, command: Command) {
180 let canonical = self.normalize_spec(spec);
181 match self.bindings.remove(&canonical) {
182 Some(existing) => {
183 self.bindings
184 .insert(canonical, chain(vec![command, existing]));
185 }
186 None => {
187 self.bindings.insert(canonical, command);
188 }
189 }
190 }
191
192 pub fn len(&self) -> usize {
194 self.bindings.len()
195 }
196
197 pub fn is_empty(&self) -> bool {
199 self.bindings.is_empty()
200 }
201}
202
203fn shift_is_implicit(key: &str) -> bool {
208 let mut chars = key.chars();
209 match (chars.next(), chars.next()) {
210 (Some(c), None) => !c.is_ascii_lowercase(),
211 _ => false,
212 }
213}
214
215pub fn base_keymap(mac: bool) -> Keymap {
219 let bindings: Vec<(&str, Command)> = vec![
220 ("Enter", Box::new(split_block)),
221 (
222 "Backspace",
223 chain(vec![
224 Box::new(delete_selection),
225 Box::new(join_backward),
226 Box::new(delete_backward),
227 ]),
228 ),
229 (
230 "Delete",
231 chain(vec![
232 Box::new(delete_selection),
233 Box::new(join_forward),
234 Box::new(delete_forward),
235 ]),
236 ),
237 ("Mod-a", Box::new(select_all)),
238 ("ArrowLeft", Box::new(caret_left)),
239 ("ArrowRight", Box::new(caret_right)),
240 ("Home", Box::new(caret_line_start)),
241 ("End", Box::new(caret_line_end)),
242 ];
243 Keymap::new(mac, bindings)
244}