Skip to content

Talon side spoken form tests #1637

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
98b44d1
Talon-side tests
pokey Aug 1, 2023
806ebdd
fixed bugs
AndreasArvidsson Aug 1, 2023
8c2ec59
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Aug 1, 2023
c5e4269
added comment
AndreasArvidsson Aug 1, 2023
3fb5a81
Merge branch 'grammar_test' of github.com:cursorless-dev/cursorless i…
AndreasArvidsson Aug 1, 2023
a0e489f
Handle modes better
AndreasArvidsson Aug 1, 2023
9d2a937
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Aug 1, 2023
14c948c
fix up
AndreasArvidsson Aug 1, 2023
19053d4
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Aug 1, 2023
07587ac
No bare except
AndreasArvidsson Aug 1, 2023
84227a8
Merge branch 'grammar_test' of github.com:cursorless-dev/cursorless i…
AndreasArvidsson Aug 1, 2023
73b1702
Tweaks to runner scripts
pokey Aug 2, 2023
481350c
Cleanup test runners
pokey Aug 2, 2023
de2d424
Cleanup
pokey Aug 2, 2023
5d97694
More proper support for navigation map tests
pokey Aug 2, 2023
f2100e3
cleanup
pokey Aug 2, 2023
5e06111
tweaks
pokey Aug 2, 2023
1c9d251
unify `getRecordedTestPaths` handling
pokey Aug 2, 2023
0b3819b
tweak
pokey Aug 2, 2023
6fc4eff
cleanup
pokey Aug 2, 2023
6d972a6
more cleanup
pokey Aug 2, 2023
513580f
Eat output to handle possible user warnings
pokey Aug 2, 2023
addd266
cleanup
pokey Aug 2, 2023
8ef893a
rename
pokey Aug 2, 2023
1cd6b90
use `asyncSafety`
pokey Aug 2, 2023
7e9e84d
fixed default vocabulary bug
AndreasArvidsson Aug 2, 2023
24f27b4
Cleanup
pokey Aug 2, 2023
9395c21
pre-commit
pokey Aug 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
]
},
{
"name": "Extension Tests",
"name": "Extension tests",
"type": "extensionHost",
"request": "launch",
"env": {
Expand All @@ -34,7 +34,7 @@
"args": [
"--profile=cursorlessDevelopment",
"--extensionDevelopmentPath=${workspaceFolder}/packages/cursorless-vscode/dist",
"--extensionTestsPath=${workspaceFolder}/packages/test-harness/out/runners/all"
"--extensionTestsPath=${workspaceFolder}/packages/test-harness/out/runners/extensionTests"
],
"outFiles": ["${workspaceFolder}/**/out/**/*.js"],
"preLaunchTask": "${defaultBuildTask}",
Expand All @@ -43,6 +43,22 @@
"!**/node_modules/**"
]
},
{
"type": "node",
"request": "launch",
"name": "Talon tests",
"program": "${workspaceFolder}/packages/test-harness/out/scripts/runTalonTests",
"env": {
"CURSORLESS_TEST": "true",
"CURSORLESS_REPO_ROOT": "${workspaceFolder}"
},
"outFiles": ["${workspaceFolder}/**/out/**/*.js"],
"preLaunchTask": "${defaultBuildTask}",
"resolveSourceMapLocations": [
"${workspaceFolder}/**",
"!**/node_modules/**"
]
},
{
"type": "node",
"request": "launch",
Expand Down
3 changes: 3 additions & 0 deletions cursorless-talon-dev/src/cursorless_dev.talon
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
tag: user.cursorless
-

# Activate this if you want the default Cursorless vocabulary
# tag(): user.cursorless_default_vocabulary

{user.cursorless_homophone} record:
user.run_rpc_command("cursorless.recordTestCase")
{user.cursorless_homophone} pause:
Expand Down
113 changes: 113 additions & 0 deletions cursorless-talon-dev/src/default_vocabulary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
from talon import Context

ctx = Context()
ctx.matches = r"""
tag: user.cursorless_default_vocabulary
"""

# https://github.com/talonhub/community/blob/9acb6c9659bb0c9b794a7b7126d025603b4ed726/core/keys/keys.py#L10
initial_default_alphabet = "air bat cap drum each fine gust harp sit jury crunch look made near odd pit quench red sun trap urge vest whale plex yank zip".split()

# https://github.com/talonhub/community/blob/9acb6c9659bb0c9b794a7b7126d025603b4ed726/core/keys/keys.py#L24
digits = "zero one two three four five six seven eight nine".split()

# https://github.com/talonhub/community/blob/9acb6c9659bb0c9b794a7b7126d025603b4ed726/core/keys/keys.py#L139C1-L171C2
punctuation_words = {
# TODO: I'm not sure why we need these, I think it has something to do with
# Dragon. Possibly it has been fixed by later improvements to talon? -rntz
# "`": "`",
# ",": ",", # <== these things
"back tick": "`",
"comma": ",",
# Workaround for issue with conformer b-series; see #946
"coma": ",",
"period": ".",
"full stop": ".",
"semicolon": ";",
"colon": ":",
"forward slash": "/",
"question mark": "?",
"exclamation mark": "!",
"exclamation point": "!",
"asterisk": "*",
"hash sign": "#",
"number sign": "#",
"percent sign": "%",
"at sign": "@",
"and sign": "&",
"ampersand": "&",
# Currencies
"dollar sign": "$",
"pound sign": "£",
"hyphen": "-",
"L paren": "(",
"left paren": "(",
"R paren": ")",
"right paren": ")",
}

# https://github.com/talonhub/community/blob/9acb6c9659bb0c9b794a7b7126d025603b4ed726/core/keys/keys.py#L172
symbol_key_words = {
"dot": ".",
"point": ".",
"quote": "'",
"question": "?",
"apostrophe": "'",
"L square": "[",
"left square": "[",
"square": "[",
"R square": "]",
"right square": "]",
"slash": "/",
"backslash": "\\",
"minus": "-",
"dash": "-",
"equals": "=",
"plus": "+",
"grave": "`",
"tilde": "~",
"bang": "!",
"down score": "_",
"underscore": "_",
"paren": "(",
"brace": "{",
"left brace": "{",
"brack": "{",
"bracket": "{",
"left bracket": "{",
"r brace": "}",
"right brace": "}",
"r brack": "}",
"r bracket": "}",
"right bracket": "}",
"angle": "<",
"left angle": "<",
"less than": "<",
"rangle": ">",
"R angle": ">",
"right angle": ">",
"greater than": ">",
"star": "*",
"hash": "#",
"percent": "%",
"caret": "^",
"amper": "&",
"pipe": "|",
"dub quote": '"',
"double quote": '"',
# Currencies
"dollar": "$",
"pound": "£",
}

any_alphanumeric_keys = {
**{w: chr(ord("a") + i) for i, w in enumerate(initial_default_alphabet)},
**{digits[i]: str(i) for i in range(10)},
**punctuation_words,
**symbol_key_words,
}


@ctx.capture("user.any_alphanumeric_key", rule="|".join(any_alphanumeric_keys.keys()))
def any_alphanumeric_key(m) -> str:
return any_alphanumeric_keys[str(m)]
107 changes: 107 additions & 0 deletions cursorless-talon-dev/src/spoken_form_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import json
from typing import Any

from talon import Context, Module, actions, scope

mod = Module()

mod.mode(
"cursorless_spoken_form_test",
"Used to run tests on the Cursorless spoken forms/grammar",
)

ctx = Context()

ctx.matches = r"""
mode: user.cursorless_spoken_form_test
"""

ctx.tags = [
"user.cursorless",
"user.cursorless_default_vocabulary",
]

# Keeps track of the microphone that was active before the spoken form test mode
saved_microphone = "None"

# Keeps a list of modes that were active before the spoken form test mode was
# enabled
saved_modes = []

# Keeps a list of commands run over the course of a spoken form test
commands_run = []


@ctx.action_class("user")
class UserActions:
def did_emit_pre_phrase_signal():
return True

def private_cursorless_run_rpc_command_and_wait(
command_id: str, arg1: Any, arg2: Any = None
):
commands_run.append(arg1)

def private_cursorless_run_rpc_command_no_wait(
command_id: str, arg1: Any, arg2: Any = None
):
commands_run.append(arg1)

def private_cursorless_run_rpc_command_get(
command_id: str, arg1: Any, arg2: Any = None
) -> Any:
commands_run.append(arg1)


@mod.action_class
class Actions:
def private_cursorless_spoken_form_test_mode(enable: bool):
"""Enable/disable Cursorless spoken form test mode"""
global saved_modes, saved_microphone

if enable:
saved_modes = scope.get("mode")
saved_microphone = actions.sound.active_microphone()

disable_modes()
actions.mode.enable("user.cursorless_spoken_form_test")
actions.sound.set_microphone("None")

actions.app.notify(
"Cursorless spoken form tests are running. Talon microphone is disabled."
)
else:
actions.mode.disable("user.cursorless_spoken_form_test")
enable_modes()
actions.sound.set_microphone(saved_microphone)

actions.app.notify(
"Cursorless spoken form tests are done. Talon microphone is re-enabled."
)

def private_cursorless_spoken_form_test(phrase: str):
"""Run Cursorless spoken form test"""
global commands_run
commands_run = []

try:
actions.mimic(phrase)
print(json.dumps(commands_run))
except Exception as e:
print(f"{e.__class__.__name__}: {e}")


def enable_modes():
for mode in saved_modes:
try:
actions.mode.enable(mode)
except Exception:
pass


def disable_modes():
for mode in saved_modes:
try:
actions.mode.disable(mode)
except Exception:
pass
4 changes: 2 additions & 2 deletions cursorless-talon/src/actions/swap.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from talon import Module

from ..primitive_target import create_base_target
from ..primitive_target import create_implicit_target

mod = Module()

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

if len(target_list) == 1:
target_list = [create_base_target()] + target_list
target_list = [create_implicit_target()] + target_list

return target_list
7 changes: 4 additions & 3 deletions cursorless-talon/src/apps/cursorless_vscode.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from talon import Context, actions, app

from ..actions.get_text import get_text
from ..cursorless_command_server import run_rpc_command_no_wait

ctx = Context()

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

def cursorless_show_settings_in_ide():
"""Show Cursorless-specific settings in ide"""
run_rpc_command_no_wait("workbench.action.openGlobalSettings")
actions.user.private_cursorless_run_rpc_command_no_wait(
"workbench.action.openGlobalSettings"
)
actions.sleep("250ms")
actions.insert("cursorless")
7 changes: 3 additions & 4 deletions cursorless-talon/src/cheatsheet/cheat_sheet.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import webbrowser
from pathlib import Path

from talon import Context, Module, app
from talon import Context, Module, actions, app

from ..cursorless_command_server import run_rpc_command_and_wait
from .get_list import get_list, get_lists
from .sections.actions import get_actions
from .sections.compound_targets import get_compound_targets
Expand Down Expand Up @@ -54,7 +53,7 @@ def cursorless_cheat_sheet_show_html():

cheatsheet_out_dir.mkdir(parents=True, exist_ok=True)
cheatsheet_out_path = cheatsheet_out_dir / cheatsheet_filename
run_rpc_command_and_wait(
actions.user.private_cursorless_run_rpc_command_and_wait(
"cursorless.showCheatsheet",
{
"version": 0,
Expand All @@ -66,7 +65,7 @@ def cursorless_cheat_sheet_show_html():

def cursorless_cheat_sheet_update_json():
"""Update default cursorless cheatsheet json (for developer use only)"""
run_rpc_command_and_wait(
actions.user.private_cursorless_run_rpc_command_and_wait(
"cursorless.internal.updateCheatsheetDefaults",
cursorless_cheat_sheet_get_json(),
)
Expand Down
11 changes: 3 additions & 8 deletions cursorless-talon/src/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@

from talon import Module, actions, speech_system

from .cursorless_command_server import (
run_rpc_command_and_wait,
run_rpc_command_get,
run_rpc_command_no_wait,
)
from .primitive_target import create_implicit_target

mod = Module()
Expand Down Expand Up @@ -73,7 +68,7 @@ def cursorless_single_target_command_get(
arg3: Any = NotSet,
):
"""Execute single-target cursorless command and return result"""
return run_rpc_command_get(
return actions.user.private_cursorless_run_rpc_command_get(
CURSORLESS_COMMAND_ID,
construct_cursorless_command_argument(
action=action,
Expand Down Expand Up @@ -101,7 +96,7 @@ def cursorless_multiple_target_command(
arg3: Any = NotSet,
):
"""Execute multi-target cursorless command"""
run_rpc_command_and_wait(
actions.user.private_cursorless_run_rpc_command_and_wait(
CURSORLESS_COMMAND_ID,
construct_cursorless_command_argument(
action=action,
Expand All @@ -118,7 +113,7 @@ def cursorless_multiple_target_command_no_wait(
arg3: Any = NotSet,
):
"""Execute multi-target cursorless command"""
run_rpc_command_no_wait(
actions.user.private_cursorless_run_rpc_command_no_wait(
CURSORLESS_COMMAND_ID,
construct_cursorless_command_argument(
action=action,
Expand Down
Loading