Krolyxon
Commit 2a1ff04

Branches

Tags

Refs →

bench51cratesbench51-guisrcvimkeys.rs

128 lines · 3.9 KB · Rust

1//! Keys as the Vim engine sees them, and their conversion from egui events.
2
3use eframe::egui::{Event, Key};
4
5#[derive(Clone, Debug, PartialEq, Eq)]
6pub enum VimKey {
7 Char(char),
8 Esc,
9 Enter,
10 Backspace,
11 Delete,
12 Tab,
13 Up,
14 Down,
15 Left,
16 Right,
17 Home,
18 End,
19 PageUp,
20 PageDown,
21 /// Ctrl plus a lowercase letter.
22 Ctrl(char),
23 /// Ctrl+V: the system clipboard's text.
24 Paste(String),
25 /// Ctrl+C.
26 Copy,
27 /// Ctrl+X.
28 Cut,
29}
30
31impl VimKey {
32 /// How the key is shown in the mode line while a command is pending.
33 pub fn label(&self) -> String {
34 match self {
35 VimKey::Char(c) => c.to_string(),
36 VimKey::Ctrl(c) => format!("^{}", c.to_ascii_uppercase()),
37 VimKey::Esc => "<Esc>".into(),
38 VimKey::Enter => "<CR>".into(),
39 other => format!("<{other:?}>"),
40 }
41 }
42}
43
44/// The keys in a keyboard event; text events can carry several chars.
45pub fn from_egui(event: &Event) -> Vec<VimKey> {
46 match event {
47 Event::Text(t) => t.chars().map(VimKey::Char).collect(),
48 Event::Paste(t) => vec![VimKey::Paste(t.clone())],
49 Event::Copy => vec![VimKey::Copy],
50 Event::Cut => vec![VimKey::Cut],
51 Event::Key {
52 key,
53 pressed: true,
54 modifiers,
55 ..
56 } => {
57 if modifiers.ctrl && !modifiers.alt {
58 let name = key.name();
59 if *key == Key::OpenBracket {
60 return vec![VimKey::Esc];
61 }
62 if name.len() == 1 && name.as_bytes()[0].is_ascii_alphabetic() {
63 return vec![VimKey::Ctrl(
64 name.to_ascii_lowercase().chars().next().unwrap(),
65 )];
66 }
67 return Vec::new();
68 }
69 let k = match key {
70 Key::Escape => VimKey::Esc,
71 Key::Enter => VimKey::Enter,
72 Key::Backspace => VimKey::Backspace,
73 Key::Delete => VimKey::Delete,
74 Key::Tab if !modifiers.shift => VimKey::Tab,
75 Key::ArrowUp => VimKey::Up,
76 Key::ArrowDown => VimKey::Down,
77 Key::ArrowLeft => VimKey::Left,
78 Key::ArrowRight => VimKey::Right,
79 Key::Home => VimKey::Home,
80 Key::End => VimKey::End,
81 Key::PageUp => VimKey::PageUp,
82 Key::PageDown => VimKey::PageDown,
83 // Printable keys arrive as `Event::Text`.
84 _ => return Vec::new(),
85 };
86 vec![k]
87 }
88 _ => Vec::new(),
89 }
90}
91
92/// Keys from a string, for tests and replays: `<Esc>`, `<CR>`, `<BS>`,
93/// `<C-r>` style names, everything else literally.
94#[cfg(test)]
95pub fn parse_keys(s: &str) -> Vec<VimKey> {
96 let mut out = Vec::new();
97 let mut rest = s;
98 while let Some(c) = rest.chars().next() {
99 if c == '<'
100 && let Some(end) = rest.find('>')
101 {
102 let name = &rest[1..end];
103 let k = match name {
104 "Esc" => Some(VimKey::Esc),
105 "CR" => Some(VimKey::Enter),
106 "BS" => Some(VimKey::Backspace),
107 "Del" => Some(VimKey::Delete),
108 "Tab" => Some(VimKey::Tab),
109 "Up" => Some(VimKey::Up),
110 "Down" => Some(VimKey::Down),
111 "Left" => Some(VimKey::Left),
112 "Right" => Some(VimKey::Right),
113 _ if name.starts_with("C-") && name.len() == 3 => {
114 Some(VimKey::Ctrl(name.chars().nth(2).unwrap()))
115 }
116 _ => None,
117 };
118 if let Some(k) = k {
119 out.push(k);
120 rest = &rest[end + 1..];
121 continue;
122 }
123 }
124 out.push(VimKey::Char(c));
125 rest = &rest[c.len_utf8()..];
126 }
127 out
128}