Created
March 31, 2022 12:47
-
-
Save rntz/5575959604198bceb0be4762d38a8612 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from talon import Module, Context, actions | |
from typing import Any, NamedTuple, Union, Optional | |
from dataclasses import dataclass | |
import logging | |
mod = Module() | |
ctx = Context() | |
# Types. | |
Motion = Union[str, tuple] | |
# Motion & region captures | |
# TODO: paragraphs, sentences | |
@mod.capture(rule=""" | |
line <number> | top | bottom | head | tail | |
| [<number_small>] (up|down|left|right) | |
| [<number_small>] (word|words) (left|right) | |
""") | |
def edit_motion(m) -> Any: | |
"""A cursor motion.""" | |
if m[0] == 'line': return ('jump_line', m.number) | |
if m[0] == 'top': return 'file_start' | |
if m[0] == 'bottom': return 'file_end' | |
if m[0] == 'head': return 'line_start' | |
if m[0] == 'tail': return 'line_end' | |
if (direction := str(m[-1])) in {'up','down','left','right'}: | |
motion = direction | |
if 'word' in m or 'words' in m: | |
assert direction in {'left', 'right'} | |
motion = 'word_' + direction | |
try: return (m.number_small, motion) | |
except: return motion | |
raise ValueError(f"Unrecognized motion: {str(m)}") | |
# Actions | |
@mod.action_class | |
class ModuleActions: | |
def edit_go(motion: Motion): | |
"""Moves the cursor according to `motion`.""" | |
# implementation is recursive, and actions can't call themselves | |
edit_go(motion) | |
def edit_go_faster(motion: Motion): | |
"""Called by `edit_go`. Override this method to provide optimized ways of implementing movement for your editor that CAN'T be implemented by just overriding the corresponding actions. Call actions.next(motion) if you encounter a pattern you can't optimize to fall back on default behavior.""" | |
raise NotImplementedError() | |
def edit_go(motion): | |
# Invoke the override but fall back if it isn't implemented for this motion. | |
try: return actions.user.edit_go_faster(motion) | |
except NotImplementedError: pass | |
# A motion can be: | |
# - stem: a string identifying an action, like 'up' or 'line_start' | |
# - (stem, *args): an action with some arguments, like 'jump_line' | |
# - (number, motion): a repeated motion | |
# TODO: sequencing motions | |
action, *args = motion if isinstance(motion, tuple) else (motion,) | |
if isinstance(action, int): # repeats | |
(motion,) = args | |
for _ in range(action): edit_go(motion) | |
return | |
try: getattr(actions.edit, action)(*args) | |
except KeyError: | |
# No such method, try user namespace. | |
getattr(actions.user, action)(*args) | |
# Example of overriding in emacs for efficiency | |
emacs_ctx = Context() | |
emacs_ctx.matches = 'app: Emacs' | |
@emacs_ctx.action_class('user') | |
class EmacsActions: | |
def edit_go_faster(motion): | |
# WTB pattern matching | |
if isinstance(motion, tuple) and len(motion) == 2 and isinstance(motion[0], int): | |
(n, inner) = motion | |
if n > 2 and inner in {'up', 'down', 'left', 'right', 'word_left', 'word_right'}: | |
logging.info('optimized movement') | |
actions.user.emacs_prefix(n) | |
getattr(actions.edit, inner)() | |
return | |
actions.next(motion) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
magic go <user.edit_motion>: user.edit_go(edit_motion) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment