-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathcommand_client.py
199 lines (158 loc) · 5.15 KB
/
command_client.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
from pathlib import Path
from typing import Any
from talon import Context, Module, actions, speech_system
from .rpc_client.get_communication_dir_path import get_communication_dir_path
# Indicates whether a pre-phrase signal was emitted during the course of the
# current phrase
did_emit_pre_phrase_signal = False
mod = Module()
ctx = Context()
mac_ctx = Context()
ctx.matches = r"""
tag: user.command_client
"""
mac_ctx.matches = r"""
os: mac
tag: user.command_client
"""
class NotSet:
def __repr__(self):
return "<argument not set>"
def run_command(
command_id: str,
*args,
wait_for_finish: bool = False,
return_command_output: bool = False,
):
"""Runs a command, using command server if available
Args:
command_id (str): The ID of the command to run.
args: The arguments to the command.
wait_for_finish (bool, optional): Whether to wait for the command to finish before returning. Defaults to False.
return_command_output (bool, optional): Whether to return the output of the command. Defaults to False.
Raises:
Exception: If there is an issue with the file-based communication, or
application raises an exception
Returns:
Object: The response from the command, if requested.
"""
# NB: This is a hack to work around the fact that talon doesn't support
# variable argument lists
args = [x for x in args if x is not NotSet]
return actions.user.rpc_client_run_command(
actions.user.command_server_directory(),
actions.user.trigger_command_server_command_execution,
command_id,
args,
wait_for_finish,
return_command_output,
)
@mod.action_class
class Actions:
def run_rpc_command(
command_id: str,
arg1: Any = NotSet,
arg2: Any = NotSet,
arg3: Any = NotSet,
arg4: Any = NotSet,
arg5: Any = NotSet,
):
"""Execute command via RPC."""
run_command(
command_id,
arg1,
arg2,
arg3,
arg4,
arg5,
)
def run_rpc_command_and_wait(
command_id: str,
arg1: Any = NotSet,
arg2: Any = NotSet,
arg3: Any = NotSet,
arg4: Any = NotSet,
arg5: Any = NotSet,
):
"""Execute command via application command server and wait for command to finish."""
run_command(
command_id,
arg1,
arg2,
arg3,
arg4,
arg5,
wait_for_finish=True,
)
def run_rpc_command_get(
command_id: str,
arg1: Any = NotSet,
arg2: Any = NotSet,
arg3: Any = NotSet,
arg4: Any = NotSet,
arg5: Any = NotSet,
) -> Any:
"""Execute command via application command server and return command output."""
return run_command(
command_id,
arg1,
arg2,
arg3,
arg4,
arg5,
return_command_output=True,
)
def command_server_directory() -> str:
"""Return the directory of the command server"""
def trigger_command_server_command_execution():
"""Issue keystroke to trigger command server to execute command that
was written to the file. For internal use only"""
actions.key("ctrl-shift-f17")
def emit_pre_phrase_signal() -> bool:
"""
If in an application supporting the command client, returns True
and touches a file to indicate that a phrase is beginning execution.
Otherwise does nothing and returns False.
"""
return False
def did_emit_pre_phrase_signal() -> bool:
"""Indicates whether the pre-phrase signal was emitted at the start of this phrase"""
# NB: This action is used by cursorless; please don't delete it :)
return did_emit_pre_phrase_signal
@mac_ctx.action_class("user")
class MacUserActions:
def trigger_command_server_command_execution():
actions.key("cmd-shift-f17")
@ctx.action_class("user")
class UserActions:
def emit_pre_phrase_signal():
get_signal_path("prePhrase").touch()
return True
class MissingCommunicationDir(Exception):
pass
def get_signal_path(name: str) -> Path:
"""
Get the path to a signal in the signal subdirectory.
Args:
name (str): The name of the signal
Returns:
Path: The signal path
"""
dir_name = actions.user.command_server_directory()
communication_dir_path = get_communication_dir_path(dir_name)
if not communication_dir_path.exists():
raise MissingCommunicationDir()
signal_dir = communication_dir_path / "signals"
signal_dir.mkdir(parents=True, exist_ok=True)
return signal_dir / name
def pre_phrase(_: Any):
try:
global did_emit_pre_phrase_signal
did_emit_pre_phrase_signal = actions.user.emit_pre_phrase_signal()
except MissingCommunicationDir:
pass
def post_phrase(_: Any):
global did_emit_pre_phrase_signal
did_emit_pre_phrase_signal = False
speech_system.register("pre:phrase", pre_phrase)
speech_system.register("post:phrase", post_phrase)