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: implemented an alt-repeat key #1392

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

Nuclear-Squid
Copy link

@Nuclear-Squid Nuclear-Squid commented Nov 29, 2024

Describe your changes. Use imperative present tense.

Implemented an alt-repeat key. It functions the same as the standard repeat key, but takes in a list of pairs of keycodes to give substitutions. For instance, you can repeat keys as usual, but send e instead of d when repeating the d key.

A possible use-case for such a feature is to implement the "magic key" from Magic-Sturdy

Disclaimer : I am not a native English speaker, I am sorry if there are typos in the docs ^^'

Checklist

  • Add documentation to docs/config.adoc
    • Yes or N/A
  • Add example and basic docs to cfg_samples/kanata.kbd
    • Yes or N/A
  • Update error messages
    • Yes or N/A
  • Added tests, or did manual testing
    • Yes

@Nuclear-Squid Nuclear-Squid changed the title Alt repeat feat: implemented an alt-repeat key Nov 29, 2024
@Nuclear-Squid
Copy link
Author

This implementation only allows simple KeyCode -> KeyCode substitutions, which is still pretty limited. It’s not possible to repeat macros for instance (or alt-repeat a symbol with a macro), and rpt doesn’t seem to work with sequences (a sequence gg isn’t detected if you type it as g rpt). I’d like to help make that system more flexible, would you be interested in that ?

@jtroo
Copy link
Owner

jtroo commented Nov 29, 2024

Thanks for the contribution! Could you comment on the advantages of this action over switch?

https://jtroo.github.io/config.html#switch

@Nuclear-Squid
Copy link
Author

I added a small section at the end of the alt-repeat and switch’s key-history to reference each other. Is that good ?

@jtroo
Copy link
Owner

jtroo commented Dec 2, 2024

I added a small section at the end of the alt-repeat and switch’s key-history to reference each other. Is that good ?

It's more like - if the end goal is to implement magic sturdy, this PR could instead be a "tutorial" style document of using switch to implement magic sturdy.

To minimize code bloat and maintenance burden, it is desirable to have fewer ways to solve a problem. To add more flavour and context, Kanata does have different ways to do things over time as it has grown, though each added item has been written to solve a previously unsolved use case. For example; there are 3 different chording solutions:

  • the first implementation, a "within layer processing" action chord system
  • the second implementation, a "pre-layer processing" chord system inspired by ZMK, solving some issues with the first implementation
  • the third implementation, a "post-layer processing" chord system inspired more by zippychord/plover, enabling new use cases not possible (or very difficult to do) in the first and second implementations

There were strong reasons why the multiple chording solutions all exist.

Then there is switch and fork.

The fork action was implemented first and is the simpler variant. The switch action came later and is the more capable, implementing all use cases of fork and more. Already having the switch action, what problem does alt-repeat solve that is not already solved by using switch, where the base case is () rpt break?


(a sequence gg isn’t detected if you type it as g rpt).

Yea the sequences code paths aren't triggered by rpt today unfortunately. I don't make use of rpt or rpt-any myself so I don't have any motivation to fix it 😅. A workaround today would be listing every alphabet character in a switch case with history and outputting the appropriate character.

@jtroo jtroo added the gauging interest Looking for additional discussion and viewpoints label Dec 10, 2024
@Nuclear-Squid
Copy link
Author

Hey ! Sorry for ghosting you for like two weeks, I got absolutely slammed by homework in uni.

To be absolutely real with you, I didn’t know switch existed before writing this PR. I don’t really know how I missed it, but it does seem like it could implement magic sturdy. (Also sorry for misunderstanding you in your comment, I’m kind of an idiot 😅)

The advantages of alt-repeat over switch

I feel like an alt-repeat key could be the "simpler variant" of a switch with key-history cases since the syntax is a lot less verbose (kind of like fork, in a way, but it comes in the software after switch…). Right now, my implementation is somewhat limited (like I said), but if we get to the point where we can alt-repeat a macro with another macro, it could turn this :

