diff --git a/cfg_samples/kanata.kbd b/cfg_samples/kanata.kbd index d5229f98d..b13c54b90 100644 --- a/cfg_samples/kanata.kbd +++ b/cfg_samples/kanata.kbd @@ -771,8 +771,12 @@ If you need help, please feel welcome to ask in the GitHub discussions. ;; The underscore _ means transparent. The key on the base layer will be used ;; instead. XX means no-op. The key will do nothing. +;; +;; A similar concept to transparent, use-defsrc means the key will always +;; behave as the key as defined by defsrc. +(defalias src use-defsrc) (deflayer numbers - _ _ _ _ _ _ nlk kp7 kp8 kp9 _ _ _ _ + @src _ _ _ _ _ nlk kp7 kp8 kp9 _ _ _ _ _ _ _ _ _ XX _ kp4 kp5 kp6 - _ _ _ _ _ C-z _ _ XX _ kp1 kp2 kp3 + _ _ _ C-z C-x C-c C-v XX _ kp0 kp0 . / _ diff --git a/docs/config.adoc b/docs/config.adoc index 7d1092ae9..17f96dc95 100644 --- a/docs/config.adoc +++ b/docs/config.adoc @@ -641,15 +641,20 @@ default to the corresponding `defsrc` key. If `+_+` is pressed on the active while-held layer, the base layer's behaviour will activate. (alternatively you can use `+‗+` `+≝+`) +A similar concept to transparent key is the `+use-defsrc+` action. +When activated, the underlying `defsrc` key will be the output action. + .Example: [source] ---- (defsrc a b c d ) - +(defalias + src use-defsrc +) (deflayer remap-only-c-to-d - _ ‗ d ≝ + _ _ d @src ) ---- diff --git a/keyberon/src/action.rs b/keyberon/src/action.rs index 7c7ae7dd1..e0c14a919 100644 --- a/keyberon/src/action.rs +++ b/keyberon/src/action.rs @@ -385,6 +385,9 @@ where /// The maximum number of actions that can activate the same time is governed by /// `ACTION_QUEUE_LEN`. Switch(&'a Switch<'a, T>), + /// Disregard the entire layer stack, i.e. the current base layer and any while-held layers, + /// and select the action from `Layout.src_keys`. + Src, } impl<'a, T> Action<'a, T> { diff --git a/keyberon/src/layout.rs b/keyberon/src/layout.rs index be47c646e..653034e91 100644 --- a/keyberon/src/layout.rs +++ b/keyberon/src/layout.rs @@ -1592,6 +1592,13 @@ impl<'a, const C: usize, const R: usize, T: 'a + Copy + std::fmt::Debug> Layout< } self.rpt_action = Some(action); } + Src => { + let action = &self.src_keys[usize::from(coord.1)]; + // Risk: infinite recursive resulting in stack overflow. + // In practice this is not expected to happen. + // The `src_keys` actions are all expected to be `KeyCode` or `NoOp` actions. + self.do_action(action, coord, delay, is_oneshot, &mut std::iter::empty()); + } Trans => { // Transparent action should be resolved to non-transparent one near the top // of `do_action`. diff --git a/parser/src/cfg/key_outputs.rs b/parser/src/cfg/key_outputs.rs index 6f0cf1e51..6d4d37baf 100644 --- a/parser/src/cfg/key_outputs.rs +++ b/parser/src/cfg/key_outputs.rs @@ -125,6 +125,9 @@ pub(crate) fn add_key_output_from_action_to_key_pos( } } } + Action::Src => { + add_kc_output(osc_slot, osc_slot, outputs, overrides); + } Action::NoOp | Action::Trans | Action::Repeat diff --git a/parser/src/cfg/mod.rs b/parser/src/cfg/mod.rs index 342283bc1..62ef9753d 100755 --- a/parser/src/cfg/mod.rs +++ b/parser/src/cfg/mod.rs @@ -1644,6 +1644,9 @@ fn parse_action_atom(ac_span: &Spanned, s: &ParserState) -> Result<&'sta ), _ => return custom(CustomAction::ReverseReleaseOrder, &s.a), }, + "use-defsrc" => { + return Ok(s.a.sref(Action::Src)); + } _ => {} }; if let Some(oscode) = str_to_oscode(ac) { @@ -2729,6 +2732,7 @@ fn find_chords_coords(chord_groups: &mut [ChordGroup], coord: (u8, u16), action: } Action::NoOp | Action::Trans + | Action::Src | Action::Repeat | Action::KeyCode(_) | Action::MultipleKeyCodes(_) @@ -2784,6 +2788,7 @@ fn fill_chords( Action::NoOp | Action::Trans | Action::Repeat + | Action::Src | Action::KeyCode(_) | Action::MultipleKeyCodes(_) | Action::Layer(_) @@ -3151,21 +3156,9 @@ fn parse_layers( let mut defsrc_anykey_used = false; let mut unmapped_anykey_used = false; let mut both_anykey_used = false; - for triplet in pairs.by_ref() { - let input = &triplet[0]; - let action = &triplet[1]; - - // TODO: remove me some time after April 2024 to reduce code bloat somewhat. - const MAPSTRS: &[&str] = &[":", "->", ">>", "maps-to", "→", "🞂"]; - const MAPSTR_ERR: &str = "Seems you are using a retired configuration style.\n\ - You should remove all mapping strings from deflayermap;\n\ - deflayermap now uses pairs instead of triples."; - if input.atom(s.vars()).is_some_and(|x| MAPSTRS.contains(&x)) { - bail_expr!(input, "{MAPSTR_ERR}"); - } - if action.atom(s.vars()).is_some_and(|x| MAPSTRS.contains(&x)) { - bail_expr!(action, "{MAPSTR_ERR}"); - } + for pair in pairs.by_ref() { + let input = &pair[0]; + let action = &pair[1]; let action = parse_action(action, s)?; if input.atom(s.vars()).is_some_and(|x| x == "_") { diff --git a/src/kanata/output_logic/zippychord.rs b/src/kanata/output_logic/zippychord.rs index 86c174e60..eedfcf1f7 100644 --- a/src/kanata/output_logic/zippychord.rs +++ b/src/kanata/output_logic/zippychord.rs @@ -8,8 +8,6 @@ use std::sync::MutexGuard; // Maybe-todos: // --- -// Feature-parity: smart spacing around words -// - fixup whitespace around punctuation? // Feature-parity: suffixes - only active while disabled, to complete a word. // Feature-parity: prefix vs. non-prefix. Assuming smart spacing is implemented and enabled, // standard activations would output space one outputs space, but not prefixes. diff --git a/src/tests/sim_tests/mod.rs b/src/tests/sim_tests/mod.rs index 84509720a..e39a8819e 100644 --- a/src/tests/sim_tests/mod.rs +++ b/src/tests/sim_tests/mod.rs @@ -23,6 +23,7 @@ mod seq_sim_tests; mod switch_sim_tests; mod unicode_sim_tests; mod unmod_sim_tests; +mod use_defsrc_sim_tests; mod zippychord_sim_tests; fn simulate>(cfg: S, sim: S) -> String { diff --git a/src/tests/sim_tests/use_defsrc_sim_tests.rs b/src/tests/sim_tests/use_defsrc_sim_tests.rs new file mode 100644 index 000000000..54c469f80 --- /dev/null +++ b/src/tests/sim_tests/use_defsrc_sim_tests.rs @@ -0,0 +1,57 @@ +use super::*; + +#[test] +fn use_defsrc_deflayer() { + let result = simulate( + r##" + (defcfg) + (defsrc a b c d) + (deflayer base + 1 2 3 (layer-while-held other) + ) + (deflayer other + 4 5 (layer-while-held src) XX + ) + (deflayer src + use-defsrc use-defsrc XX XX + ) + "##, + "d:d d:c d:b d:a t:100", + ) + .to_ascii(); + assert_eq!("t:2ms dn:B t:1ms dn:A", result); +} + +#[test] +fn use_defsrc_deflayermap() { + const CFG: &str = " + (defcfg process-unmapped-keys yes) + (defsrc a b c d) + (deflayer base + 1 + (layer-while-held othermap1) + (layer-while-held othermap2) + (layer-while-held othermap3) + ) + (deflayermap (othermap1) + a 5 + ___ use-defsrc + ) + (deflayermap (othermap2) + a 6 + __ use-defsrc + _ x + ) + (deflayermap (othermap3) + a 7 + _ use-defsrc + __ x + ) + "; + let result = simulate(CFG, "d:b d:a d:c d:e t:10").to_ascii(); + assert_eq!("t:1ms dn:Kb5 t:1ms dn:C t:1ms dn:E", result); + let result = simulate(CFG, "d:c d:a d:c d:e t:10").to_ascii(); + assert_eq!("t:1ms dn:Kb6 t:1ms dn:X t:1ms dn:E", result); + let result = simulate(CFG, "d:d d:a d:c d:e t:10").to_ascii(); + assert_eq!("t:1ms dn:Kb7 t:1ms dn:C t:1ms dn:X", result); +}