Rewrite Key Storage Implementation - #381
Conversation
df0a75b to
e6161c6
Compare
|
Another point of friction is the Currently, the Since I want to rewrite the key definitions implementation, for non-trivial keymaps it'd be useful to have a I expect that there's an assumption that After re-implementation/rewriting, perhaps such an assumption can be fixed. |
5c1e8f3 to
452e955
Compare
452e955 to
88c01fa
Compare
|
Getting closer to rewriting. The next blocker is the use of I think Rewriting away from tree-like |
0120c64 to
7784556
Compare
|
Right, so the general approach is:
That should allow ~most of the implemented functionality to still stay the same (Events, Context, PKS, KS). Though, without the need for the complex Implementation-wise:
|
|
I see that e.g.
(Ah ... I think |
6bda36f to
324f71b
Compare
|
Some progress. -- An integration test that checks "tap 'a'" now passes. The next steps, bring up all the integration tests (noting as a checklist in first comment). |
|
Hmm. Next tricky issue is coming up with the Nickel code to construct this "key data[], and key refs". Fortunately, I think the idea is as follows:
|
|
I kinda like the idea of (eventually):
|
ef060ca to
3f99acc
Compare
|
Hmm. The cucumber tests never touched keymap-codegen. Instead, they relied on the tree structure being deserializable by serde json. Instead... I guess keymap-codegen can take the keymap-json value (from km-ncl), and construct json-representation of the required data and refs. |
f317a9c to
c8accf3
Compare
|
Getting there. Currently, a vertical set of tests work, for the most trivial keymap feature ( But, it's clear that it's quite a comprehensive rewrite. So, it's going to be one big PR. |
|
The old implementation has some very awkward When trying to avoid these constants, (e.g. using something like I'm not sure if it's possible for all these cases, but probably the better way of arranging the code is passing interfaces around. |
|
Hmm. From what I can tell, the methods from the Previously, But, by using Instead.. |
eba68b2 to
5aad329
Compare
|
I've had friction with Rust trying to use some kind of Though, I was able to make use of a trait with various associated types, and this simplifies (shifts the complexity to places where it's easier to handle) generics for the various key data types. It terms of bringing up other keys? Progress seems smooth. (The work has been done to come up with the abstractions; now it's mostly just coming up with the I'm still curious to see if this does result in a reduction in firmware size. (Because there's no 'exponential' growth in sizes of the types). I'm optimistic that |
6809d8c to
702cc9f
Compare
|
"This branch cannot be rebased due to conflicts" I think GitHub's UI is confused. (At some point, this PR got closed because the code it pointed to got merged elsewhere). Anyway. Rewrote the layering system. -- The layering system is a bit neat in that it's got two kinds of keys (layer modifiers, layered keys). Definitely having a good suite of integration tests has been useful! |
|
Wow, Rust is difficult. It's incorrect ("not what I meant") to have |
|
Trying the 8key-artseyio keymap from #373, currently gets: SUCCESS! (Before, it was >105%, IIRC -- EDIT: not sure what's changed, but currently I see 95% when building just 8key for the ch32, going up to 97% when building with the 48key keymap. Still, ~95 down to ~60 is a big win). This validates the hope I had that this rewrite would result in a significant reduction in firmware size! |
|
Finally, all the keys which had been implemented have been rewritten. These ~170+ commits are all going to get squashed. Next, I'll brush up some of the codegen stuff, as well as restoring the CI, tests, etc.). |
eeb1bc2 to
3bde921
Compare
|
Memory usage at 54.47% for the CH32X firmware build: https://github.com/rgoulter/smart-keymap/actions/runs/17768309755?pr=381 Down from 78.51% https://github.com/rgoulter/smart-keymap/actions/runs/17526419123 |
There was a problem hiding this comment.
Pull Request Overview
This PR implements a comprehensive rewrite of the keymap system to address size and complexity issues. It transitions from tree-based key definitions to a reference-based array system for improved efficiency and maintainability.
Key changes include:
- Replacing tree-structured key definitions with flat arrays and references
- Simplifying the keymap initialization API
- Removing the complex tuple-based key definition system in favor of array-based storage
Reviewed Changes
Copilot reviewed 50 out of 52 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/rust/tap_hold.rs | Temporarily disables layered test module |
| tests/rust/keymap.rs | Updates keymap construction to use new array-based system |
| tests/ncl/*/expected.rs | Updates generated test expectations to match new keymap structure |
| tests/cucumber/keymap.rs | Adapts Cucumber tests to new keymap API |
| stm32f4-rtic-smart-keyboard/src/main.rs | Updates keymap initialization call |
| stm32-embassy-smart-keyboard/src/main.rs | Updates keymap initialization call |
| src/lib.rs | Major restructuring of init module and removal of tuples module |
| src/keymap.rs | Complete rewrite to use reference-based system instead of tree traversal |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
510e2f1 to
fca9096
Compare
|
The unit tests haven't yet been rewritten. During the rewrite, I leaned on the component/integration tests ( Still. I'd rather re-implement the unit tests to better spec / convey the interface expected of the various bits. I know the codebase well enough that if an integration test fails, I can quickly poke through to find what's gone wrong. But, there are plenty of assumptions (stuff like "does this key system emit an event on new_pressed_key?") such that complementing the implementation with a unit test couldn't hurt. Also, some of the |
fca9096 to
a921f73
Compare
|
I've removed some unit tests. (I'm satisfied with the I think the |
6af918f to
e4bae38
Compare
|
The codegen modules in |
Summary
This PR rewrites the key data mechanism of smart keymap. This benefits:
Previously: smart-keymap's main traits were
key::Key, and its associatedContext,Event,PendingKeyState,KeyStatetypes. Thekey::Keywas implemented in a nested tree structure, where types were nested usingkey::composite::{BaseKey, TapHold, TapHoldKey, Layered, LayeredKey, Chorded, ChordedKey}. -- Some motivation for moving away from this discussed below.This PR rewrites the
key::Keyandkey::KeyStatetraits to akey::Systemtrait; and splits from using&Keyto usingRefvalues to look upKeyvalues inSystem. Keys only nestRefvalues, and so key storage doesn't suffer exponential growth in storage size.Tasks
key::keyboard) so thekeymap!integration tests pass.key::keyboard(for "key code + modifier" keys).key::compositeas thekey_system.key::keyboardas thekey_system.initmodule (or equivalent) usable by the Rust firmware.doc_de_*, to the extent this helps clarify Nickel codegen output.keymap-codegen.ncl.Motivation and Discussion I've had an idea. It seems to me that the current implementation of the keymap (especially the key systems) is overcomplicated.
Motivating problems I think can be fixed with a better implementation:
I believe the current implementation takes up a larger firmware size than it needs to. (Size of
key::Keyimplementing values is exponential in some sense). c.f. the artseyio implementation is too big for CH32X. It smells like it should fit.I believe can be improved: the system requires a lot of boilerplate/overhead in order to implement a new smart key. (e.g. a "sequence key" would be like a sequential variant of a chorded key. But, this would be difficult to implement atm).
The
crate::init(smart_keymap::init) in order to get theKeymap(andKeyDefinitions) type, and the variousconsts (& references tocrate::init::Context). This is awkward/inelegant.Writing out the
KeyDefinitionsTypeforKeysNis too cumbersome to write out by hand; but, the codebase doesn't provide a simpler way of writing it out.Broadly, I reckon:
TapHold { tap: BaseKey::Keyboard, hold: BaseKey::Keyboard }, I imagineTapHold { tap: { tag: Kbd, ref: ... }, ... }.key::composite, instead shuffle around some of the abstractions so that each key type (keyboard, caps word, etc., tap hold, layered, chorded) is exposed as a "System".key::composite::Eventas the primary way of passing events around, theEventtype can remain internal to its own System. Onlykey::Event(input::Event,keymap::Event), need to cross the System interface.And, since this rewrite might make the keymap even harder to write by hand, might as well make use of a proc-macro to generate the code in Rust for the tests.