Krolyxon
Commit 92928dd

Branches

Tags

Refs →

remagesrcinputkeymap.rs

436 lines · 14.3 KB · Rust

1//! Per-mode key trie: pending sequences, counts and which-key candidates.
2
3use std::collections::BTreeMap;
4
5use super::keys::{Key, KeySeq};
6use crate::action::Action;
7use crate::state::Mode;
8
9/// Bindings from one config layer, in file order. `None` is `"none"`: remove the binding.
10pub type Bindings = Vec<(KeySeq, Option<Vec<Action>>)>;
11
12/// One layer's `[keys.*]` tables.
13#[derive(Clone, Debug, Default, PartialEq)]
14pub struct KeyTables {
15 pub global: Bindings,
16 pub image: Bindings,
17 pub grid: Bindings,
18 pub visual: Bindings,
19}
20
21/// A completed binding: the actions to run and the count typed before it.
22#[derive(Clone, Debug, PartialEq)]
23pub struct Run {
24 pub actions: Vec<Action>,
25 pub count: Option<usize>,
26}
27
28impl Run {
29 /// The actions with the count applied.
30 pub fn counted(self) -> Vec<Action> {
31 match self.count {
32 Some(n) => self.actions.into_iter().map(|a| a.with_count(n)).collect(),
33 None => self.actions,
34 }
35 }
36}
37
38/// A next key that would continue the pending sequence.
39#[allow(dead_code)] // which-key arrives in M6
40#[derive(Clone, Debug, PartialEq)]
41pub struct Candidate {
42 pub key: Key,
43 pub actions: Option<Vec<Action>>,
44 /// Longer sequences continue through this key.
45 pub prefix: bool,
46}
47
48#[derive(Clone, Debug, Default)]
49struct Node {
50 actions: Option<Vec<Action>>,
51 children: BTreeMap<Key, Node>,
52}
53
54impl Node {
55 fn walk(&self, keys: &[Key]) -> Option<&Node> {
56 keys.iter().try_fold(self, |n, k| n.children.get(k))
57 }
58
59 fn insert(&mut self, keys: &[Key], actions: Vec<Action>) {
60 let node = keys
61 .iter()
62 .fold(self, |n, k| n.children.entry(*k).or_default());
63 node.actions = Some(actions);
64 }
65
66 fn collect(&self, prefix: &mut Vec<Key>, out: &mut Vec<(KeySeq, Vec<Action>)>) {
67 if let Some(a) = &self.actions {
68 out.push((KeySeq(prefix.clone()), a.clone()));
69 }
70 for (k, child) in &self.children {
71 prefix.push(*k);
72 child.collect(prefix, out);
73 prefix.pop();
74 }
75 }
76}
77
78/// Largest count accepted; more digits are ignored.
79const MAX_COUNT: usize = 9_999_999;
80
81#[derive(Clone, Debug, Default)]
82pub struct Keymap {
83 /// Image, grid, visual.
84 tries: [Node; 3],
85 pending: Vec<Key>,
86 count: Option<usize>,
87}
88
89fn idx(mode: Mode) -> usize {
90 match mode {
91 Mode::Image => 0,
92 Mode::Grid => 1,
93 Mode::Visual => 2,
94 }
95}
96
97impl Keymap {
98 /// Build from config layers, lowest priority first. Within each mode the levels stack
99 /// global < mode (< visual for visual mode, which sits on grid); later layers win inside a
100 /// level, and `"none"` removes whatever a lower level bound.
101 pub fn new(layers: &[&KeyTables]) -> Self {
102 type Level = fn(&KeyTables) -> &Bindings;
103 let global: Level = |t| &t.global;
104 let image: Level = |t| &t.image;
105 let grid: Level = |t| &t.grid;
106 let visual: Level = |t| &t.visual;
107 let chains: [&[Level]; 3] = [&[global, image], &[global, grid], &[global, grid, visual]];
108 let mut km = Keymap::default();
109 for (trie, chain) in km.tries.iter_mut().zip(chains) {
110 let mut flat: BTreeMap<&KeySeq, &Vec<Action>> = BTreeMap::new();
111 for level in chain {
112 let mut merged: BTreeMap<&KeySeq, Option<&Vec<Action>>> = BTreeMap::new();
113 for layer in layers {
114 for (k, v) in level(layer) {
115 merged.insert(k, v.as_ref());
116 }
117 }
118 for (k, v) in merged {
119 match v {
120 Some(a) => flat.insert(k, a),
121 None => flat.remove(k),
122 };
123 }
124 }
125 for (k, a) in flat {
126 trie.insert(&k.0, a.clone());
127 }
128 }
129 km
130 }
131
132 fn root(&self, mode: Mode) -> &Node {
133 &self.tries[idx(mode)]
134 }
135
136 /// Feed one key press. Returns the bindings it completed (usually none or one; an
137 /// ambiguous prefix followed by a non-continuing key yields the prefix's binding first).
138 pub fn feed(&mut self, mode: Mode, key: Key) -> Vec<Run> {
139 if self.pending.is_empty()
140 && let Some(d) = key.digit()
141 && (d != 0 || self.count.is_some())
142 {
143 let c = self.count.unwrap_or(0).saturating_mul(10) + d as usize;
144 self.count = Some(c.min(MAX_COUNT));
145 return Vec::new();
146 }
147 self.pending.push(key);
148 match self.root(mode).walk(&self.pending) {
149 Some(node) if node.children.is_empty() => {
150 let actions = node.actions.clone().unwrap_or_default();
151 self.pending.clear();
152 vec![Run {
153 actions,
154 count: self.count.take(),
155 }]
156 }
157 Some(_) => Vec::new(),
158 None => {
159 self.pending.pop();
160 let prefix = self
161 .root(mode)
162 .walk(&self.pending)
163 .and_then(|n| n.actions.clone())
164 .filter(|_| !self.pending.is_empty());
165 self.pending.clear();
166 match prefix {
167 // `g` is bound and so is `gg`: `gx` runs `g`, then `x` on its own.
168 Some(actions) => {
169 let mut out = vec![Run {
170 actions,
171 count: self.count.take(),
172 }];
173 out.extend(self.feed(mode, key));
174 out
175 }
176 None => {
177 self.count = None;
178 Vec::new()
179 }
180 }
181 }
182 }
183 }
184
185 /// The pending sequence is itself bound and also a prefix of longer ones.
186 pub fn is_ambiguous(&self, mode: Mode) -> bool {
187 !self.pending.is_empty()
188 && self
189 .root(mode)
190 .walk(&self.pending)
191 .is_some_and(|n| n.actions.is_some() && !n.children.is_empty())
192 }
193
194 /// The user stopped typing: an ambiguous sequence runs its own binding.
195 pub fn timeout(&mut self, mode: Mode) -> Vec<Run> {
196 if !self.is_ambiguous(mode) {
197 return Vec::new();
198 }
199 let actions = self
200 .root(mode)
201 .walk(&self.pending)
202 .and_then(|n| n.actions.clone())
203 .unwrap_or_default();
204 self.pending.clear();
205 vec![Run {
206 actions,
207 count: self.count.take(),
208 }]
209 }
210
211 /// A key sequence (not a count) is waiting for more keys.
212 #[allow(dead_code)] // which-key arrives in M6
213 pub fn is_pending(&self) -> bool {
214 !self.pending.is_empty()
215 }
216
217 pub fn cancel(&mut self) {
218 self.pending.clear();
219 self.count = None;
220 }
221
222 /// The count and keys typed so far, for the status bar.
223 pub fn pending_display(&self) -> String {
224 let mut s = self.count.map(|c| c.to_string()).unwrap_or_default();
225 if !self.pending.is_empty() {
226 s.push_str(&KeySeq(self.pending.clone()).to_string());
227 }
228 s
229 }
230
231 /// The binding of a complete sequence (mouse buttons and the wheel bypass the trie).
232 pub fn lookup(&self, mode: Mode, keys: &[Key]) -> Option<&[Action]> {
233 self.root(mode).walk(keys)?.actions.as_deref()
234 }
235
236 /// The keys that continue the pending sequence, for which-key.
237 #[allow(dead_code)] // which-key arrives in M6
238 pub fn candidates(&self, mode: Mode) -> Vec<Candidate> {
239 let Some(node) = self.root(mode).walk(&self.pending) else {
240 return Vec::new();
241 };
242 node.children
243 .iter()
244 .map(|(k, n)| Candidate {
245 key: *k,
246 actions: n.actions.clone(),
247 prefix: !n.children.is_empty(),
248 })
249 .collect()
250 }
251
252 /// Every binding in a mode, in key order.
253 pub fn bindings(&self, mode: Mode) -> Vec<(KeySeq, Vec<Action>)> {
254 let mut out = Vec::new();
255 self.root(mode).collect(&mut Vec::new(), &mut out);
256 out
257 }
258
259 /// The sequences bound to exactly `action` in a mode, shortest first.
260 #[allow(dead_code)] // the palette and help arrive in M6
261 pub fn keys_for(&self, mode: Mode, action: &Action) -> Vec<KeySeq> {
262 let mut keys: Vec<KeySeq> = self
263 .bindings(mode)
264 .into_iter()
265 .filter(|(_, a)| a.len() == 1 && a[0] == *action)
266 .map(|(k, _)| k)
267 .collect();
268 keys.sort_by_key(|k| k.0.len());
269 keys
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 use super::*;
276 use crate::action::Zoom;
277
278 fn bind(pairs: &[(&str, &str)]) -> Bindings {
279 pairs
280 .iter()
281 .map(|(k, a)| {
282 let actions = (*a != "none").then(|| vec![a.parse().unwrap()]);
283 (k.parse().unwrap(), actions)
284 })
285 .collect()
286 }
287
288 fn keys(s: &str) -> Vec<Key> {
289 s.parse::<KeySeq>().unwrap().0
290 }
291
292 /// Feed a whole string and collect the counted actions.
293 fn type_(km: &mut Keymap, mode: Mode, s: &str) -> Vec<Action> {
294 keys(s)
295 .into_iter()
296 .flat_map(|k| km.feed(mode, k))
297 .flat_map(Run::counted)
298 .collect()
299 }
300
301 fn defaults() -> KeyTables {
302 KeyTables {
303 global: bind(&[
304 ("n", "next"),
305 ("gg", "first"),
306 ("G", "last"),
307 ("q", "quit"),
308 ("0", "first"),
309 ("<Space>w", "run wallpaper"),
310 ("<Space>c", "run copy"),
311 ]),
312 image: bind(&[("+", "zoom in"), ("h", "pan left")]),
313 grid: bind(&[("h", "prev"), ("v", "visual")]),
314 visual: bind(&[("m", "mark-all")]),
315 }
316 }
317
318 #[test]
319 fn sequences_and_counts() {
320 let t = defaults();
321 let mut km = Keymap::new(&[&t]);
322 let m = Mode::Image;
323 assert_eq!(type_(&mut km, m, "n"), [Action::Next(1)]);
324 assert_eq!(type_(&mut km, m, "g"), []);
325 assert_eq!(km.pending_display(), "g");
326 assert_eq!(type_(&mut km, m, "g"), [Action::First]);
327 assert_eq!(type_(&mut km, m, "12G"), [Action::Goto(12)]);
328 assert_eq!(type_(&mut km, m, "5gg"), [Action::Goto(5)]);
329 assert_eq!(type_(&mut km, m, "3n"), [Action::Next(3)]);
330 assert_eq!(type_(&mut km, m, "3+"), [Action::Zoom(Zoom::In(3))]);
331 // `0` is a key unless a count is pending.
332 assert_eq!(type_(&mut km, m, "0"), [Action::First]);
333 assert_eq!(type_(&mut km, m, "10n"), [Action::Next(10)]);
334 assert_eq!(km.pending_display(), "");
335 }
336
337 #[test]
338 fn broken_sequences_are_dropped() {
339 let t = defaults();
340 let mut km = Keymap::new(&[&t]);
341 let m = Mode::Image;
342 assert_eq!(type_(&mut km, m, "5gx"), []);
343 assert!(!km.is_pending());
344 assert_eq!(km.pending_display(), "");
345 assert_eq!(type_(&mut km, m, "<Space>"), []);
346 assert_eq!(type_(&mut km, m, "<Esc>"), []);
347 assert_eq!(type_(&mut km, m, "n"), [Action::Next(1)]);
348 assert_eq!(type_(&mut km, m, "Z"), []);
349 }
350
351 #[test]
352 fn modes_override_global() {
353 let t = defaults();
354 let mut km = Keymap::new(&[&t]);
355 assert_eq!(
356 type_(&mut km, Mode::Image, "h"),
357 [Action::Pan(crate::state::view::Dir::Left)]
358 );
359 assert_eq!(type_(&mut km, Mode::Grid, "h"), [Action::Prev(1)]);
360 // Visual sits on grid.
361 assert_eq!(type_(&mut km, Mode::Visual, "h"), [Action::Prev(1)]);
362 assert_eq!(type_(&mut km, Mode::Visual, "m"), [Action::MarkAll]);
363 assert_eq!(type_(&mut km, Mode::Grid, "m"), []);
364 }
365
366 #[test]
367 fn later_layers_win_and_none_removes() {
368 let base = defaults();
369 let user = KeyTables {
370 global: bind(&[("n", "prev"), ("q", "none")]),
371 image: bind(&[("+", "none"), ("G", "none")]),
372 ..Default::default()
373 };
374 let mut km = Keymap::new(&[&base, &user]);
375 assert_eq!(type_(&mut km, Mode::Image, "n"), [Action::Prev(1)]);
376 assert_eq!(type_(&mut km, Mode::Image, "q"), []);
377 assert_eq!(type_(&mut km, Mode::Image, "+"), []);
378 // A mode's "none" masks the global binding in that mode only.
379 assert_eq!(type_(&mut km, Mode::Image, "G"), []);
380 assert_eq!(type_(&mut km, Mode::Grid, "G"), [Action::Last]);
381 }
382
383 #[test]
384 fn ambiguous_prefix_waits_then_resolves() {
385 let t = KeyTables {
386 global: bind(&[("g", "first"), ("gg", "last"), ("n", "next")]),
387 ..Default::default()
388 };
389 let mut km = Keymap::new(&[&t]);
390 let m = Mode::Image;
391 assert_eq!(type_(&mut km, m, "g"), []);
392 assert!(km.is_ambiguous(m));
393 assert_eq!(type_(&mut km, m, "g"), [Action::Last]);
394 // A non-continuing key runs the prefix, then itself.
395 assert_eq!(type_(&mut km, m, "gn"), [Action::First, Action::Next(1)]);
396 type_(&mut km, m, "4g");
397 let fired: Vec<Action> = km.timeout(m).into_iter().flat_map(Run::counted).collect();
398 assert_eq!(fired, [Action::Goto(4)]);
399 assert!(!km.is_pending());
400 }
401
402 #[test]
403 fn which_key_candidates() {
404 let t = defaults();
405 let mut km = Keymap::new(&[&t]);
406 type_(&mut km, Mode::Image, "<Space>");
407 let c = km.candidates(Mode::Image);
408 assert_eq!(c.len(), 2);
409 assert_eq!(c[0].key, Key::plain('c'));
410 assert_eq!(c[0].actions, Some(vec![Action::Run("copy".into())]));
411 assert!(!c[0].prefix);
412 km.cancel();
413 let root = km.candidates(Mode::Image);
414 assert!(root.iter().any(|c| c.key == Key::plain('g') && c.prefix));
415 }
416
417 #[test]
418 fn lookup_and_reverse_lookup() {
419 let t = defaults();
420 let km = Keymap::new(&[&t]);
421 assert_eq!(
422 km.lookup(Mode::Image, &keys("n")),
423 Some(&[Action::Next(1)][..])
424 );
425 assert_eq!(km.lookup(Mode::Image, &keys("g")), None);
426 assert_eq!(
427 km.keys_for(Mode::Image, &Action::First),
428 vec!["0".parse().unwrap(), "gg".parse().unwrap()]
429 );
430 assert!(
431 km.bindings(Mode::Grid)
432 .iter()
433 .any(|(k, _)| k.to_string() == "v")
434 );
435 }
436}