dotfiles/talon/user/community/plugin/cancel/cancel.py

72 lines
2 KiB
Python

# to disable command cancellation, comment out this entire file.
# you may also wish to adjust the commands in misc/cancel.talon.
import time
from talon import Context, Module, actions, speech_system
from talon.grammar import Phrase
# To change the phrase used to cancel commands, you must also adjust misc/cancel.talon
cancel_phrase = "cancel cancel".split()
mod = Module()
ctx = Context()
ts_threshold: float = 0
@ctx.action_class("speech")
class SpeechActions:
# When Talon wakes we set the timestamp threshold. On the next command we
# will compare the phrase timestamp to the threshold and cancel any phrase
# started before wakeup. This is to prevent speech said before wake-up to
# be interpreted as a command if the user wakes Talon using a noise or
# keypress.
def enable():
actions.user.cancel_current_phrase()
actions.next()
@mod.action_class
class Actions:
def cancel_current_phrase():
"""Cancel/abort current spoken phrase"""
global ts_threshold
ts_threshold = time.perf_counter()
def pre_phrase(phrase: Phrase):
global ts_threshold
words = phrase["phrase"]
if not words:
return
# Check if the phrase is before the threshold
if ts_threshold != 0:
start = getattr(words[0], "start", phrase["_ts"])
phrase_starts_before_threshold = start < ts_threshold
ts_threshold = 0
# Start of phrase is before threshold timestamp
if phrase_starts_before_threshold:
print(f"Canceled phrase: {' '.join(words)}")
cancel_entire_phrase(phrase)
return
# Check if the phrase is a cancel command
n = len(cancel_phrase)
before, after = words[:-n], words[-n:]
if after == cancel_phrase:
actions.app.notify(f"Command canceled: {' '.join(before)!r}")
cancel_entire_phrase(phrase)
return
def cancel_entire_phrase(phrase: Phrase):
phrase["phrase"] = []
if "parsed" in phrase:
phrase["parsed"]._sequence = []
speech_system.register("pre:phrase", pre_phrase)