1pub mod key;
2pub mod press;
3
4use std::{collections::HashMap, str::FromStr};
5
6use crate::reactive::RwSignal;
7use floem_editor_core::{
8 command::{EditCommand, MoveCommand, MultiSelectionCommand, ScrollCommand},
9 mode::Mode,
10};
11use floem_reactive::{SignalGet, SignalWith};
12use ui_events::keyboard::{Key, Modifiers};
13
14use super::{
15 Editor,
16 command::{Command, CommandExecuted},
17};
18
19#[derive(Clone, PartialEq, Eq, Hash)]
20pub struct KeypressKey {
21 pub key: Key,
22 pub modifiers: Modifiers,
23}
24
25#[derive(Clone)]
28pub struct KeypressMap {
29 pub keymaps: HashMap<KeypressKey, Command>,
30}
31impl KeypressMap {
32 pub fn default_windows() -> Self {
33 let mut keymaps = HashMap::new();
34 add_default_common(&mut keymaps);
35 add_default_windows(&mut keymaps);
36 Self { keymaps }
37 }
38
39 pub fn default_macos() -> Self {
40 let mut keymaps = HashMap::new();
41 add_default_common(&mut keymaps);
42 add_default_macos(&mut keymaps);
43 Self { keymaps }
44 }
45
46 pub fn default_linux() -> Self {
47 let mut keymaps = HashMap::new();
48 add_default_common(&mut keymaps);
49 add_default_linux(&mut keymaps);
50 Self { keymaps }
51 }
52
53 fn resolve_command(
59 &self,
60 editor: RwSignal<Editor>,
61 keypress: &KeypressKey,
62 ) -> Option<&Command> {
63 if let Some(cmd) = self.keymaps.get(keypress) {
64 return Some(cmd);
65 }
66
67 let mode = editor.get_untracked().cursor.get_untracked().get_mode();
68 if mode != Mode::Insert {
69 return None;
70 }
71
72 let mut unshifted = keypress.modifiers;
75 unshifted.set(Modifiers::SHIFT, false);
76 self.keymaps.get(&KeypressKey {
77 key: keypress.key.clone(),
78 modifiers: unshifted,
79 })
80 }
81
82 pub fn handle_keypress(
102 &self,
103 editor: RwSignal<Editor>,
104 keypress: &KeypressKey,
105 ) -> CommandExecuted {
106 let Some(command) = self.resolve_command(editor, keypress) else {
107 return CommandExecuted::No;
108 };
109
110 editor.with_untracked(|editor| {
111 editor
112 .doc()
113 .run_command(editor, command, Some(1), keypress.modifiers)
114 })
115 }
116}
117
118impl Default for KeypressMap {
119 fn default() -> Self {
120 match std::env::consts::OS {
121 "macos" => Self::default_macos(),
122 "windows" => Self::default_windows(),
123 _ => Self::default_linux(),
124 }
125 }
126}
127
128fn key(s: &str, m: Modifiers) -> KeypressKey {
129 KeypressKey {
130 key: Key::from_str(s).unwrap(),
131 modifiers: m,
132 }
133}
134
135fn key_d(s: &str) -> KeypressKey {
136 key(s, Modifiers::default())
137}
138
139fn add_default_common(c: &mut HashMap<KeypressKey, Command>) {
140 c.insert(
146 key("ArrowUp", Modifiers::ALT),
147 Command::Edit(EditCommand::MoveLineUp),
148 );
149 c.insert(
150 key("ArrowDown", Modifiers::ALT),
151 Command::Edit(EditCommand::MoveLineDown),
152 );
153
154 c.insert(key_d("Delete"), Command::Edit(EditCommand::DeleteForward));
155 c.insert(
156 key_d("Backspace"),
157 Command::Edit(EditCommand::DeleteBackward),
158 );
159 c.insert(
160 key("Backspace", Modifiers::SHIFT),
161 Command::Edit(EditCommand::DeleteForward),
162 );
163
164 c.insert(key_d("Home"), Command::Move(MoveCommand::LineStartNonBlank));
165 c.insert(key_d("End"), Command::Move(MoveCommand::LineEnd));
166
167 c.insert(key_d("PageUp"), Command::Scroll(ScrollCommand::PageUp));
168 c.insert(key_d("PageDown"), Command::Scroll(ScrollCommand::PageDown));
169 c.insert(
170 key("PageUp", Modifiers::CONTROL),
171 Command::Scroll(ScrollCommand::ScrollUp),
172 );
173 c.insert(
174 key("PageDown", Modifiers::CONTROL),
175 Command::Scroll(ScrollCommand::ScrollDown),
176 );
177
178 c.insert(
181 key("i", Modifiers::ALT | Modifiers::SHIFT),
182 Command::MultiSelection(MultiSelectionCommand::InsertCursorEndOfLine),
183 );
184
185 c.insert(key_d("ArrowRight"), Command::Move(MoveCommand::Right));
191 c.insert(key_d("ArrowLeft"), Command::Move(MoveCommand::Left));
192 c.insert(key_d("ArrowUp"), Command::Move(MoveCommand::Up));
193 c.insert(key_d("ArrowDown"), Command::Move(MoveCommand::Down));
194
195 c.insert(key_d("Enter"), Command::Edit(EditCommand::InsertNewLine));
196
197 c.insert(key_d("Tab"), Command::Edit(EditCommand::InsertTab));
198
199 c.insert(
200 key("ArrowUp", Modifiers::ALT | Modifiers::SHIFT),
201 Command::Edit(EditCommand::DuplicateLineUp),
202 );
203 c.insert(
204 key("ArrowDown", Modifiers::ALT | Modifiers::SHIFT),
205 Command::Edit(EditCommand::DuplicateLineDown),
206 );
207}
208
209fn add_default_windows(c: &mut HashMap<KeypressKey, Command>) {
210 add_default_nonmacos(c);
211}
212
213fn add_default_macos(c: &mut HashMap<KeypressKey, Command>) {
214 c.insert(key("z", Modifiers::META), Command::Edit(EditCommand::Undo));
219 c.insert(
220 key("z", Modifiers::META | Modifiers::SHIFT),
221 Command::Edit(EditCommand::Redo),
222 );
223 c.insert(key("y", Modifiers::META), Command::Edit(EditCommand::Redo));
224 c.insert(
225 key("x", Modifiers::META),
226 Command::Edit(EditCommand::ClipboardCut),
227 );
228 c.insert(
229 key("c", Modifiers::META),
230 Command::Edit(EditCommand::ClipboardCopy),
231 );
232 c.insert(
233 key("v", Modifiers::META),
234 Command::Edit(EditCommand::ClipboardPaste),
235 );
236
237 c.insert(
238 key("ArrowRight", Modifiers::ALT),
239 Command::Move(MoveCommand::WordEndForward),
240 );
241 c.insert(
242 key("ArrowLeft", Modifiers::ALT),
243 Command::Move(MoveCommand::WordBackward),
244 );
245 c.insert(
246 key("ArrowLeft", Modifiers::META),
247 Command::Move(MoveCommand::LineStartNonBlank),
248 );
249 c.insert(
250 key("ArrowRight", Modifiers::META),
251 Command::Move(MoveCommand::LineEnd),
252 );
253
254 c.insert(
255 key("a", Modifiers::CONTROL),
256 Command::Move(MoveCommand::LineStartNonBlank),
257 );
258 c.insert(
259 key("e", Modifiers::CONTROL),
260 Command::Move(MoveCommand::LineEnd),
261 );
262
263 c.insert(
264 key("k", Modifiers::META | Modifiers::SHIFT),
265 Command::Edit(EditCommand::DeleteLine),
266 );
267
268 c.insert(
269 key("Backspace", Modifiers::ALT),
270 Command::Edit(EditCommand::DeleteWordBackward),
271 );
272 c.insert(
273 key("Backspace", Modifiers::META),
274 Command::Edit(EditCommand::DeleteToBeginningOfLine),
275 );
276 c.insert(
277 key("k", Modifiers::CONTROL),
278 Command::Edit(EditCommand::DeleteToEndOfLine),
279 );
280 c.insert(
281 key("Delete", Modifiers::ALT),
282 Command::Edit(EditCommand::DeleteWordForward),
283 );
284
285 c.insert(
289 key("a", Modifiers::META),
290 Command::MultiSelection(MultiSelectionCommand::SelectAll),
291 );
292
293 c.insert(
294 key("Enter", Modifiers::META),
295 Command::Edit(EditCommand::NewLineBelow),
296 );
297 c.insert(
298 key("Enter", Modifiers::META | Modifiers::SHIFT),
299 Command::Edit(EditCommand::NewLineAbove),
300 );
301
302 c.insert(
304 key("ArrowUp", Modifiers::ALT | Modifiers::META),
305 Command::MultiSelection(MultiSelectionCommand::InsertCursorAbove),
306 );
307 c.insert(
308 key("ArrowDown", Modifiers::ALT | Modifiers::META),
309 Command::MultiSelection(MultiSelectionCommand::InsertCursorBelow),
310 );
311
312 c.insert(
313 key("l", Modifiers::META),
314 Command::MultiSelection(MultiSelectionCommand::SelectCurrentLine),
315 );
316 c.insert(
317 key("l", Modifiers::META | Modifiers::SHIFT),
318 Command::MultiSelection(MultiSelectionCommand::SelectAllCurrent),
319 );
320
321 c.insert(
322 key("u", Modifiers::META),
323 Command::MultiSelection(MultiSelectionCommand::SelectUndo),
324 );
325
326 c.insert(
328 key("ArrowUp", Modifiers::META),
329 Command::Move(MoveCommand::DocumentStart),
330 );
331 c.insert(
332 key("ArrowDown", Modifiers::META),
333 Command::Move(MoveCommand::DocumentEnd),
334 );
335}
336
337fn add_default_linux(c: &mut HashMap<KeypressKey, Command>) {
338 add_default_nonmacos(c);
339}
340
341fn add_default_nonmacos(c: &mut HashMap<KeypressKey, Command>) {
342 c.insert(
347 key("z", Modifiers::CONTROL),
348 Command::Edit(EditCommand::Undo),
349 );
350 c.insert(
351 key("z", Modifiers::CONTROL | Modifiers::SHIFT),
352 Command::Edit(EditCommand::Redo),
353 );
354 c.insert(
355 key("y", Modifiers::CONTROL),
356 Command::Edit(EditCommand::Redo),
357 );
358 c.insert(
359 key("x", Modifiers::CONTROL),
360 Command::Edit(EditCommand::ClipboardCut),
361 );
362 c.insert(
363 key("Delete", Modifiers::SHIFT),
364 Command::Edit(EditCommand::ClipboardCut),
365 );
366 c.insert(
367 key("c", Modifiers::CONTROL),
368 Command::Edit(EditCommand::ClipboardCopy),
369 );
370 c.insert(
371 key("Insert", Modifiers::CONTROL),
372 Command::Edit(EditCommand::ClipboardCopy),
373 );
374 c.insert(
375 key("v", Modifiers::CONTROL),
376 Command::Edit(EditCommand::ClipboardPaste),
377 );
378 c.insert(
379 key("Insert", Modifiers::SHIFT),
380 Command::Edit(EditCommand::ClipboardPaste),
381 );
382
383 c.insert(
384 key("ArrowRight", Modifiers::CONTROL),
385 Command::Move(MoveCommand::WordEndForward),
386 );
387 c.insert(
388 key("ArrowLeft", Modifiers::CONTROL),
389 Command::Move(MoveCommand::WordBackward),
390 );
391
392 c.insert(
393 key("Backspace", Modifiers::CONTROL),
394 Command::Edit(EditCommand::DeleteWordBackward),
395 );
396 c.insert(
397 key("Delete", Modifiers::CONTROL),
398 Command::Edit(EditCommand::DeleteWordForward),
399 );
400
401 c.insert(
406 key("a", Modifiers::CONTROL),
407 Command::MultiSelection(MultiSelectionCommand::SelectAll),
408 );
409
410 c.insert(
411 key("Enter", Modifiers::CONTROL),
412 Command::Edit(EditCommand::NewLineAbove),
413 );
414
415 c.insert(
417 key("ArrowUp", Modifiers::CONTROL | Modifiers::ALT),
418 Command::MultiSelection(MultiSelectionCommand::InsertCursorAbove),
419 );
420 c.insert(
421 key("ArrowDown", Modifiers::CONTROL | Modifiers::ALT),
422 Command::MultiSelection(MultiSelectionCommand::InsertCursorBelow),
423 );
424
425 c.insert(
426 key("l", Modifiers::CONTROL),
427 Command::MultiSelection(MultiSelectionCommand::SelectCurrentLine),
428 );
429 c.insert(
430 key("l", Modifiers::CONTROL | Modifiers::SHIFT),
431 Command::MultiSelection(MultiSelectionCommand::SelectAllCurrent),
432 );
433
434 c.insert(
435 key("u", Modifiers::CONTROL),
436 Command::MultiSelection(MultiSelectionCommand::SelectUndo),
437 );
438
439 c.insert(
441 key("Home", Modifiers::CONTROL),
442 Command::Move(MoveCommand::DocumentStart),
443 );
444 c.insert(
445 key("End", Modifiers::CONTROL),
446 Command::Move(MoveCommand::DocumentEnd),
447 );
448}
449
450pub fn default_key_handler(
457 editor: RwSignal<Editor>,
458) -> impl Fn(KeypressKey) -> CommandExecuted + 'static {
459 let keypress_map = KeypressMap::default();
460 move |keypress| keypress_map.handle_keypress(editor, &keypress)
461}