Skip to content

Instantly share code, notes, and snippets.

@rntz
Created March 31, 2022 12:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rntz/5575959604198bceb0be4762d38a8612 to your computer and use it in GitHub Desktop.
Save rntz/5575959604198bceb0be4762d38a8612 to your computer and use it in GitHub Desktop.
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)
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