Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add reverse-release-order action #1240

Merged
merged 6 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions cfg_samples/kanata.kbd
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,10 @@ If you need help, please feel welcome to ask in the GitHub discussions.
;; This use case for multi is typing an all-caps string.
alp (multi lsft a b c d e f g h i j k l m n o p q r s t u v w x y z)

;; Within multi you can also include reverse-release-order to release keys
;; from last-to-first order instead of first-to-last which is the default.
S-a-reversed (multi lsft a reverse-release-order)

;; Chords using the shortcut syntax. These ones are used for copying/pasting
;; from some Linux terminals.
csv C-S-v
Expand Down Expand Up @@ -708,7 +712,7 @@ If you need help, please feel welcome to ask in the GitHub discussions.
;;
;; The command takes two extra arguments at the beginning `<log_level>`,
;; and `<error_log_level>`. `<log_level>` controls where the name
;; of the command is logged, as well as the success message and command
;; of the command is logged, as well as the success message and command
;; stdout and stderr.
;;
;; `<error_log_level>` is only used if there is a failure executing the initial
Expand All @@ -729,7 +733,7 @@ If you need help, please feel welcome to ask in the GitHub discussions.
;; of the command and treats it as an S-Expression, similarly to `macro`.
;; However, only delays, keys, chords, and chorded lists are supported.
;; Other actions are not.
;;
;;
;; bash: type date-time as YYYY-MM-DD HH:MM
;; cmd-output-keys bash -c "date +'%F %R' | sed 's/./& /g' | sed 's/:/S-;/g' | sed 's/\(.\{20\}\)\(.*\)/\(\1 spc \2\)/'"
)
Expand Down
21 changes: 21 additions & 0 deletions docs/config.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,27 @@ and that would work as intended.
It is recommended to avoid `multi` if it can be replaced
with a different action like `macro` or an output chord.

==== reverse-release-order

Within `multi` you can use include `reverse-release-order`
to do what the action states: reverse the typical release order from
if you have multiple keys in multi.

For example, pressing then releasing a key with the action:
`(multi a b c)` would press a b c in the stated order
and then release a b c in the stated order.
Changing it to `(multi a b c reverse-release-order)`
would press a b c in the stated order
and then release c b a in the stated order.

.Example:
[source]
----
(defalias
S-a-reversed (multi lsft a reverse-release-order)
)
----

[[mouse-actions]]
=== Mouse actions
<<table-of-contents,Back to ToC>>
Expand Down
14 changes: 13 additions & 1 deletion parser/src/cfg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1207,6 +1207,7 @@ pub struct ParserState {
block_unmapped_keys: bool,
switch_max_key_timing: Cell<u16>,
trans_forbidden_reason: Option<&'static str>,
multi_action_nest_count: Cell<u16>,
pub lsp_hints: RefCell<LspHints>,
a: Arc<Allocations>,
}
Expand Down Expand Up @@ -1237,6 +1238,7 @@ impl Default for ParserState {
block_unmapped_keys: default_cfg.block_unmapped_keys,
switch_max_key_timing: Cell::new(0),
trans_forbidden_reason: None,
multi_action_nest_count: Cell::new(0),
lsp_hints: Default::default(),
a: unsafe { Allocations::new() },
}
Expand Down Expand Up @@ -1476,7 +1478,6 @@ fn parse_action_atom(ac_span: &Spanned<String>, s: &ParserState) -> Result<&'sta
"This is a list action and must be in parentheses: ({ac} ...)"
);
}

