Skip to content

Commit 1a62f3c

Browse files
AndreasArvidssonpokeypre-commit-ci-lite[bot]
authored
Talon side spoken form tests (#1637)
- Fixes #449 - Fixes #1675 ## Potential manual tests to add For these, just bake payload and spoken form into `const`s in the test itself - [x] "slice past" - [x] "two tokens forward" - [x] "one token" - [x] "take inside pair" ## Checklist - [x] Handle if Talon was in sleep mode initially - [x] Add deprecation warning if user says "to after"? I believe we support this today, and some users are still saying that, as indicated by slack questions from the past month. We could probably have this as an alternate spoken form for "after" and show a warning if they use it - [x] Stop using `generateSpokenForms` to check whether to run a test; instead read `spokenFormError` field on recorded test - [x] How do we actually want to launch this? Launch config in vscode and then? - [x] Add notifications when test start and stop - [x] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [/] I have updated the [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) - [x] I have not broken the cheatsheet --------- Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
1 parent 47fe875 commit 1a62f3c

File tree

9 files changed

+97
-47
lines changed

9 files changed

+97
-47
lines changed

src/actions/swap.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from talon import Module
22

3-
from ..primitive_target import create_base_target
3+
from ..primitive_target import create_implicit_target
44

55
mod = Module()
66

@@ -20,6 +20,6 @@ def cursorless_swap_targets(m) -> list[dict]:
2020
target_list = m.cursorless_target_list
2121

2222
if len(target_list) == 1:
23-
target_list = [create_base_target()] + target_list
23+
target_list = [create_implicit_target()] + target_list
2424

2525
return target_list

src/apps/cursorless_vscode.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from talon import Context, actions, app
22

33
from ..actions.get_text import get_text
4-
from ..cursorless_command_server import run_rpc_command_no_wait
54

65
ctx = Context()
76

@@ -21,12 +20,14 @@ def cursorless_private_run_find_action(target: dict):
2120
if len(search_text) > 200:
2221
search_text = search_text[:200]
2322
app.notify("Search text is longer than 200 characters; truncating")
24-
run_rpc_command_no_wait("actions.find")
23+
actions.user.private_cursorless_run_rpc_command_no_wait("actions.find")
2524
actions.sleep("50ms")
2625
actions.insert(search_text)
2726

2827
def cursorless_show_settings_in_ide():
2928
"""Show Cursorless-specific settings in ide"""
30-
run_rpc_command_no_wait("workbench.action.openGlobalSettings")
29+
actions.user.private_cursorless_run_rpc_command_no_wait(
30+
"workbench.action.openGlobalSettings"
31+
)
3132
actions.sleep("250ms")
3233
actions.insert("cursorless")

src/cheatsheet/cheat_sheet.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import webbrowser
22
from pathlib import Path
33

4-
from talon import Context, Module, app
4+
from talon import Context, Module, actions, app
55

6-
from ..cursorless_command_server import run_rpc_command_and_wait
76
from .get_list import get_list, get_lists
87
from .sections.actions import get_actions
98
from .sections.compound_targets import get_compound_targets
@@ -54,7 +53,7 @@ def cursorless_cheat_sheet_show_html():
5453

5554
cheatsheet_out_dir.mkdir(parents=True, exist_ok=True)
5655
cheatsheet_out_path = cheatsheet_out_dir / cheatsheet_filename
57-
run_rpc_command_and_wait(
56+
actions.user.private_cursorless_run_rpc_command_and_wait(
5857
"cursorless.showCheatsheet",
5958
{
6059
"version": 0,
@@ -66,7 +65,7 @@ def cursorless_cheat_sheet_show_html():
6665

6766
def cursorless_cheat_sheet_update_json():
6867
"""Update default cursorless cheatsheet json (for developer use only)"""
69-
run_rpc_command_and_wait(
68+
actions.user.private_cursorless_run_rpc_command_and_wait(
7069
"cursorless.internal.updateCheatsheetDefaults",
7170
cursorless_cheat_sheet_get_json(),
7271
)

src/command.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,6 @@
22

33
from talon import Module, actions, speech_system
44

5-
from .cursorless_command_server import (
6-
run_rpc_command_and_wait,
7-
run_rpc_command_get,
8-
run_rpc_command_no_wait,
9-
)
105
from .primitive_target import create_implicit_target
116

127
mod = Module()
@@ -73,7 +68,7 @@ def cursorless_single_target_command_get(
7368
arg3: Any = NotSet,
7469
):
7570
"""Execute single-target cursorless command and return result"""
76-
return run_rpc_command_get(
71+
return actions.user.private_cursorless_run_rpc_command_get(
7772
CURSORLESS_COMMAND_ID,
7873
construct_cursorless_command_argument(
7974
action=action,
@@ -101,7 +96,7 @@ def cursorless_multiple_target_command(
10196
arg3: Any = NotSet,
10297
):
10398
"""Execute multi-target cursorless command"""
104-
run_rpc_command_and_wait(
99+
actions.user.private_cursorless_run_rpc_command_and_wait(
105100
CURSORLESS_COMMAND_ID,
106101
construct_cursorless_command_argument(
107102
action=action,
@@ -118,7 +113,7 @@ def cursorless_multiple_target_command_no_wait(
118113
arg3: Any = NotSet,
119114
):
120115
"""Execute multi-target cursorless command"""
121-
run_rpc_command_no_wait(
116+
actions.user.private_cursorless_run_rpc_command_no_wait(
122117
CURSORLESS_COMMAND_ID,
123118
construct_cursorless_command_argument(
124119
action=action,

src/csv_overrides.py

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,22 @@
1414

1515

1616
mod = Module()
17+
mod.tag(
18+
"cursorless_default_vocabulary",
19+
desc="Use default cursorless vocabulary instead of user custom",
20+
)
1721
cursorless_settings_directory = mod.setting(
1822
"cursorless_settings_directory",
1923
type=str,
2024
default="cursorless-settings",
2125
desc="The directory to use for cursorless settings csvs relative to talon user directory",
2226
)
2327

28+
default_ctx = Context()
29+
default_ctx.matches = r"""
30+
tag: user.cursorless_default_vocabulary
31+
"""
32+
2433

2534
def init_csv_and_watch_changes(
2635
filename: str,
@@ -68,6 +77,9 @@ def init_csv_and_watch_changes(
6877

6978
file_path.parent.mkdir(parents=True, exist_ok=True)
7079

80+
check_for_duplicates(filename, default_values)
81+
create_default_vocabulary_dicts(default_values, pluralize_lists)
82+
7183
def on_watch(path, flags):
7284
if file_path.match(path):
7385
current_values, has_errors = read_file(
@@ -126,27 +138,49 @@ def unsubscribe():
126138
return unsubscribe
127139

128140

141+
def check_for_duplicates(filename, default_values):
142+
results_map = {}
143+
for list_name, dict in default_values.items():
144+
for key, value in dict.items():
145+
if value in results_map:
146+
existing_list_name = results_map[value]["list"]
147+
warning = f"WARNING ({filename}): Value `{value}` duplicated between lists '{existing_list_name}' and '{list_name}'"
148+
print(warning)
149+
app.notify(warning)
150+
151+
129152
def is_removed(value: str):
130153
return value.startswith("-")
131154

132155

156+
def create_default_vocabulary_dicts(
157+
default_values: dict[str, dict], pluralize_lists: list[str]
158+
):
159+
default_values_updated = {}
160+
for key, value in default_values.items():
161+
updated_dict = {}
162+
for key2, value2 in value.items():
163+
# Enable deactivated(prefixed with a `-`) items
164+
active_key = key2[1:] if key2.startswith("-") else key2
165+
if active_key:
166+
updated_dict[active_key] = value2
167+
default_values_updated[key] = updated_dict
168+
assign_lists_to_context(default_ctx, default_values_updated, pluralize_lists)
169+
170+
133171
def update_dicts(
134172
default_values: dict[str, dict],
135173
current_values: dict,
136174
extra_ignored_values: list[str],
137175
allow_unknown_values: bool,
138176
default_list_name: Optional[str],
139-
pluralize_lists: Optional[list[str]],
177+
pluralize_lists: list[str],
140178
ctx: Context,
141179
):
142180
# Create map with all default values
143181
results_map = {}
144182
for list_name, dict in default_values.items():
145183
for key, value in dict.items():
146-
if value in results_map:
147-
warning = f"WARNING: Duplicate value `{value}` in {list_name}.csv"
148-
print(warning)
149-
app.notify(warning)
150184
results_map[value] = {"key": key, "value": value, "list": list_name}
151185

152186
# Update result with current values
@@ -183,6 +217,14 @@ def update_dicts(
183217
results[obj["list"]][k.strip()] = value
184218

185219
# Assign result to talon context list
220+
assign_lists_to_context(ctx, results, pluralize_lists)
221+
222+
223+
def assign_lists_to_context(
224+
ctx: Context,
225+
results: dict,
226+
pluralize_lists: list[str],
227+
):
186228
for list_name, dict in results.items():
187229
list_singular_name = get_cursorless_list_name(list_name)
188230
ctx.lists[list_singular_name] = dict

src/cursorless.talon

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
mode: command
2+
mode: user.cursorless_spoken_form_test
13
tag: user.cursorless
24
-
35

src/cursorless_command_server.py

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,35 @@
11
from typing import Any
22

3-
from talon import actions
3+
from talon import Module, actions
44

5+
mod = Module()
56

6-
def run_rpc_command_no_wait(command_id: str, *args):
7-
"""Execute command via rpc."""
8-
try:
9-
actions.user.run_rpc_command(command_id, *args)
10-
except KeyError:
11-
actions.user.vscode_with_plugin(command_id, *args)
127

8+
@mod.action_class
9+
class Actions:
10+
def private_cursorless_run_rpc_command_and_wait(
11+
command_id: str, arg1: Any = None, arg2: Any = None
12+
):
13+
"""Execute command via rpc and wait for command to finish."""
14+
try:
15+
actions.user.run_rpc_command_and_wait(command_id, arg1, arg2)
16+
except KeyError:
17+
actions.user.vscode_with_plugin_and_wait(command_id, arg1, arg2)
1318

14-
def run_rpc_command_and_wait(command_id: str, *args):
15-
"""Execute command via rpc and wait for command to finish."""
16-
try:
17-
actions.user.run_rpc_command_and_wait(command_id, *args)
18-
except KeyError:
19-
actions.user.vscode_with_plugin_and_wait(command_id, *args)
19+
def private_cursorless_run_rpc_command_no_wait(
20+
command_id: str, arg1: Any = None, arg2: Any = None
21+
):
22+
"""Execute command via rpc and DON'T wait."""
23+
try:
24+
actions.user.run_rpc_command(command_id, arg1, arg2)
25+
except KeyError:
26+
actions.user.vscode_with_plugin(command_id, arg1, arg2)
2027

21-
22-
def run_rpc_command_get(command_id: str, *args) -> Any:
23-
"""Execute command via rpc and return command output."""
24-
try:
25-
return actions.user.run_rpc_command_get(command_id, *args)
26-
except KeyError:
27-
return actions.user.vscode_get(command_id, *args)
28+
def private_cursorless_run_rpc_command_get(
29+
command_id: str, arg1: Any = None, arg2: Any = None
30+
) -> Any:
31+
"""Execute command via rpc and return command output."""
32+
try:
33+
return actions.user.run_rpc_command_get(command_id, arg1, arg2)
34+
except KeyError:
35+
return actions.user.vscode_get(command_id, arg1, arg2)

src/cursorless_snippets.talon

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
mode: command
2+
mode: user.cursorless_spoken_form_test
13
tag: user.cursorless
24
-
35

src/scope_visualizer.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
from talon import Module, app
1+
from talon import Module, actions, app
22

33
from .csv_overrides import init_csv_and_watch_changes
4-
from .cursorless_command_server import run_rpc_command_no_wait
54

65
mod = Module()
76
mod.list("cursorless_show_scope_visualizer", desc="Show scope visualizer")
@@ -25,13 +24,15 @@ def private_cursorless_show_scope_visualizer(
2524
scope_type: dict, visualization_type: str
2625
):
2726
"""Shows scope visualizer"""
28-
run_rpc_command_no_wait(
27+
actions.user.private_cursorless_run_rpc_command_no_wait(
2928
"cursorless.showScopeVisualizer", scope_type, visualization_type
3029
)
3130

3231
def private_cursorless_hide_scope_visualizer():
33-
"""Hides scope visualizer"""
34-
run_rpc_command_no_wait("cursorless.hideScopeVisualizer")
32+
"""Hides scope visual_izer"""
33+
actions.user.private_cursorless_run_rpc_command_no_wait(
34+
"cursorless.hideScopeVisualizer"
35+
)
3536

3637

3738
def on_ready():

0 commit comments

Comments
 (0)