(defalias
  magic-key (switch
    ((and (key-history a 3) (key-history b 2) (key-history c 1)) @macro2 break
    () rpt break
  )
)

into this (which I feel is a lot simpler) :

(defalias magic-key (arpt @macro1 @macro2))

It does have the benefit of not having to rewrite what your alias expands to (which in my mind greatly improves the ease of configuration), but in practice we’re essentially gaining a more compact and expressive syntax. Which is a whole lot less than what I thought we would get when I first opened this PR (since that was before you told me about switch), not to mention that it’s probably a pretty niche feature.

While I would still like to help improve the repeat key system (like to fix the sequences, or repeat macros, if that’s something you or the community wants), this alt-repeat key idea may not be worth it.

Now what ?

If switch isn’t great at matching macros, and you’re not that interested by my alt-repeat key (since even the "ultimate version" of my alt-repeat key is only a subset of switch but more compact, and you would prefer not duplicating features around; which I totally get), then improving switch could be a better solution. I have no idea how hard it would be to implement, but being able to have any kind of action or alias in a key-history could simplify my example quite a bit :

(defalias
  magic-key (switch
    (key-history @macro1 1) @macro2 break
    () rpt break
  )
)

The syntax is still a tad verbose for my liking, but it dramatically improves the ease of configuration (my main problem with switch). I’m also guessing that if we were to add this feature, then we’d have a way of remembering the last action instead of the last keycode, which could greatly help making the repeat key more flexible.

(and if I add to my Christmas list the incredibly unrealistic request of being able to iterate on a list in a template, then we could write the alt-repeat as a super simple template-expand !)

(defalias magic-key (t! alt-repeat (@macro1 @macro2)))

On a more serious note, is allowing key-history to match any action in an alias something you’d like me to work on ? I’m probably neglecting something that makes implementing this feature more complicated than what I imagine, and it’s probably something for another PR, but I’m curious what you think of it ^^

Due to uni, I probably wont be able to write much code up until mid January, but I am motivated ^^

@Nuclear-Squid Nuclear-Squid marked this pull request as draft December 15, 2024 14:45
@jtroo
Copy link
Owner

jtroo commented Dec 16, 2024

The backing types - and at a higher level, the backing concepts - of key history switch logic and macros are not trivially convertible. A macro has more capabilities than simply typing keys. I suppose only the mappable subset of macro can be "converted" to switch logic when used in that position.

macro:

pub enum SequenceEvent<'a, T: 'a> {
/// No operation action: just do nothing (a placeholder).
NoOp,
/// A keypress/keydown
Press(KeyCode),
/// Key release/keyup
Release(KeyCode),
/// A shortcut for `Press(KeyCode), Release(KeyCode)`
Tap(KeyCode),
/// For sequences that need to wait a bit before continuing
Delay {
/// How long (in ticks) this Delay will last
duration: u32, // NOTE: This isn't a u16 because that's only max ~65 seconds (assuming 1000 ticks/sec)
},
/// Custom event in sequence.
Custom(&'a T),
/// Cancels the running sequence and can be used to mark the end of a sequence
/// instead of using a number of Release() events
Complete,
}

key history:

pub struct OpCode(u16);
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
/// The more useful interpretion of an OpCode.
enum OpCodeType {
BooleanOp(OperatorAndEndIndex),
KeyCode(u16),
HistoricalKeyCode(HistoricalKeyCode),
Input(KCoord),
HistoricalInput(HistoricalInput),
TicksSinceLessThan(TicksSinceNthKey),
TicksSinceGreaterThan(TicksSinceNthKey),
Layer(u16),
BaseLayer(u16),
}

pub fn new_key_history(kc: KeyCode, key_recency: u8) -> Self {
assert!((kc as u16) <= MAX_OPCODE_LEN);
assert!(key_recency <= MAX_KEY_RECENCY);
Self((kc as u16 & MAX_OPCODE_LEN) | HISTORICAL_KEYCODE_VAL | ((key_recency as u16) << 12))
}

allowing key-history to match any action in an alias something

You should consider whether input-history solves the problem of matching "arbitrary actions", given that arbitrary actions tend to be mapped to known keys. Though this does have missing information of: on what layer did that activate? This may or may not be sufficient, in which case an action history could be created given that actions do implement PartialEq.

Though, if you mean parsing the underlying key output order of arbitrary actions and mapping that to a key history, such that typing the same key history outside of using that action results in true for the switch logic, that sounds quite complex; given that action outputs are non-deterministic.

iterate on a list in a template

This to me would be the most fruitful improvement to implement. I don't see any issues with adding more functionality in templates, and it could help in plenty of other actions outside of switch. I don't expect it would be too challenging to implement either.

https://github.com/jtroo/kanata/blob/main/parser/src/cfg/deftemplate.rs#L45

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
gauging interest Looking for additional discussion and viewpoints
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants