111 lines
3.7 KiB
Python
111 lines
3.7 KiB
Python
from typing import Callable
|
|
|
|
from talon import actions
|
|
|
|
from .versions import COMMAND_VERSION
|
|
|
|
# This ensures that we remember to update fallback if the response payload changes
|
|
assert COMMAND_VERSION == 7
|
|
|
|
action_callbacks = {
|
|
"getText": lambda: [actions.edit.selected_text()],
|
|
"setSelection": actions.skip,
|
|
"setSelectionBefore": actions.edit.left,
|
|
"setSelectionAfter": actions.edit.right,
|
|
"copyToClipboard": actions.edit.copy,
|
|
"cutToClipboard": actions.edit.cut,
|
|
"pasteFromClipboard": actions.edit.paste,
|
|
"clearAndSetSelection": actions.edit.delete,
|
|
"remove": actions.edit.delete,
|
|
"editNewLineBefore": actions.edit.line_insert_up,
|
|
"editNewLineAfter": actions.edit.line_insert_down,
|
|
}
|
|
|
|
modifier_callbacks = {
|
|
"extendThroughStartOf.line": actions.user.select_line_start,
|
|
"extendThroughEndOf.line": actions.user.select_line_end,
|
|
"containingScope.document": actions.edit.select_all,
|
|
"containingScope.paragraph": actions.edit.select_paragraph,
|
|
"containingScope.line": actions.edit.select_line,
|
|
"containingScope.token": actions.edit.select_word,
|
|
}
|
|
|
|
|
|
def call_as_function(callee: str):
|
|
wrap_with_paired_delimiter(f"{callee}(", ")")
|
|
|
|
|
|
def wrap_with_paired_delimiter(left: str, right: str):
|
|
selected = actions.edit.selected_text()
|
|
actions.insert(f"{left}{selected}{right}")
|
|
for _ in right:
|
|
actions.edit.left()
|
|
|
|
|
|
def containing_token_if_empty():
|
|
if actions.edit.selected_text() == "":
|
|
actions.edit.select_word()
|
|
|
|
|
|
def perform_fallback(fallback: dict):
|
|
try:
|
|
modifier_callbacks = get_modifier_callbacks(fallback)
|
|
action_callback = get_action_callback(fallback)
|
|
for callback in reversed(modifier_callbacks):
|
|
callback()
|
|
return action_callback()
|
|
except ValueError as ex:
|
|
actions.app.notify(str(ex))
|
|
raise ex
|
|
|
|
|
|
def get_action_callback(fallback: dict) -> Callable:
|
|
action = fallback["action"]
|
|
|
|
if action in action_callbacks:
|
|
return action_callbacks[action]
|
|
|
|
match action:
|
|
case "insert":
|
|
return lambda: actions.insert(fallback["text"])
|
|
case "callAsFunction":
|
|
return lambda: call_as_function(fallback["callee"])
|
|
case "wrapWithPairedDelimiter":
|
|
return lambda: wrap_with_paired_delimiter(
|
|
fallback["left"], fallback["right"]
|
|
)
|
|
|
|
raise ValueError(f"Unknown Cursorless fallback action: {action}")
|
|
|
|
|
|
def get_modifier_callbacks(fallback: dict) -> list[Callable]:
|
|
return [get_modifier_callback(modifier) for modifier in fallback["modifiers"]]
|
|
|
|
|
|
def get_modifier_callback(modifier: dict) -> Callable:
|
|
modifier_type = modifier["type"]
|
|
|
|
match modifier_type:
|
|
case "containingTokenIfEmpty":
|
|
return containing_token_if_empty
|
|
case "containingScope":
|
|
scope_type_type = modifier["scopeType"]["type"]
|
|
return get_simple_modifier_callback(f"{modifier_type}.{scope_type_type}")
|
|
case "preferredScope":
|
|
scope_type_type = modifier["scopeType"]["type"]
|
|
return get_simple_modifier_callback(f"containingScope.{scope_type_type}")
|
|
case "extendThroughStartOf":
|
|
if "modifiers" not in modifier:
|
|
return get_simple_modifier_callback(f"{modifier_type}.line")
|
|
case "extendThroughEndOf":
|
|
if "modifiers" not in modifier:
|
|
return get_simple_modifier_callback(f"{modifier_type}.line")
|
|
|
|
raise ValueError(f"Unknown Cursorless fallback modifier: {modifier_type}")
|
|
|
|
|
|
def get_simple_modifier_callback(key: str) -> Callable:
|
|
try:
|
|
return modifier_callbacks[key]
|
|
except KeyError:
|
|
raise ValueError(f"Unknown Cursorless fallback modifier: {key}")
|