import dataclasses from typing import Any from talon import Module, actions, speech_system from .fallback import perform_fallback from .versions import COMMAND_VERSION @dataclasses.dataclass class CursorlessCommand: version = COMMAND_VERSION spokenForm: str usePrePhraseSnapshot: bool action: dict CURSORLESS_COMMAND_ID = "cursorless.command" last_phrase: dict = {} mod = Module() def on_phrase(d): global last_phrase last_phrase = d speech_system.register("pre:phrase", on_phrase) @mod.action_class class Actions: def private_cursorless_command_and_wait(action: dict): # pyright: ignore [reportGeneralTypeIssues] """Execute cursorless command and wait for it to finish""" response = actions.user.private_cursorless_run_rpc_command_get( CURSORLESS_COMMAND_ID, construct_cursorless_command(action), ) if "fallback" in response: perform_fallback(response["fallback"]) def private_cursorless_command_no_wait(action: dict): # pyright: ignore [reportGeneralTypeIssues] """Execute cursorless command without waiting""" actions.user.private_cursorless_run_rpc_command_no_wait( CURSORLESS_COMMAND_ID, construct_cursorless_command(action), ) def private_cursorless_command_get(action: dict): # pyright: ignore [reportGeneralTypeIssues] """Execute cursorless command and return result""" response = actions.user.private_cursorless_run_rpc_command_get( CURSORLESS_COMMAND_ID, construct_cursorless_command(action), ) if "fallback" in response: return perform_fallback(response["fallback"]) if "returnValue" in response: return response["returnValue"] return None def construct_cursorless_command(action: dict) -> dict: try: use_pre_phrase_snapshot = actions.user.did_emit_pre_phrase_signal() except KeyError: use_pre_phrase_snapshot = False spoken_form = " ".join(last_phrase["phrase"]) return make_serializable( CursorlessCommand( spoken_form, use_pre_phrase_snapshot, action, ) ) def make_serializable(value: Any) -> Any: """ Converts a dataclass into a serializable dict Note that we don't use the built-in asdict() function because it will ignore the static `type` field. Args: value (any): The value to convert Returns: _type_: The converted value, ready for serialization """ if isinstance(value, dict): return {k: make_serializable(v) for k, v in value.items()} if isinstance(value, list): return [make_serializable(v) for v in value] if dataclasses.is_dataclass(value): items = { **{ k: v for k, v in vars(type(value)).items() if not k.startswith("_") and not isinstance(v, property) and not isinstance(v, staticmethod) }, **value.__dict__, } return {k: make_serializable(v) for k, v in items.items() if v is not None} return value