match ac {
"_" | "‗" | "≝" => {
if let Some(trans_forbidden_reason) = s.trans_forbidden_reason {
Expand Down Expand Up @@ -1546,6 +1547,13 @@ fn parse_action_atom(ac_span: &Spanned<String>, s: &ParserState) -> Result<&'sta
"dynamic-macro-record-stop" => {
return custom(CustomAction::DynamicMacroRecordStop(0), &s.a)
}
"reverse-release-order" => match s.multi_action_nest_count.get() {
0 => bail_span!(
ac_span,
"reverse-release-order is only allowed inside of a (multi ...) action list"
),
_ => return custom(CustomAction::ReverseReleaseOrder, &s.a),
},
_ => {}
};
if let Some(oscode) = str_to_oscode(ac) {
Expand Down Expand Up @@ -1917,6 +1925,8 @@ fn parse_multi(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAc
if ac_params.is_empty() {
bail!("multi expects at least one item after it")
}
s.multi_action_nest_count
.replace(s.multi_action_nest_count.get().saturating_add(1));
let mut actions = Vec::new();
let mut custom_actions: Vec<&'static CustomAction> = Vec::new();
for expr in ac_params {
Expand Down Expand Up @@ -1966,6 +1976,8 @@ fn parse_multi(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAc
bail!("Cannot combine multiple tap-hold/tap-dance/chord");
}

s.multi_action_nest_count
.replace(s.multi_action_nest_count.get().saturating_sub(1));
Ok(s.a.sref(Action::MultipleActions(s.a.sref(s.a.sref_vec(actions)))))
}

Expand Down
13 changes: 13 additions & 0 deletions parser/src/cfg/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2013,3 +2013,16 @@ fn disallow_whitespace_in_tooltip_size() {
";
parse_cfg(source).map(|_| ()).expect_err("fails");
}

#[test]
fn reverse_release_order_must_be_within_multi() {
let source = "
(defsrc a)
(deflayer base reverse-release-order)
";
let e = parse_cfg(source).map(|_| ()).expect_err("fails");
assert_eq!(
e.msg,
"reverse-release-order is only allowed inside of a (multi ...) action list"
);
}
1 change: 1 addition & 0 deletions parser/src/custom_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ pub enum CustomAction {
Unshifted {
keys: Box<[KeyCode]>,
},
ReverseReleaseOrder,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
Expand Down
12 changes: 11 additions & 1 deletion src/kanata/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,7 @@ impl Kanata {
let mut live_reload_requested = false;
let cur_keys = &mut self.cur_keys;
cur_keys.extend(layout.keycodes());
let mut reverse_release_order = false;

// Deal with unmodded. Unlike other custom actions, this should come before key presses and
// releases. I don't quite remember why custom actions come after the key processing, but I
Expand Down Expand Up @@ -944,6 +945,9 @@ impl Kanata {
CustomAction::Unshifted { keys } => {
self.unshifted_keys.retain(|k| !keys.contains(k));
}
CustomAction::ReverseReleaseOrder => {
reverse_release_order = true;
}
_ => {}
}
}
Expand Down Expand Up @@ -1027,7 +1031,11 @@ impl Kanata {
// Given that there appears to be no practical negative consequences for this bug
// remaining.
log::trace!("{:?}", &self.prev_keys);
for k in &self.prev_keys {
let keys: &mut dyn Iterator<Item = &KeyCode> = match reverse_release_order {
false => &mut self.prev_keys.iter(),
true => &mut self.prev_keys.iter().rev(),
};
for k in keys {
if cur_keys.contains(k) {
continue;
}
Expand Down Expand Up @@ -1492,6 +1500,8 @@ impl Kanata {
| CustomAction::DelayOnRelease(_)
| CustomAction::Unmodded { .. }
| CustomAction::Unshifted { .. }
// Note: ReverseReleaseOrder is already handled earlier on.
| CustomAction::ReverseReleaseOrder { .. }
| CustomAction::CancelMacroOnRelease => {}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/tests/sim_tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod capsword_sim_tests;
mod chord_sim_tests;
mod layer_sim_tests;
mod override_tests;
mod release_sim_tests;
mod repeat_sim_tests;
mod seq_sim_tests;
mod switch_sim_tests;
Expand Down
31 changes: 31 additions & 0 deletions src/tests/sim_tests/release_sim_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use super::*;

#[test]
fn release_standard() {
let result = simulate(
"
(defsrc a)
(deflayer base (multi lalt a))
",
"
d:a t:10 u:a t:10
",
)
.to_ascii();
assert_eq!("dn:LAlt dn:A t:10ms up:LAlt up:A", result);
}

#[test]
fn release_reversed() {
let result = simulate(
"
(defsrc a)
(deflayer base (multi lalt a reverse-release-order))
",
"
d:a t:10 u:a t:10
",
)
.to_ascii();
assert_eq!("dn:LAlt dn:A t:10ms up:A up:LAlt", result);
}
Loading