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

Enable recursion for callables in macros #1073

Merged
merged 3 commits into from
Feb 2, 2025
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
38 changes: 35 additions & 3 deletions docs/en/macros.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,12 +235,11 @@ DISCO = KC.MACRO(
)
```

### Example 2
### Example 2a

Here's a programmatic version of the earlier countdown-to-paste example, using a
generator.
Any return value that is not `None` is interpreted as a delay instruction in
milliseconds.
Any integer return value is interpreted as a delay instruction in milliseconds.

```python
def countdown(count, delay_ms):
Expand All @@ -262,8 +261,41 @@ COUNTDOWN_TO_PASTE = KC.MACRO(
)
```

### Example 2b

On popular demand: Callables in macros are fully recursive.
Here's a programmatic version of the earlier countdown example, using a
generator, but the countdown gets faster and there's a surprise at the end

```python
def countdown(count, delay_ms):
def generator(keyboard):
for n in range(count, 0, -1):
yield '{n}\n'.format(n)
yield n * delay_ms
yield '#🎉; open https://github.com/KMKfw/kmk_firmware/blob/main/docs/en/macros.md'
return generator

COUNTDOWN_TO_SURPRISE = KC.MACRO(
countdown(10, 100),
)
```

### Example 3

Sometimes there's no need for a generator and a simple function is enough to
type a string that's created at runtime.
And sometimes it's really hard to remember what keys are currently pressed:

```python
def keys_pressed(keyboard):
return str(keyboard.keys_pressed)

KEYS_PRESSED = KC.MACRO(keys_pressed)
```

### Example 4

A high productivity replacement for the common space key:
This macro ensures that you make good use of your time by measuring how long
you've been holding the space key for, printing the result to the debug
Expand Down
26 changes: 14 additions & 12 deletions kmk/modules/macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,13 @@ def post(keyboard):
def MacroIter(keyboard, macro, unicode_mode):
for item in macro:
if callable(item):
ret = item(keyboard)
if ret.__class__.__name__ == 'generator':
for _ in ret:
yield _
yield
else:
yield ret
item = item(keyboard)

if item is None:
yield

elif isinstance(item, int):
yield item

elif isinstance(item, str):
for char in item:
Expand All @@ -139,8 +139,7 @@ def MacroIter(keyboard, macro, unicode_mode):

else:
# unicode code points
for _ in unicode_mode.pre(keyboard):
yield _
yield from unicode_mode.pre(keyboard)
yield

for digit in hex(ord(char))[2:]:
Expand All @@ -150,12 +149,15 @@ def MacroIter(keyboard, macro, unicode_mode):
key.on_release(keyboard)
yield

for _ in unicode_mode.post(keyboard):
yield _
yield from unicode_mode.post(keyboard)
yield

elif item.__class__.__name__ == 'generator':
yield from MacroIter(keyboard, item, unicode_mode)
yield

elif debug.enabled:
debug('unsupported macro type', item.__class__.__name__)
debug('unsupported macro type ', item.__class__.__name__)


class Macros(Module):
Expand Down
3 changes: 2 additions & 1 deletion util/aspell.en.pws
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
personal_ws-1.1 en 363
personal_ws-1.1 en 364
ADNS
AMS
ANAVI
Expand All @@ -17,6 +17,7 @@ Boardsource
Budgy
CIRCUITPY
CRKBD
Callables
CapsWord
Carbonfet
Carbonfets
Expand Down