caksoylar / keymap-drawer

Visualize keymaps that use advanced features like hold-taps and combos, with automatic parsing
https://caksoylar.github.io/keymap-drawer
MIT License
730 stars 62 forks source link

Feature request: binding inheritence or aliases #54

Closed MattSturgeon closed 1 year ago

MattSturgeon commented 1 year ago

I've noticed that since ZMK has so many duplicate keybind definitions, I end up with excessively large binding maps:

RET:
  type: enter
  tap: $$mdi:keyboard-return$$
RETURN:
  type: enter
  tap: $$mdi:keyboard-return$$
ENTER:
  type: enter
  tap: $$mdi:keyboard-return$$
ESC: $$mdi:keyboard-esc$$
ESCAPE: $$mdi:keyboard-esc$$
TAB: $$mdi:keyboard-tab$$
LS(TAB): $$mdi:keyboard-tab-reverse$$
SPACE: $$mdi:keyboard-space$$
BACKSPACE:
  type: backspace
  tap: $$mdi:backspace$$
BSPC:
  type: backspace
  tap: $$mdi:backspace$$
DELETE: $$mdi:backspace-reverse-outline$$
DEL: $$mdi:backspace-reverse-outline$$

LALT: 'Alt'
LEFT_ALT: 'Alt'
RALT: 'AltGr' # UK layout
RIGHT_ALT: 'AltGr' # UK layout
LCTRL: 'Ctrl'
LEFT_CONTROL: 'Ctrl'
RCTRL: 'Ctrl'
RIGHT_CONTROL: 'Ctrl'
LSHFT: $$mdi:apple-keyboard-shift$$
LSHIFT: $$mdi:apple-keyboard-shift$$
LEFT_SHIFT: $$mdi:apple-keyboard-shift$$
RSHFT: $$mdi:apple-keyboard-shift$$
RSHIFT: $$mdi:apple-keyboard-shift$$
RIGHT_SHIFT: $$mdi:apple-keyboard-shift$$
LGUI:
  tap: $$mdi:monitor-dashboard$$
  hold: Meta
  type: info
LEFT_GUI:
  tap: $$mdi:monitor-dashboard$$
  hold: Meta
  type: info
LMETA:
  tap: $$mdi:monitor-dashboard$$
  hold: Meta
  type: info
LEFT_META:
  tap: $$mdi:monitor-dashboard$$
  hold: Meta
  type: info

Solution 1: Inheritance

One strategy to to mitigate this would be an inherit, base, or extend field:

LGUI:
  tap: $$mdi:monitor-dashboard$$
  hold: Meta
  type: info
LEFT_GUI:
  base: LGUI
LMETA: {base: LGUI}
LEFT_META: {b: LGUI}

This could also be used for similar binds, as aything defined on the inheriting bind would override the base definition.

One complexity would be recursive binds. These should produce an error:

# Recursive
LEFT_GUI:
  base: LEFT_META
LEFT_META:
  b: LEFT_GUI

Approach 2: Aliases

A different approach might be to have an "also applies to" field:

# alias field accepts a list of strings
LGUI:
  alias: [ 'LEFT_GUI', 'LMETA', 'LEFT_META' ]
  tap: $$mdi:monitor-dashboard$$
  hold: Meta
  type: info

If a bind is defined multiple times, they could be combined (similar to inheritance) or simply let the one defined last override previous definitions (perhaps with a warning)?

# LEFT_GUI bind is defined twice
LGUI:
  alias: [ 'LEFT_GUI', 'LMETA', 'LEFT_META' ]
  tap: $$mdi:monitor-dashboard$$
  hold: Meta
  type: info
LEFT_GUI:
  tap: $$mdi:monitor-dashboard$$
  hold: Meta
  type: info

Approach 3: Parse map keys into lists

Another approach would be mapping list->bind instead of string->bind, however since yaml doesn't have syntax for that we'd have to invent our own:

# Space separated could work?
'LGUI LEFT_GUI LMETA LEFT_META':
  tap: $$mdi:monitor-dashboard$$
  hold: Meta
  type: info

Personally, I think the approach 2 (aliases) works best :thinking:

caksoylar commented 1 year ago

Hey, thanks for the nice thought out post. For this I prefer to use YAML anchors and aliases. Even though I am not a fan of most of YAML's features, this is one that came in handy frequently. Basically, you can assign an anchor to a node with &ref syntax and refer to it later via *ref, a contrived example:

LGUI: &lgui
  tap: $$mdi:monitor-dashboard$$
  held: Left
LEFT_GUI: *lgui
# above keys have identical values

RGUI:
  <<: *lgui
  held: Right
# overrides `held` field from above two

(As an aside, I find oq helpful to work with yamls, e.g. oq -i yaml -o simpleyaml can expand aliases like above and print the final result.)

You can see I (ab)use this in the keyboard layouts YAML to reduce repetition: https://github.com/caksoylar/keymap-drawer/blob/main/resources/zmk_keyboard_layouts.yaml

This is similar to your approach 1 in principle, except it is using the configuration syntax features rather than a feature from the keymap-drawer side. Let me know what you think, i.e. if that is good enough. I'd prefer to not add complexity if not needed.

MattSturgeon commented 1 year ago

Thanks. I didn't know yaml had an alias system, that'll come in handy.

That does exactly what I was asking for, probably better than any of my proposals.