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}")