Skip to main content

smart_keymap_core/key/
history.rs

1//! History keys: behaviours that depend on previously resolved key output.
2//!
3//! - [Key::Repeat](crate::key::history::Key::Repeat) re-emits the last remembered
4//!   [crate::key::KeyOutput] as the pressed key's own output while held.
5//! - [Key::AltRepeat](crate::key::history::Key::AltRepeat) looks up that last
6//!   output in a Nickel-defined table ([Config::alt_repeat](crate::key::history::Config::alt_repeat))
7//!   and emits the mapped alternate while held (QMK-style alternate repeat for
8//!   single keys).
9//! - [Key::Adaptive](crate::key::history::Key::Adaptive) looks up that last
10//!   output in a per-key rule table (Hands Down / ZMK adaptive-key style).
11//!   On a miss, the key emits its default output.
12
13use core::fmt::Debug;
14use core::marker::PhantomData;
15use core::ops::Index;
16
17use serde::Deserialize;
18
19use crate::key;
20use crate::keymap;
21
22/// Reference for a history key.
23#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
24pub struct Ref(pub Key);
25
26/// History key kinds.
27#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
28pub enum Key {
29    /// Re-emit the last remembered key output while pressed.
30    Repeat,
31    /// Emit the configured alternate of the last remembered key output while pressed.
32    ///
33    /// If the last output is unmapped (or history is empty), contributes no output.
34    AltRepeat,
35    /// Per-site adaptive key.
36    ///
37    /// Looks up the last remembered output in this key's rule table.
38    /// If no rule matches, emits [AdaptiveKey::default].
39    /// The `u8` indexes the [System] adaptive-key array.
40    Adaptive(u8),
41}
42
43impl Key {
44    /// Constructs a [Key::Repeat].
45    pub const fn new_repeat() -> Self {
46        Key::Repeat
47    }
48
49    /// Constructs a [Key::AltRepeat].
50    pub const fn new_alt_repeat() -> Self {
51        Key::AltRepeat
52    }
53
54    /// Constructs a [Key::Adaptive] referencing the given system index.
55    pub const fn new_adaptive(index: u8) -> Self {
56        Key::Adaptive(index)
57    }
58}
59
60/// Maximum number of `{ prev, emit }` rules stored on one [AdaptiveKey].
61///
62/// Unused slots are [AltRepeatRule::EMPTY].
63pub const MAX_ADAPTIVE_RULES: usize = 8;
64
65/// One alternate-repeat mapping: when the last remembered output equals [Self::prev],
66/// [Key::AltRepeat] emits [Self::emit].
67#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
68pub struct AltRepeatRule {
69    /// Previous resolved output that triggers this rule.
70    pub prev: key::KeyOutput,
71    /// Output to emit instead of repeating [Self::prev].
72    pub emit: key::KeyOutput,
73}
74
75impl AltRepeatRule {
76    /// Empty placeholder rule (used to pad fixed-size arrays).
77    pub const EMPTY: Self = Self {
78        prev: key::KeyOutput::NO_OUTPUT,
79        emit: key::KeyOutput::NO_OUTPUT,
80    };
81
82    /// Constructs a rule.
83    pub const fn new(prev: key::KeyOutput, emit: key::KeyOutput) -> Self {
84        Self { prev, emit }
85    }
86}
87
88/// Per-site adaptive key.
89///
90/// Holds a default output and a table of last-output → emit rules.
91#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
92pub struct AdaptiveKey {
93    /// Output when no rule matches (or history is empty).
94    pub default: key::KeyOutput,
95    /// Sparse last-output → emit mappings for this key site.
96    #[serde(deserialize_with = "deserialize_adaptive_rules")]
97    pub rules: [AltRepeatRule; MAX_ADAPTIVE_RULES],
98}
99
100impl AdaptiveKey {
101    /// Empty adaptive key (no-op default, no rules).
102    pub const EMPTY: Self = Self {
103        default: key::KeyOutput::NO_OUTPUT,
104        rules: [AltRepeatRule::EMPTY; MAX_ADAPTIVE_RULES],
105    };
106
107    /// Constructs an adaptive key from a default and a padded rule array.
108    pub const fn new(default: key::KeyOutput, rules: [AltRepeatRule; MAX_ADAPTIVE_RULES]) -> Self {
109        Self { default, rules }
110    }
111
112    /// Looks up `prev` in this key's rule table.
113    pub fn lookup(&self, prev: &key::KeyOutput) -> Option<key::KeyOutput> {
114        self.rules
115            .iter()
116            .find(|r| r.prev == *prev && **r != AltRepeatRule::EMPTY)
117            .map(|r| r.emit)
118    }
119}
120
121/// Builds a fixed-size adaptive rule array from a shorter const list (codegen helper).
122pub const fn adaptive_rules<const N: usize>(
123    rules: [AltRepeatRule; N],
124) -> [AltRepeatRule; MAX_ADAPTIVE_RULES] {
125    let mut out: [AltRepeatRule; MAX_ADAPTIVE_RULES] = [AltRepeatRule::EMPTY; MAX_ADAPTIVE_RULES];
126
127    if N > MAX_ADAPTIVE_RULES {
128        panic!("Too many adaptive rules for AdaptiveKey");
129    }
130
131    let mut i = 0;
132    while i < N {
133        out[i] = rules[i];
134        i += 1;
135    }
136    out
137}
138
139fn deserialize_adaptive_rules<'de, D>(
140    deserializer: D,
141) -> Result<[AltRepeatRule; MAX_ADAPTIVE_RULES], D::Error>
142where
143    D: serde::Deserializer<'de>,
144{
145    let rules_vec: heapless::Vec<AltRepeatRule, MAX_ADAPTIVE_RULES> =
146        Deserialize::deserialize(deserializer)?;
147
148    let mut rules_array: [AltRepeatRule; MAX_ADAPTIVE_RULES] =
149        [AltRepeatRule::EMPTY; MAX_ADAPTIVE_RULES];
150    for (i, rule) in rules_vec.iter().enumerate() {
151        rules_array[i] = *rule;
152    }
153
154    Ok(rules_array)
155}
156
157fn output_or_none(output: key::KeyOutput) -> Option<key::KeyOutput> {
158    if is_rememberable(&output) {
159        Some(output)
160    } else {
161        None
162    }
163}
164
165/// Config for history keys (alt-repeat lookup table).
166#[derive(Deserialize, Clone, Copy, PartialEq)]
167pub struct Config<const ALT_REPEAT_RULE_COUNT: usize> {
168    /// Sparse map of previous output → alternate output for [Key::AltRepeat].
169    #[serde(deserialize_with = "deserialize_alt_repeat")]
170    pub alt_repeat: [AltRepeatRule; ALT_REPEAT_RULE_COUNT],
171}
172
173struct AltRepeatDebugHelper<'a, const N: usize> {
174    rules: &'a [AltRepeatRule; N],
175}
176
177impl<'a, const N: usize> core::fmt::Debug for AltRepeatDebugHelper<'a, N> {
178    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
179        let last_non_empty = self
180            .rules
181            .iter()
182            .rposition(|r| *r != AltRepeatRule::EMPTY)
183            .map_or(0, |pos| pos + 1);
184        if last_non_empty < N {
185            f.debug_list()
186                .entries(&self.rules[..last_non_empty])
187                .finish_non_exhaustive()
188        } else {
189            f.debug_list().entries(&self.rules[..]).finish()
190        }
191    }
192}
193
194impl<const ALT_REPEAT_RULE_COUNT: usize> core::fmt::Debug for Config<ALT_REPEAT_RULE_COUNT> {
195    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
196        f.debug_struct("Config")
197            .field(
198                "alt_repeat",
199                &AltRepeatDebugHelper {
200                    rules: &self.alt_repeat,
201                },
202            )
203            .finish()
204    }
205}
206
207/// Builds a fixed-size alt-repeat rule array from a shorter const list (codegen helper).
208pub const fn alt_repeat_rules<const N: usize, const ALT_REPEAT_RULE_COUNT: usize>(
209    rules: [AltRepeatRule; N],
210) -> [AltRepeatRule; ALT_REPEAT_RULE_COUNT] {
211    let mut out: [AltRepeatRule; ALT_REPEAT_RULE_COUNT] =
212        [AltRepeatRule::EMPTY; ALT_REPEAT_RULE_COUNT];
213
214    if N > ALT_REPEAT_RULE_COUNT {
215        panic!("Too many alt-repeat rules for alt_repeat array");
216    }
217
218    let mut i = 0;
219    while i < N {
220        out[i] = rules[i];
221        i += 1;
222    }
223    out
224}
225
226fn deserialize_alt_repeat<'de, D, const ALT_REPEAT_RULE_COUNT: usize>(
227    deserializer: D,
228) -> Result<[AltRepeatRule; ALT_REPEAT_RULE_COUNT], D::Error>
229where
230    D: serde::Deserializer<'de>,
231{
232    let rules_vec: heapless::Vec<AltRepeatRule, ALT_REPEAT_RULE_COUNT> =
233        Deserialize::deserialize(deserializer)?;
234
235    let mut rules_array: [AltRepeatRule; ALT_REPEAT_RULE_COUNT] =
236        [AltRepeatRule::EMPTY; ALT_REPEAT_RULE_COUNT];
237    for (i, rule) in rules_vec.iter().enumerate() {
238        rules_array[i] = *rule;
239    }
240
241    Ok(rules_array)
242}
243
244impl<const ALT_REPEAT_RULE_COUNT: usize> Config<ALT_REPEAT_RULE_COUNT> {
245    /// Constructs a new default [Config] (empty alt-repeat table).
246    pub const fn new() -> Self {
247        Self {
248            alt_repeat: [AltRepeatRule::EMPTY; ALT_REPEAT_RULE_COUNT],
249        }
250    }
251
252    /// Looks up the alternate for `prev`, if any rule matches exactly.
253    pub fn lookup_alt(&self, prev: &key::KeyOutput) -> Option<key::KeyOutput> {
254        self.alt_repeat
255            .iter()
256            .find(|r| r.prev == *prev && **r != AltRepeatRule::EMPTY)
257            .map(|r| r.emit)
258    }
259}
260
261impl<const ALT_REPEAT_RULE_COUNT: usize> Default for Config<ALT_REPEAT_RULE_COUNT> {
262    fn default() -> Self {
263        Self::new()
264    }
265}
266
267/// Whether a resolved [key::KeyOutput] should become the remembered last output.
268///
269/// Empty / no-op outputs are ignored so that pressing Repeat with no history
270/// (or keys that resolve without output) does not clear a prior memory.
271pub fn is_rememberable(key_output: &key::KeyOutput) -> bool {
272    *key_output != key::KeyOutput::NO_OUTPUT
273}
274
275/// Context for history keys: tracks the last rememberable resolved output.
276#[derive(Debug, Clone, Copy, PartialEq)]
277pub struct Context<const ALT_REPEAT_RULE_COUNT: usize = 0> {
278    /// History / alt-repeat configuration.
279    pub config: Config<ALT_REPEAT_RULE_COUNT>,
280    last: Option<key::KeyOutput>,
281}
282
283impl<const ALT_REPEAT_RULE_COUNT: usize> Default for Context<ALT_REPEAT_RULE_COUNT> {
284    fn default() -> Self {
285        Self::new()
286    }
287}
288
289impl<const ALT_REPEAT_RULE_COUNT: usize> Context<ALT_REPEAT_RULE_COUNT> {
290    /// Constructs a new [Context] with default config.
291    pub const fn new() -> Self {
292        Self::from_config(Config::new())
293    }
294
295    /// Constructs a context from the given config.
296    pub const fn from_config(config: Config<ALT_REPEAT_RULE_COUNT>) -> Self {
297        Context { config, last: None }
298    }
299
300    /// Clear remembered history (keeps config).
301    pub fn reset(&mut self) {
302        *self = Self::from_config(self.config);
303    }
304
305    /// The last rememberable resolved key output, if any.
306    pub fn last(&self) -> Option<key::KeyOutput> {
307        self.last
308    }
309
310    fn handle_event(&mut self, event: key::Event<Event>) -> key::KeyEvents<Event> {
311        if let key::Event::Keymap(keymap::KeymapEvent::ResolvedKeyOutput { key_output, .. }) = event
312        {
313            if is_rememberable(&key_output) {
314                self.last = Some(key_output);
315            }
316        }
317        key::KeyEvents::no_events()
318    }
319}
320
321impl<const ALT_REPEAT_RULE_COUNT: usize> key::Context for Context<ALT_REPEAT_RULE_COUNT> {
322    type Event = Event;
323
324    fn handle_event(&mut self, event: key::Event<Self::Event>) -> key::KeyEvents<Self::Event> {
325        self.handle_event(event)
326    }
327
328    fn reset(&mut self) {
329        Context::reset(self);
330    }
331}
332
333/// Events for history keys. (None for v1.)
334#[derive(Debug, Clone, Copy, PartialEq)]
335pub struct Event;
336
337/// Pending key state type for history keys. (No pending state.)
338#[derive(Debug, Clone, Copy, PartialEq)]
339pub struct PendingKeyState;
340
341/// Pressed state: the output being emitted (if any) for the hold duration.
342#[derive(Debug, Clone, Copy, PartialEq)]
343pub struct KeyState {
344    output: Option<key::KeyOutput>,
345}
346
347impl KeyState {
348    /// Constructs a key state with the given output.
349    pub const fn new(output: Option<key::KeyOutput>) -> Self {
350        Self { output }
351    }
352
353    /// The output this pressed history key contributes, if any.
354    pub const fn output(&self) -> Option<key::KeyOutput> {
355        self.output
356    }
357}
358
359/// The [key::System] implementation for history keys.
360#[derive(Debug, Clone, Copy, PartialEq)]
361pub struct System<R, Keys = [AdaptiveKey; 0], const ALT_REPEAT_RULE_COUNT: usize = 0> {
362    keys: Keys,
363    _r: PhantomData<R>,
364}
365
366impl<R, Keys, const ALT_REPEAT_RULE_COUNT: usize> System<R, Keys, ALT_REPEAT_RULE_COUNT> {
367    /// Constructs a new [System] with the given adaptive-key data.
368    pub const fn new(keys: Keys) -> Self {
369        Self {
370            keys,
371            _r: PhantomData,
372        }
373    }
374}
375
376impl<R, Keys: Default, const ALT_REPEAT_RULE_COUNT: usize> Default
377    for System<R, Keys, ALT_REPEAT_RULE_COUNT>
378{
379    fn default() -> Self {
380        Self::new(Keys::default())
381    }
382}
383
384impl<
385        R: Debug,
386        Keys: Debug + Index<usize, Output = AdaptiveKey>,
387        const ALT_REPEAT_RULE_COUNT: usize,
388    > key::System<R> for System<R, Keys, ALT_REPEAT_RULE_COUNT>
389{
390    type Ref = Ref;
391    type Context = Context<ALT_REPEAT_RULE_COUNT>;
392    type Event = Event;
393    type PendingKeyState = PendingKeyState;
394    type KeyState = KeyState;
395
396    fn new_pressed_key(
397        &self,
398        _keymap_index: u16,
399        context: &Self::Context,
400        Ref(key): Ref,
401    ) -> (
402        key::PressedKeyResult<R, Self::PendingKeyState, Self::KeyState>,
403        key::KeyEvents<Self::Event>,
404    ) {
405        let output = match key {
406            Key::Repeat => context.last(),
407            Key::AltRepeat => context
408                .last()
409                .and_then(|last| context.config.lookup_alt(&last)),
410            Key::Adaptive(index) => {
411                let spec = &self.keys[index as usize];
412                let raw = context
413                    .last()
414                    .and_then(|last| spec.lookup(&last))
415                    .unwrap_or(spec.default);
416                output_or_none(raw)
417            }
418        };
419        (
420            key::PressedKeyResult::Resolved(KeyState::new(output)),
421            key::KeyEvents::no_events(),
422        )
423    }
424
425    fn update_pending_state(
426        &self,
427        _pending_state: &mut Self::PendingKeyState,
428        _keymap_index: u16,
429        _context: &Self::Context,
430        _key_ref: Ref,
431        _event: key::Event<Self::Event>,
432    ) -> (Option<key::NewPressedKey<R>>, key::KeyEvents<Self::Event>) {
433        panic!()
434    }
435
436    fn key_output(
437        &self,
438        _key_ref: &Self::Ref,
439        key_state: &Self::KeyState,
440    ) -> Option<key::KeyOutput> {
441        key_state.output()
442    }
443}
444
445#[cfg(test)]
446mod tests {
447    use super::*;
448    use crate::key::System as _;
449
450    #[test]
451    fn test_sizeof_ref() {
452        // Repeat / AltRepeat / Adaptive(u8) → discriminant + index.
453        assert_eq!(2, core::mem::size_of::<Ref>());
454    }
455
456    #[test]
457    fn test_sizeof_event() {
458        assert_eq!(0, core::mem::size_of::<Event>());
459    }
460
461    #[test]
462    fn context_remembers_resolved_keyboard_output() {
463        let mut ctx = Context::<0>::new();
464        assert_eq!(None, ctx.last());
465
466        let key_output = key::KeyOutput::from_key_code(0x04);
467        let _ = key::Context::handle_event(
468            &mut ctx,
469            key::Event::Keymap(keymap::KeymapEvent::ResolvedKeyOutput {
470                keymap_index: 0,
471                key_output,
472            }),
473        );
474
475        assert_eq!(Some(key_output), ctx.last());
476    }
477
478    #[test]
479    fn context_ignores_empty_output() {
480        let mut ctx = Context::<0>::new();
481        let remembered = key::KeyOutput::from_key_code(0x04);
482        ctx.last = Some(remembered);
483
484        let _ = key::Context::handle_event(
485            &mut ctx,
486            key::Event::Keymap(keymap::KeymapEvent::ResolvedKeyOutput {
487                keymap_index: 0,
488                key_output: key::KeyOutput::NO_OUTPUT,
489            }),
490        );
491
492        assert_eq!(Some(remembered), ctx.last());
493    }
494
495    #[test]
496    fn repeat_pressed_key_uses_context_last() {
497        let system = System::<()>::new([]);
498        let mut ctx = Context::<0>::new();
499        let key_output = key::KeyOutput::from_key_code(0x05);
500        ctx.last = Some(key_output);
501
502        let (pkr, _) = system.new_pressed_key(0, &ctx, Ref(Key::Repeat));
503        let ks = pkr.unwrap_resolved();
504        assert_eq!(Some(key_output), system.key_output(&Ref(Key::Repeat), &ks));
505    }
506
507    #[test]
508    fn alt_repeat_looks_up_config_rule() {
509        let left = key::KeyOutput::from_key_code(0x50);
510        let right = key::KeyOutput::from_key_code(0x4F);
511        let config = Config {
512            alt_repeat: [AltRepeatRule::new(left, right)],
513        };
514        let mut ctx = Context::from_config(config);
515        ctx.last = Some(left);
516
517        let system = System::<(), [AdaptiveKey; 0], 1>::new([]);
518        let (pkr, _) = system.new_pressed_key(0, &ctx, Ref(Key::AltRepeat));
519        let ks = pkr.unwrap_resolved();
520        assert_eq!(Some(right), system.key_output(&Ref(Key::AltRepeat), &ks));
521    }
522
523    #[test]
524    fn alt_repeat_unmapped_is_none() {
525        let system = System::<()>::new([]);
526        let mut ctx = Context::<0>::new();
527        ctx.last = Some(key::KeyOutput::from_key_code(0x04));
528
529        let (pkr, _) = system.new_pressed_key(0, &ctx, Ref(Key::AltRepeat));
530        let ks = pkr.unwrap_resolved();
531        assert_eq!(None, system.key_output(&Ref(Key::AltRepeat), &ks));
532    }
533
534    #[test]
535    fn adaptive_uses_matching_rule() {
536        // Assemble: H with A → U, last output is A
537        let a = key::KeyOutput::from_key_code(0x04);
538        let h = key::KeyOutput::from_key_code(0x0B);
539        let u = key::KeyOutput::from_key_code(0x18);
540        let keys = [AdaptiveKey::new(
541            h,
542            adaptive_rules([AltRepeatRule::new(a, u)]),
543        )];
544        let system = System::<(), _>::new(keys);
545        let mut ctx = Context::<0>::new();
546        ctx.last = Some(a);
547
548        // Act: press adaptive H
549        let (pkr, _) = system.new_pressed_key(0, &ctx, Ref(Key::Adaptive(0)));
550        let ks = pkr.unwrap_resolved();
551
552        // Assert: emits U
553        assert_eq!(Some(u), system.key_output(&Ref(Key::Adaptive(0)), &ks));
554    }
555
556    #[test]
557    fn adaptive_falls_back_to_default() {
558        // Assemble: H with A → U, last output is B
559        let a = key::KeyOutput::from_key_code(0x04);
560        let b = key::KeyOutput::from_key_code(0x05);
561        let h = key::KeyOutput::from_key_code(0x0B);
562        let u = key::KeyOutput::from_key_code(0x18);
563        let keys = [AdaptiveKey::new(
564            h,
565            adaptive_rules([AltRepeatRule::new(a, u)]),
566        )];
567        let system = System::<(), _>::new(keys);
568        let mut ctx = Context::<0>::new();
569        ctx.last = Some(b);
570
571        // Act: press adaptive H
572        let (pkr, _) = system.new_pressed_key(0, &ctx, Ref(Key::Adaptive(0)));
573        let ks = pkr.unwrap_resolved();
574
575        // Assert: emits default H
576        assert_eq!(Some(h), system.key_output(&Ref(Key::Adaptive(0)), &ks));
577    }
578
579    #[test]
580    fn adaptive_empty_history_uses_default() {
581        // Assemble: H with no rules and empty history
582        let h = key::KeyOutput::from_key_code(0x0B);
583        let keys = [AdaptiveKey::new(h, adaptive_rules([]))];
584        let system = System::<(), _>::new(keys);
585        let ctx = Context::<0>::new();
586
587        // Act: press adaptive H
588        let (pkr, _) = system.new_pressed_key(0, &ctx, Ref(Key::Adaptive(0)));
589        let ks = pkr.unwrap_resolved();
590
591        // Assert: emits default H
592        assert_eq!(Some(h), system.key_output(&Ref(Key::Adaptive(0)), &ks));
593    }
594
595    #[test]
596    fn adaptive_noop_default_is_none() {
597        // Assemble: empty adaptive key and empty history
598        let system = System::<(), _>::new([AdaptiveKey::EMPTY]);
599        let ctx = Context::<0>::new();
600
601        // Act: press adaptive key
602        let (pkr, _) = system.new_pressed_key(0, &ctx, Ref(Key::Adaptive(0)));
603        let ks = pkr.unwrap_resolved();
604
605        // Assert: no output
606        assert_eq!(None, system.key_output(&Ref(Key::Adaptive(0)), &ks));
607    }
608}