From b0a4129c4fe879d3562e082808a97e7a5e31e9f3 Mon Sep 17 00:00:00 2001 From: jtroo Date: Mon, 11 Nov 2024 22:07:26 -0800 Subject: [PATCH] add combined action, add tests, fix a bug --- cfg_samples/kanata.kbd | 2 ++ docs/config.adoc | 20 +++++++++++++++++-- parser/src/cfg/list_actions.rs | 6 ++++++ parser/src/cfg/mod.rs | 27 ++++++++++++++++++++++++++ src/kanata/mod.rs | 4 ++-- src/tests/sim_tests/macro_sim_tests.rs | 7 +++++++ 6 files changed, 62 insertions(+), 4 deletions(-) diff --git a/cfg_samples/kanata.kbd b/cfg_samples/kanata.kbd index 725f3dc00..b75279e05 100644 --- a/cfg_samples/kanata.kbd +++ b/cfg_samples/kanata.kbd @@ -605,12 +605,14 @@ If you need help, please feel welcome to ask in the GitHub discussions. rls (macro-release-cancel Digit1 500 bspc S-1 500 bspc S-2) cop (macro-cancel-on-press Digit1 500 bspc S-1 500 bspc S-2) + rlpr (macro-release-cancel-and-cancel-on-press Digit1 500 bspc S-1 500 bspc S-2) ;; repeat variants will repeat while held, once ALL macros have ended, ;; including the held macro. mr1 (macro-repeat mltp) mr2 (macro-repeat-release-cancel mltp) mr3 (macro-repeat-cancel-on-press mltp) + mr4 (macro-repeat-release-cancel-and-cancel-on-press mltp) ;; Kanata also supports dynamic macros. Dynamic macros can be nested, but ;; cannot recurse. diff --git a/docs/config.adoc b/docs/config.adoc index 6c248b3c3..916bf9b88 100644 --- a/docs/config.adoc +++ b/docs/config.adoc @@ -1422,7 +1422,7 @@ needs the delay of `5` to work correctly. ---- [[macro-release-cancel]] -==== `macro-release-cancel` +==== macro-release-cancel The `macro-release-cancel` variant of the `+macro+` action will cancel all active macros @@ -1450,7 +1450,7 @@ and the rest of the macro does not run. ---- [[macro-cancel-on-press]] -==== `macro-cancel-on-press` +==== macro-cancel-on-press The `macro-cancel-on-press` variant of the `macro action` enables a cancellation trigger for all active macros including itself, @@ -1465,6 +1465,21 @@ The trigger is enabled while the macro is in progress. ) ---- +[[macro-release-cancel-and-cancel-on-press]] +==== macro-release-cancel-and-cancel-on-press + +The `macro-release-cancel-and-cancel-on-press` variant +combines the cancel behaviours +of both the release-cancel and cancel-on-press. + +[source] +---- +(defalias + 1 1 + 1!@ (macro-release-cancel-and-cancel-on-press @1 500 bspc S-1 500 bspc S-2) +) +---- + [[macro-repeat]] ==== macro-repeat @@ -1482,6 +1497,7 @@ only the most recently pressed macro will be repeated. mr1 (macro-repeat mltp) mr2 (macro-repeat-release-cancel mltp) mr3 (macro-repeat-cancel-on-press mltp) + mr4 (macro-repeat-release-cancel-and-cancel-on-press mltp) ) ---- diff --git a/parser/src/cfg/list_actions.rs b/parser/src/cfg/list_actions.rs index 220123c89..e8103af45 100644 --- a/parser/src/cfg/list_actions.rs +++ b/parser/src/cfg/list_actions.rs @@ -28,6 +28,10 @@ pub const MACRO_REPEAT_RELEASE_CANCEL: &str = "macro-repeat-release-cancel"; pub const MACRO_REPEAT_RELEASE_CANCEL_A: &str = "macro⟳↑⤫"; pub const MACRO_CANCEL_ON_NEXT_PRESS: &str = "macro-cancel-on-press"; pub const MACRO_REPEAT_CANCEL_ON_NEXT_PRESS: &str = "macro-repeat-cancel-on-press"; +pub const MACRO_CANCEL_ON_NEXT_PRESS_CANCEL_ON_RELEASE: &str = + "macro-release-cancel-and-cancel-on-press"; +pub const MACRO_REPEAT_CANCEL_ON_NEXT_PRESS_CANCEL_ON_RELEASE: &str = + "macro-repeat-release-cancel-and-cancel-on-press"; pub const UNICODE: &str = "unicode"; pub const SYM: &str = "🔣"; pub const ONE_SHOT: &str = "one-shot"; @@ -225,6 +229,8 @@ pub fn is_list_action(ac: &str) -> bool { ON_IDLE, MACRO_CANCEL_ON_NEXT_PRESS, MACRO_REPEAT_CANCEL_ON_NEXT_PRESS, + MACRO_CANCEL_ON_NEXT_PRESS_CANCEL_ON_RELEASE, + MACRO_REPEAT_CANCEL_ON_NEXT_PRESS_CANCEL_ON_RELEASE, ]; LIST_ACTIONS.contains(&ac) } diff --git a/parser/src/cfg/mod.rs b/parser/src/cfg/mod.rs index 5cd8a88cc..f151c4f67 100755 --- a/parser/src/cfg/mod.rs +++ b/parser/src/cfg/mod.rs @@ -1750,6 +1750,12 @@ fn parse_action_list(ac: &[SExpr], s: &ParserState) -> Result<&'static KanataAct MACRO_REPEAT_CANCEL_ON_NEXT_PRESS => { parse_macro_cancel_on_next_press(&ac[1..], s, RepeatMacro::Yes) } + MACRO_CANCEL_ON_NEXT_PRESS_CANCEL_ON_RELEASE => { + parse_macro_cancel_on_next_press_cancel_on_release(&ac[1..], s, RepeatMacro::No) + } + MACRO_REPEAT_CANCEL_ON_NEXT_PRESS_CANCEL_ON_RELEASE => { + parse_macro_cancel_on_next_press_cancel_on_release(&ac[1..], s, RepeatMacro::Yes) + } UNICODE | SYM => parse_unicode(&ac[1..], s), ONE_SHOT | ONE_SHOT_PRESS | ONE_SHOT_PRESS_A => { parse_one_shot(&ac[1..], s, OneShotEndConfig::EndOnFirstPress) @@ -2156,6 +2162,27 @@ fn parse_macro_cancel_on_next_press( ]))))) } +fn parse_macro_cancel_on_next_press_cancel_on_release( + ac_params: &[SExpr], + s: &ParserState, + repeat: RepeatMacro, +) -> Result<&'static KanataAction> { + let macro_action = parse_macro(ac_params, s, repeat)?; + let macro_duration = match macro_action { + Action::RepeatableSequence { events } | Action::Sequence { events } => { + macro_sequence_event_total_duration(events) + } + _ => unreachable!("parse_macro should return sequence action"), + }; + Ok(s.a.sref(Action::MultipleActions(s.a.sref(s.a.sref_vec(vec![ + *macro_action, + Action::Custom( + s.a.sref(s.a.sref_slice(CustomAction::CancelMacroOnNextPress(macro_duration))), + ), + Action::Custom(s.a.sref(s.a.sref_slice(CustomAction::CancelMacroOnRelease))), + ]))))) +} + fn macro_sequence_event_total_duration(events: &[SequenceEvent]) -> u32 { events.iter().fold(0, |duration, event| { duration.saturating_add(match event { diff --git a/src/kanata/mod.rs b/src/kanata/mod.rs index ef3690c70..f35cc567b 100755 --- a/src/kanata/mod.rs +++ b/src/kanata/mod.rs @@ -689,7 +689,7 @@ impl Kanata { layout.active_sequences.clear(); layout .states - .retain(|s| !matches!(s, State::FakeKey { .. })); + .retain(|s| !matches!(s, State::FakeKey { .. } | State::RepeatingSequence { .. })); } Event::Press(0, evc) } @@ -1653,7 +1653,7 @@ impl Kanata { self.macro_on_press_cancel_duration = 0; layout .states - .retain(|s| !matches!(s, State::FakeKey { .. })); + .retain(|s| !matches!(s, State::FakeKey { .. } | State::RepeatingSequence { .. })); pbtn } CustomAction::SendArbitraryCode(code) => { diff --git a/src/tests/sim_tests/macro_sim_tests.rs b/src/tests/sim_tests/macro_sim_tests.rs index 39fece90b..afbf45f82 100644 --- a/src/tests/sim_tests/macro_sim_tests.rs +++ b/src/tests/sim_tests/macro_sim_tests.rs @@ -5,7 +5,14 @@ fn macro_cancel_on_press() { let cfg = "\ (defsrc a b c) (deflayer base (macro-cancel-on-press z 100 y) (macro x 100 w) c)"; + test_on_press(cfg); + let cfg = "\ +(defsrc a b c) +(deflayer base (macro-repeat-cancel-on-press z 100 y 100) (macro x 100 w) c)"; + test_on_press(cfg); +} +fn test_on_press(cfg: &str) { // Cancellation should happen. let result = simulate(cfg, "d:a t:50 d:c t:100").to_ascii(); assert_eq!("t:1ms dn:Z t:1ms up:Z t:48ms dn:C", result);