diff --git a/cursorless-talon/.github/CODEOWNERS b/cursorless-talon/.github/CODEOWNERS
deleted file mode 100644
index 33f266adbb..0000000000
--- a/cursorless-talon/.github/CODEOWNERS
+++ /dev/null
@@ -1 +0,0 @@
-* @pokey
diff --git a/cursorless-talon/.github/ISSUE_TEMPLATE/config.yml b/cursorless-talon/.github/ISSUE_TEMPLATE/config.yml
deleted file mode 100644
index d371b3a3c3..0000000000
--- a/cursorless-talon/.github/ISSUE_TEMPLATE/config.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-blank_issues_enabled: false
-contact_links:
- - name: Create issue on cursorless-dev/cursorless
- url: https://github.com/cursorless-dev/cursorless/issues/new
- about: Please file issues on the main Cursorless repository
diff --git a/cursorless-talon/.gitignore b/cursorless-talon/.gitignore
deleted file mode 100644
index 97c530fa22..0000000000
--- a/cursorless-talon/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-*.flac
-data/
-.vscode/settings.json
diff --git a/cursorless-talon/LICENSE b/cursorless-talon/LICENSE
deleted file mode 100644
index ec2b623613..0000000000
--- a/cursorless-talon/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2021 Brandon Virgil Rule
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/cursorless-talon/README.md b/cursorless-talon/README.md
deleted file mode 100644
index dc4ac1c302..0000000000
--- a/cursorless-talon/README.md
+++ /dev/null
@@ -1,32 +0,0 @@
-
Welcome to Cursorless!
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Cursorless is a spoken language for structural code editing, enabling developers to code by voice at speeds not possible with a keyboard. Cursorless decorates every token on the screen and defines a spoken language for rapid, high-level semantic manipulation of structured text.
-
-This repository holds the Talon side of Cursorless. If you've arrived here as part of the [Cursorless installation process](https://www.cursorless.org/docs/user/installation/), then you're in the right place!
-
-# Contributing
-
-If you're looking to improve Cursorless, note that Cursorless is now maintained as a monorepo, so if you're here in a browser, and your address bar points to https://github.com/cursorless-dev/cursorless-talon, then you're probably in the wrong place. The monorepo is hosted at [`cursorless`](https://github.com/cursorless-dev/cursorless), and the source of truth for these talon files is in the [`cursorless-talon`](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon) subdirectory.
-
-See [the contributor docs](https://www.cursorless.org/docs/contributing/) to get started.
-
-# Cursorless talon
-
-This directory contains the talon side of [Cursorless](https://marketplace.visualstudio.com/items?itemName=pokey.cursorless).
diff --git a/cursorless-talon/docs/README.md b/cursorless-talon/docs/README.md
deleted file mode 100644
index 6af3830dcc..0000000000
--- a/cursorless-talon/docs/README.md
+++ /dev/null
@@ -1 +0,0 @@
-Cursorless is now monorepo 🙌. The docs now live at https://www.cursorless.org/docs/.
diff --git a/cursorless-talon/docs/customization.md b/cursorless-talon/docs/customization.md
deleted file mode 100644
index 7fed001ae2..0000000000
--- a/cursorless-talon/docs/customization.md
+++ /dev/null
@@ -1 +0,0 @@
-Cursorless is now monorepo 🙌. This document now lives at https://www.cursorless.org/docs/user/customization/.
diff --git a/cursorless-talon/docs/experimental.md b/cursorless-talon/docs/experimental.md
deleted file mode 100644
index f579d28476..0000000000
--- a/cursorless-talon/docs/experimental.md
+++ /dev/null
@@ -1 +0,0 @@
-Cursorless is now monorepo 🙌. This document now lives at https://www.cursorless.org/docs/user/experimental/
diff --git a/cursorless-talon/src/actions/actions.py b/cursorless-talon/src/actions/actions.py
deleted file mode 100644
index c9e75cf750..0000000000
--- a/cursorless-talon/src/actions/actions.py
+++ /dev/null
@@ -1,142 +0,0 @@
-from typing import Callable, Union
-
-from talon import Module, actions
-
-from ..targets.target_types import (
- CursorlessDestination,
- CursorlessExplicitTarget,
- CursorlessTarget,
- ImplicitDestination,
-)
-from .bring_move import BringMoveTargets
-from .execute_command import cursorless_execute_command_action
-from .homophones import cursorless_homophones_action
-from .replace import cursorless_replace_action
-
-mod = Module()
-
-mod.list(
- "cursorless_simple_action",
- desc="Cursorless internal: simple actions",
-)
-
-mod.list(
- "cursorless_callback_action",
- desc="Cursorless internal: actions implemented via a callback function",
-)
-
-mod.list(
- "cursorless_custom_action",
- desc="Cursorless internal: user-defined custom actions",
-)
-
-mod.list(
- "cursorless_experimental_action",
- desc="Cursorless internal: experimental actions",
-)
-
-ACTION_LIST_NAMES = [
- "simple_action",
- "callback_action",
- "paste_action",
- "bring_move_action",
- "swap_action",
- "wrap_action",
- "insert_snippet_action",
- "reformat_action",
- "call_action",
- "experimental_action",
- "custom_action",
-]
-
-callback_actions: dict[str, Callable[[CursorlessExplicitTarget], None]] = {
- "nextHomophone": cursorless_homophones_action,
-}
-
-# Don't wait for these actions to finish, usually because they hang on some kind of user interaction
-no_wait_actions = [
- "generateSnippet",
- "rename",
-]
-
-# These are actions that we don't wait for, but still want to have a post action sleep
-no_wait_actions_post_sleep = {
- "rename": 0.3,
-}
-
-
-@mod.capture(
- rule=(
- "{user.cursorless_simple_action} |"
- "{user.cursorless_experimental_action} |"
- "{user.cursorless_callback_action} |"
- "{user.cursorless_call_action} |"
- "{user.cursorless_custom_action}"
- )
-)
-def cursorless_action_or_ide_command(m) -> dict[str, str]:
- try:
- value = m.cursorless_custom_action
- type = "ide_command"
- except AttributeError:
- value = m[0]
- type = "cursorless_action"
- return {
- "value": value,
- "type": type,
- }
-
-
-@mod.action_class
-class Actions:
- def cursorless_command(action_name: str, target: CursorlessExplicitTarget): # pyright: ignore [reportGeneralTypeIssues]
- """Perform cursorless command on target"""
- if action_name in callback_actions:
- callback_actions[action_name](target)
- elif action_name in ["replaceWithTarget", "moveToTarget"]:
- actions.user.private_cursorless_bring_move(
- action_name, BringMoveTargets(target, ImplicitDestination())
- )
- elif action_name == "callAsFunction":
- actions.user.private_cursorless_call(target)
- elif action_name in no_wait_actions:
- action = {"name": action_name, "target": target}
- actions.user.private_cursorless_command_no_wait(action)
- if action_name in no_wait_actions_post_sleep:
- actions.sleep(no_wait_actions_post_sleep[action_name])
- else:
- action = {"name": action_name, "target": target}
- actions.user.private_cursorless_command_and_wait(action)
-
- def cursorless_vscode_command(command_id: str, target: CursorlessTarget): # pyright: ignore [reportGeneralTypeIssues]
- """
- Perform vscode command on cursorless target
-
- Deprecated: prefer `cursorless_ide_command`
- """
- return actions.user.cursorless_ide_command(command_id, target)
-
- def cursorless_ide_command(command_id: str, target: CursorlessTarget): # pyright: ignore [reportGeneralTypeIssues]
- """Perform ide command on cursorless target"""
- return cursorless_execute_command_action(command_id, target)
-
- def cursorless_insert(
- destination: CursorlessDestination, # pyright: ignore [reportGeneralTypeIssues]
- text: Union[str, list[str]],
- ):
- """Perform text insertion on Cursorless destination"""
- if isinstance(text, str):
- text = [text]
- cursorless_replace_action(destination, text)
-
- def private_cursorless_action_or_ide_command(
- instruction: dict[str, str], # pyright: ignore [reportGeneralTypeIssues]
- target: CursorlessTarget,
- ):
- """Perform cursorless action or ide command on target (internal use only)"""
- type = instruction["type"]
- value = instruction["value"]
- if type == "cursorless_action":
- actions.user.cursorless_command(value, target)
- elif type == "ide_command":
- actions.user.cursorless_ide_command(value, target)
diff --git a/cursorless-talon/src/actions/bring_move.py b/cursorless-talon/src/actions/bring_move.py
deleted file mode 100644
index bb13497ada..0000000000
--- a/cursorless-talon/src/actions/bring_move.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from dataclasses import dataclass
-
-from talon import Module, actions
-
-from ..targets.target_types import (
- CursorlessDestination,
- CursorlessTarget,
- ImplicitDestination,
-)
-
-
-@dataclass
-class BringMoveTargets:
- source: CursorlessTarget
- destination: CursorlessDestination
-
-
-mod = Module()
-
-
-mod.list("cursorless_bring_move_action", desc="Cursorless bring or move actions")
-
-
-@mod.capture(rule=" []")
-def cursorless_bring_move_targets(m) -> BringMoveTargets:
- source = m.cursorless_target
-
- try:
- destination = m.cursorless_destination
- except AttributeError:
- destination = ImplicitDestination()
-
- return BringMoveTargets(source, destination)
-
-
-@mod.action_class
-class Actions:
- def private_cursorless_bring_move(action_name: str, targets: BringMoveTargets): # pyright: ignore [reportGeneralTypeIssues]
- """Execute Cursorless move/bring action"""
- actions.user.private_cursorless_command_and_wait(
- {
- "name": action_name,
- "source": targets.source,
- "destination": targets.destination,
- }
- )
diff --git a/cursorless-talon/src/actions/call.py b/cursorless-talon/src/actions/call.py
deleted file mode 100644
index 7bfe75cb78..0000000000
--- a/cursorless-talon/src/actions/call.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from talon import Module, actions
-
-from ..targets.target_types import CursorlessTarget, ImplicitTarget
-
-mod = Module()
-mod.list("cursorless_call_action", desc="Cursorless call action")
-
-
-@mod.action_class
-class Actions:
- def private_cursorless_call(
- callee: CursorlessTarget, # pyright: ignore [reportGeneralTypeIssues]
- argument: CursorlessTarget = ImplicitTarget(),
- ):
- """Execute Cursorless call action"""
- actions.user.private_cursorless_command_and_wait(
- {
- "name": "callAsFunction",
- "callee": callee,
- "argument": argument,
- }
- )
diff --git a/cursorless-talon/src/actions/execute_command.py b/cursorless-talon/src/actions/execute_command.py
deleted file mode 100644
index 067fc14488..0000000000
--- a/cursorless-talon/src/actions/execute_command.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from talon import actions
-
-from ..targets.target_types import CursorlessTarget
-
-
-def cursorless_execute_command_action(
- command_id: str, target: CursorlessTarget, command_options: dict = {}
-):
- """Execute Cursorless execute command action"""
- actions.user.private_cursorless_command_and_wait(
- {
- "name": "executeCommand",
- "commandId": command_id,
- "options": command_options,
- "target": target,
- }
- )
diff --git a/cursorless-talon/src/actions/get_text.py b/cursorless-talon/src/actions/get_text.py
deleted file mode 100644
index 6f1770ad85..0000000000
--- a/cursorless-talon/src/actions/get_text.py
+++ /dev/null
@@ -1,56 +0,0 @@
-from typing import Optional
-
-from talon import Module, actions
-
-from ..targets.target_types import CursorlessTarget
-
-mod = Module()
-
-
-@mod.action_class
-class Actions:
- def cursorless_get_text(
- target: CursorlessTarget, # pyright: ignore [reportGeneralTypeIssues]
- hide_decorations: bool = False,
- ) -> str:
- """Get target text. If hide_decorations is True, don't show decorations"""
- return cursorless_get_text_action(
- target,
- show_decorations=not hide_decorations,
- ensure_single_target=True,
- )[0]
-
- def cursorless_get_text_list(
- target: CursorlessTarget, # pyright: ignore [reportGeneralTypeIssues]
- hide_decorations: bool = False,
- ) -> list[str]:
- """Get texts for multiple targets. If hide_decorations is True, don't show decorations"""
- return cursorless_get_text_action(
- target,
- show_decorations=not hide_decorations,
- ensure_single_target=False,
- )
-
-
-def cursorless_get_text_action(
- target: CursorlessTarget,
- *,
- show_decorations: Optional[bool] = None,
- ensure_single_target: Optional[bool] = None,
-) -> list[str]:
- """Get target texts"""
- options: dict[str, bool] = {}
-
- if show_decorations is not None:
- options["showDecorations"] = show_decorations
-
- if ensure_single_target is not None:
- options["ensureSingleTarget"] = ensure_single_target
-
- return actions.user.private_cursorless_command_get(
- {
- "name": "getText",
- "options": options,
- "target": target,
- }
- )
diff --git a/cursorless-talon/src/actions/homophones.py b/cursorless-talon/src/actions/homophones.py
deleted file mode 100644
index 4b3b7f3280..0000000000
--- a/cursorless-talon/src/actions/homophones.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from typing import Optional
-
-from talon import actions, app
-
-from ..targets.target_types import (
- CursorlessExplicitTarget,
- PrimitiveDestination,
-)
-from .get_text import cursorless_get_text_action
-from .replace import cursorless_replace_action
-
-
-def cursorless_homophones_action(target: CursorlessExplicitTarget):
- """Replaced target with next homophone"""
- texts = cursorless_get_text_action(target, show_decorations=False)
- try:
- updated_texts = list(map(get_next_homophone, texts))
- except LookupError as e:
- app.notify(str(e))
- return
- destination = PrimitiveDestination("to", target)
- cursorless_replace_action(destination, updated_texts)
-
-
-def get_next_homophone(word: str) -> str:
- homophones: Optional[list[str]] = actions.user.homophones_get(word)
- if not homophones:
- raise LookupError(f"Found no homophones for '{word}'")
- index = (homophones.index(word.lower()) + 1) % len(homophones)
- homophone = homophones[index]
- return format_homophone(word, homophone)
-
-
-def format_homophone(word: str, homophone: str) -> str:
- if word.isupper():
- return homophone.upper()
- if word == word.capitalize():
- return homophone.capitalize()
- return homophone
diff --git a/cursorless-talon/src/actions/paste.py b/cursorless-talon/src/actions/paste.py
deleted file mode 100644
index 5ee32b97bc..0000000000
--- a/cursorless-talon/src/actions/paste.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from talon import Module, actions
-
-from ..targets.target_types import CursorlessDestination
-
-mod = Module()
-
-mod.list("cursorless_paste_action", desc="Cursorless paste action")
-
-
-@mod.action_class
-class Actions:
- def private_cursorless_paste(
- destination: CursorlessDestination, # pyright: ignore [reportGeneralTypeIssues]
- ):
- """Execute Cursorless paste action"""
- actions.user.private_cursorless_command_and_wait(
- {
- "name": "pasteFromClipboard",
- "destination": destination,
- }
- )
diff --git a/cursorless-talon/src/actions/reformat.py b/cursorless-talon/src/actions/reformat.py
deleted file mode 100644
index 1b60bee875..0000000000
--- a/cursorless-talon/src/actions/reformat.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from talon import Module, actions
-
-from ..targets.target_types import (
- CursorlessExplicitTarget,
- PrimitiveDestination,
-)
-from .get_text import cursorless_get_text_action
-from .replace import cursorless_replace_action
-
-mod = Module()
-
-mod.list("cursorless_reformat_action", desc="Cursorless reformat action")
-
-
-@mod.action_class
-class Actions:
- def private_cursorless_reformat(
- target: CursorlessExplicitTarget, # pyright: ignore [reportGeneralTypeIssues]
- formatters: str,
- ):
- """Execute Cursorless reformat action. Reformat target with formatter"""
- texts = cursorless_get_text_action(target, show_decorations=False)
- updated_texts = [actions.user.reformat_text(text, formatters) for text in texts]
- destination = PrimitiveDestination("to", target)
- cursorless_replace_action(destination, updated_texts)
diff --git a/cursorless-talon/src/actions/replace.py b/cursorless-talon/src/actions/replace.py
deleted file mode 100644
index 4067eb1f2b..0000000000
--- a/cursorless-talon/src/actions/replace.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from talon import actions
-
-from ..targets.target_types import CursorlessDestination
-
-
-def cursorless_replace_action(
- destination: CursorlessDestination, replace_with: list[str]
-):
- """Execute Cursorless replace action. Replace targets with texts"""
- actions.user.private_cursorless_command_and_wait(
- {
- "name": "replace",
- "replaceWith": replace_with,
- "destination": destination,
- }
- )
diff --git a/cursorless-talon/src/actions/swap.py b/cursorless-talon/src/actions/swap.py
deleted file mode 100644
index 862f7dcaba..0000000000
--- a/cursorless-talon/src/actions/swap.py
+++ /dev/null
@@ -1,49 +0,0 @@
-from dataclasses import dataclass
-
-from talon import Module, actions
-
-from ..targets.target_types import CursorlessTarget, ImplicitTarget
-
-
-@dataclass
-class SwapTargets:
- target1: CursorlessTarget
- target2: CursorlessTarget
-
-
-mod = Module()
-
-mod.list("cursorless_swap_action", desc="Cursorless swap action")
-mod.list(
- "cursorless_swap_connective",
- desc="The connective used to separate swap targets",
-)
-
-
-@mod.capture(
- rule=(
- "[] {user.cursorless_swap_connective} "
- )
-)
-def cursorless_swap_targets(m) -> SwapTargets:
- targets = m.cursorless_target_list
-
- return SwapTargets(
- ImplicitTarget() if len(targets) == 1 else targets[0],
- targets[-1],
- )
-
-
-@mod.action_class
-class Actions:
- def private_cursorless_swap(
- targets: SwapTargets, # pyright: ignore [reportGeneralTypeIssues]
- ):
- """Execute Cursorless swap action"""
- actions.user.private_cursorless_command_and_wait(
- {
- "name": "swapTargets",
- "target1": targets.target1,
- "target2": targets.target2,
- }
- )
diff --git a/cursorless-talon/src/actions/wrap.py b/cursorless-talon/src/actions/wrap.py
deleted file mode 100644
index f9e846fc56..0000000000
--- a/cursorless-talon/src/actions/wrap.py
+++ /dev/null
@@ -1,60 +0,0 @@
-from talon import Module, actions
-
-from ..targets.target_types import CursorlessTarget
-
-mod = Module()
-
-mod.list("cursorless_wrap_action", desc="Cursorless wrap action")
-
-
-@mod.action_class
-class Actions:
- def private_cursorless_wrap_with_paired_delimiter(
- action_name: str, # pyright: ignore [reportGeneralTypeIssues]
- target: CursorlessTarget,
- paired_delimiter: list[str],
- ):
- """Execute Cursorless wrap/rewrap with paired delimiter action"""
- if action_name == "rewrap":
- action_name = "rewrapWithPairedDelimiter"
-
- actions.user.private_cursorless_command_and_wait(
- {
- "name": action_name,
- "left": paired_delimiter[0],
- "right": paired_delimiter[1],
- "target": target,
- }
- )
-
- def private_cursorless_wrap_with_snippet(
- action_name: str, # pyright: ignore [reportGeneralTypeIssues]
- target: CursorlessTarget,
- snippet_location: str,
- ):
- """Execute Cursorless wrap with snippet action"""
- if action_name == "wrapWithPairedDelimiter":
- action_name = "wrapWithSnippet"
- elif action_name == "rewrap":
- raise Exception("Rewrapping with snippet not supported")
-
- snippet_name, variable_name = parse_snippet_location(snippet_location)
-
- actions.user.private_cursorless_command_and_wait(
- {
- "name": action_name,
- "snippetDescription": {
- "type": "named",
- "name": snippet_name,
- "variableName": variable_name,
- },
- "target": target,
- }
- )
-
-
-def parse_snippet_location(snippet_location: str) -> tuple[str, str]:
- [snippet_name, variable_name] = snippet_location.split(".")
- if snippet_name is None or variable_name is None:
- raise Exception("Snippet location missing '.'")
- return (snippet_name, variable_name)
diff --git a/cursorless-talon/src/apps/cursorless_vscode.py b/cursorless-talon/src/apps/cursorless_vscode.py
deleted file mode 100644
index 63c20daa94..0000000000
--- a/cursorless-talon/src/apps/cursorless_vscode.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from talon import Context, actions
-
-ctx = Context()
-
-ctx.matches = r"""
-app: vscode
-"""
-
-ctx.tags = ["user.cursorless"]
-
-
-@ctx.action_class("user")
-class Actions:
- def private_cursorless_show_settings_in_ide():
- """Show Cursorless-specific settings in ide"""
- actions.user.private_cursorless_run_rpc_command_no_wait(
- "workbench.action.openSettings", "@ext:pokey.cursorless "
- )
- actions.sleep("250ms")
- actions.key("right")
-
- def private_cursorless_show_sidebar():
- """Show Cursorless sidebar"""
- actions.user.private_cursorless_run_rpc_command_and_wait(
- "workbench.view.extension.cursorless"
- )
diff --git a/cursorless-talon/src/apps/vscode_settings.py b/cursorless-talon/src/apps/vscode_settings.py
deleted file mode 100644
index f1ec80a631..0000000000
--- a/cursorless-talon/src/apps/vscode_settings.py
+++ /dev/null
@@ -1,114 +0,0 @@
-import os
-import traceback
-from pathlib import Path
-from typing import Any
-
-from talon import Context, Module, actions
-
-from ..vendor.jstyleson import loads
-
-mod = Module()
-
-windows_ctx = Context()
-mac_ctx = Context()
-linux_ctx = Context()
-
-windows_ctx.matches = r"""
-os: windows
-"""
-mac_ctx.matches = r"""
-os: mac
-"""
-linux_ctx.matches = r"""
-os: linux
-"""
-
-
-@mod.action_class
-class Actions:
- def vscode_settings_path() -> Path:
- """Get path of vscode settings json file"""
- ...
-
- def vscode_get_setting(key: str, default_value: Any = None): # pyright: ignore [reportGeneralTypeIssues]
- """Get the value of vscode setting at the given key"""
- path: Path = actions.user.vscode_settings_path()
- settings: dict = loads(path.read_text())
-
- if default_value is not None:
- return settings.get(key, default_value)
- else:
- return settings[key]
-
- def vscode_get_setting_with_fallback(
- key: str, # pyright: ignore [reportGeneralTypeIssues]
- default_value: Any,
- fallback_value: Any,
- fallback_message: str,
- ) -> tuple[Any, bool]:
- """Returns a vscode setting with a fallback in case there's an error
-
- Args:
- key (str): The key of the setting to look up
- default_value (Any): The default value to return if the setting is not defined
- fallback_value (Any): The value to return if there is an error looking up the setting
- fallback_message (str): The message to show to the user if we end up having to use the fallback
-
- Returns:
- tuple[Any, bool]: The value of the setting or the default or fall back, along with boolean which is true if there was an error
- """
- try:
- return actions.user.vscode_get_setting(key, default_value), False
- except Exception:
- print(fallback_message)
- traceback.print_exc()
- return fallback_value, True
-
-
-def pick_path(paths: list[Path]) -> Path:
- existing_paths = [path for path in paths if path.exists()]
- return max(existing_paths, key=lambda path: path.stat().st_mtime)
-
-
-@mac_ctx.action_class("user")
-class MacUserActions:
- def vscode_settings_path() -> Path:
- application_support = Path.home() / "Library/Application Support"
- return pick_path(
- [
- application_support / "Code/User/settings.json",
- application_support / "VSCodium/User/settings.json",
- ]
- )
-
-
-@linux_ctx.action_class("user")
-class LinuxUserActions:
- def vscode_settings_path() -> Path:
- xdg_config_home = Path(
- os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")
- )
- flatpak_apps = Path.home() / ".var/app"
- return pick_path(
- [
- xdg_config_home / "Code/User/settings.json",
- xdg_config_home / "VSCodium/User/settings.json",
- xdg_config_home / "Code - OSS/User/settings.json",
- flatpak_apps / "com.visualstudio.code/config/Code/User/settings.json",
- flatpak_apps / "com.vscodium.codium/config/VSCodium/User/settings.json",
- flatpak_apps
- / "com.visualstudio.code-oss/config/Code - OSS/User/settings.json",
- ]
- )
-
-
-@windows_ctx.action_class("user")
-class WindowsUserActions:
- def vscode_settings_path() -> Path:
- appdata = Path(os.environ["APPDATA"])
- return pick_path(
- [
- appdata / "Code/User/settings.json",
- appdata / "VSCodium/User/settings.json",
- ]
- )
diff --git a/cursorless-talon/src/cheatsheet/cheat_sheet.py b/cursorless-talon/src/cheatsheet/cheat_sheet.py
deleted file mode 100644
index 5d4b938576..0000000000
--- a/cursorless-talon/src/cheatsheet/cheat_sheet.py
+++ /dev/null
@@ -1,154 +0,0 @@
-import webbrowser
-from pathlib import Path
-
-from talon import Context, Module, actions, app
-
-from .get_list import get_list, get_lists
-from .sections.actions import get_actions
-from .sections.compound_targets import get_compound_targets
-from .sections.destinations import get_destinations
-from .sections.get_scope_visualizer import get_scope_visualizer
-from .sections.modifiers import get_modifiers
-from .sections.scopes import get_scopes
-from .sections.special_marks import get_special_marks
-
-mod = Module()
-ctx = Context()
-ctx.matches = r"""
-tag: user.cursorless
-"""
-
-instructions_url = "https://www.cursorless.org/docs/"
-
-
-@mod.action_class
-class Actions:
- def private_cursorless_cheat_sheet_show_html():
- """Show new cursorless html cheat sheet"""
- app.notify(
- 'Please first focus an app that supports cursorless, eg say "focus code"'
- )
-
- def private_cursorless_cheat_sheet_update_json():
- """Update default cursorless cheatsheet json (for developer use only)"""
- app.notify(
- 'Please first focus an app that supports cursorless, eg say "focus code"'
- )
-
- def private_cursorless_open_instructions():
- """Open web page with cursorless instructions"""
- webbrowser.open(instructions_url)
-
-
-@ctx.action_class("user")
-class CursorlessActions:
- def private_cursorless_cheat_sheet_show_html():
- """Show cursorless html cheat sheet"""
- # On Linux browsers installed using snap can't open files in a hidden directory
- if app.platform == "linux":
- cheatsheet_out_dir = cheatsheet_dir_linux()
- cheatsheet_filename = "cursorless-cheatsheet.html"
- else:
- cheatsheet_out_dir = Path.home() / ".cursorless"
- cheatsheet_filename = "cheatsheet.html"
-
- cheatsheet_out_dir.mkdir(parents=True, exist_ok=True)
- cheatsheet_out_path = cheatsheet_out_dir / cheatsheet_filename
- actions.user.private_cursorless_run_rpc_command_and_wait(
- "cursorless.showCheatsheet",
- {
- "version": 0,
- "spokenFormInfo": cursorless_cheat_sheet_get_json(),
- "outputPath": str(cheatsheet_out_path),
- },
- )
- webbrowser.open(cheatsheet_out_path.as_uri())
-
- def private_cursorless_cheat_sheet_update_json():
- """Update default cursorless cheatsheet json (for developer use only)"""
- actions.user.private_cursorless_run_rpc_command_and_wait(
- "cursorless.internal.updateCheatsheetDefaults",
- cursorless_cheat_sheet_get_json(),
- )
-
-
-def cheatsheet_dir_linux() -> Path:
- """Get cheatsheet directory for Linux"""
- try:
- # 1. Get users actual document directory
- import platformdirs # pyright: ignore [reportMissingImports]
-
- return Path(platformdirs.user_documents_dir())
- except Exception:
- # 2. Look for a documents directory in user home
- user_documents_dir = Path.home() / "Documents"
- if user_documents_dir.is_dir():
- return user_documents_dir
-
- # 3. Fall back to user home
- return Path.home()
-
-
-def cursorless_cheat_sheet_get_json():
- """Get cursorless cheat sheet json"""
- return {
- "sections": [
- {
- "name": "Actions",
- "id": "actions",
- "items": get_actions(),
- },
- {
- "name": "Destinations",
- "id": "destinations",
- "items": get_destinations(),
- },
- {
- "name": "Scopes",
- "id": "scopes",
- "items": get_scopes(),
- },
- {
- "name": "Scope visualizer",
- "id": "scopeVisualizer",
- "items": get_scope_visualizer(),
- },
- {
- "name": "Modifiers",
- "id": "modifiers",
- "items": get_modifiers(),
- },
- {
- "name": "Paired delimiters",
- "id": "pairedDelimiters",
- "items": get_lists(
- [
- "wrapper_only_paired_delimiter",
- "wrapper_selectable_paired_delimiter",
- "selectable_only_paired_delimiter",
- ],
- "pairedDelimiter",
- ),
- },
- {
- "name": "Special marks",
- "id": "specialMarks",
- "items": get_special_marks(),
- },
- {
- "name": "Compound targets",
- "id": "compoundTargets",
- "items": get_compound_targets(),
- },
- {
- "name": "Colors",
- "id": "colors",
- "items": get_list("hat_color", "hatColor"),
- },
- {
- "name": "Shapes",
- "id": "shapes",
- "items": get_list("hat_shape", "hatShape"),
- },
- ]
- }
diff --git a/cursorless-talon/src/cheatsheet/get_list.py b/cursorless-talon/src/cheatsheet/get_list.py
deleted file mode 100644
index 341873fd6a..0000000000
--- a/cursorless-talon/src/cheatsheet/get_list.py
+++ /dev/null
@@ -1,92 +0,0 @@
-import re
-import typing
-from collections.abc import Mapping, Sequence
-from typing import Optional, TypedDict
-
-from talon import registry
-
-from ..conventions import get_cursorless_list_name
-
-
-class Variation(TypedDict):
- spokenForm: str
- description: str
-
-
-class ListItemDescriptor(TypedDict):
- id: str
- type: str
- variations: list[Variation]
-
-
-def get_list(
- name: str, type: str, descriptions: Optional[Mapping[str, str]] = None
-) -> list[ListItemDescriptor]:
- if descriptions is None:
- descriptions = {}
-
- items = get_raw_list(name)
-
- return make_dict_readable(type, items, descriptions)
-
-
-def get_lists(
- names: Sequence[str], type: str, descriptions: Optional[Mapping[str, str]] = None
-) -> list[ListItemDescriptor]:
- return [item for name in names for item in get_list(name, type, descriptions)]
-
-
-def get_raw_list(name: str) -> Mapping[str, str]:
- cursorless_list_name = get_cursorless_list_name(name)
- return typing.cast(dict[str, str], registry.lists[cursorless_list_name][0]).copy()
-
-
-def get_spoken_form_from_list(list_name: str, value: str) -> str:
- """Get the spoken form of a value from a list.
-
- Args:
- list_name (str): The name of the list.
- value (str): The value to look up.
-
- Returns:
- str: The spoken form of the value.
- """
- return next(
- spoken_form for spoken_form, v in get_raw_list(list_name).items() if v == value
- )
-
-
-def make_dict_readable(
- type: str, dict: Mapping[str, str], descriptions: Mapping[str, str]
-) -> list[ListItemDescriptor]:
- return [
- {
- "id": value,
- "type": type,
- "variations": [
- {
- "spokenForm": key,
- "description": descriptions.get(value, make_readable(value)),
- }
- ],
- }
- for key, value in dict.items()
- ]
-
-
-def make_readable(text: str) -> str:
- text, is_private = (
- (text[8:], True) if text.startswith("private.") else (text, False)
- )
- text = text.replace(".", " ")
- text = de_camel(text).lower().capitalize()
- return f"{text} (PRIVATE)" if is_private else text
-
-
-def de_camel(text: str) -> str:
- """Replacing camelCase boundaries with blank space"""
- return re.sub(
- r"(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?<=[a-zA-Z])(?=[0-9])|(?<=[0-9])(?=[a-zA-Z])",
- " ",
- text,
- )
diff --git a/cursorless-talon/src/cheatsheet/sections/actions.py b/cursorless-talon/src/cheatsheet/sections/actions.py
deleted file mode 100644
index b83c3e5ec8..0000000000
--- a/cursorless-talon/src/cheatsheet/sections/actions.py
+++ /dev/null
@@ -1,138 +0,0 @@
-from ...actions.actions import ACTION_LIST_NAMES
-from ..get_list import get_raw_list, make_dict_readable
-
-
-def get_actions():
- all_actions = {}
- for name in ACTION_LIST_NAMES:
- all_actions.update(get_raw_list(name))
-
- multiple_target_action_names = [
- "replaceWithTarget",
- "moveToTarget",
- "swapTargets",
- "applyFormatter",
- "callAsFunction",
- "wrapWithPairedDelimiter",
- "rewrap",
- "pasteFromClipboard",
- ]
- simple_actions = {
- f"{key} ": value
- for key, value in all_actions.items()
- if value not in multiple_target_action_names
- }
- complex_actions = {
- value: key
- for key, value in all_actions.items()
- if value in multiple_target_action_names
- }
-
- swap_connective = list(get_raw_list("swap_connective").keys())[0]
-
- return [
- *make_dict_readable(
- "action",
- simple_actions,
- {
- "editNewLineAfter": "Edit new line/scope after",
- "editNewLineBefore": "Edit new line/scope before",
- },
- ),
- {
- "id": "replaceWithTarget",
- "type": "action",
- "variations": [
- {
- "spokenForm": f"{complex_actions['replaceWithTarget']} ",
- "description": "Copy to ",
- },
- {
- "spokenForm": f"{complex_actions['replaceWithTarget']} ",
- "description": "Insert copy of at cursor",
- },
- ],
- },
- {
- "id": "pasteFromClipboard",
- "type": "action",
- "variations": [
- {
- "spokenForm": f"{complex_actions['pasteFromClipboard']} ",
- "description": "Paste from clipboard at ",
- }
- ],
- },
- {
- "id": "moveToTarget",
- "type": "action",
- "variations": [
- {
- "spokenForm": f"{complex_actions['moveToTarget']} ",
- "description": "Move to ",
- },
- {
- "spokenForm": f"{complex_actions['moveToTarget']} ",
- "description": "Move to cursor position",
- },
- ],
- },
- {
- "id": "swapTargets",
- "type": "action",
- "variations": [
- {
- "spokenForm": f"{complex_actions['swapTargets']} {swap_connective} ",
- "description": "Swap with ",
- },
- {
- "spokenForm": f"{complex_actions['swapTargets']} {swap_connective} ",
- "description": "Swap selection with ",
- },
- ],
- },
- {
- "id": "applyFormatter",
- "type": "action",
- "variations": [
- {
- "spokenForm": f"{complex_actions['applyFormatter']} at ",
- "description": "Reformat as ",
- }
- ],
- },
- {
- "id": "callAsFunction",
- "type": "action",
- "variations": [
- {
- "spokenForm": f"{complex_actions['callAsFunction']} ",
- "description": "Call on selection",
- },
- {
- "spokenForm": f"{complex_actions['callAsFunction']} on ",
- "description": "Call on ",
- },
- ],
- },
- {
- "id": "wrapWithPairedDelimiter",
- "type": "action",
- "variations": [
- {
- "spokenForm": f" {complex_actions['wrapWithPairedDelimiter']} ",
- "description": "Wrap with ",
- }
- ],
- },
- {
- "id": "rewrap",
- "type": "action",
- "variations": [
- {
- "spokenForm": f" {complex_actions['rewrap']} ",
- "description": "Rewrap with ",
- }
- ],
- },
- ]
diff --git a/cursorless-talon/src/cheatsheet/sections/compound_targets.py b/cursorless-talon/src/cheatsheet/sections/compound_targets.py
deleted file mode 100644
index 9f1b85b439..0000000000
--- a/cursorless-talon/src/cheatsheet/sections/compound_targets.py
+++ /dev/null
@@ -1,53 +0,0 @@
-from ..get_list import get_raw_list, get_spoken_form_from_list
-
-FORMATTERS = {
- "rangeExclusive": lambda start, end: f"between {start} and {end}",
- "rangeInclusive": lambda start, end: f"{start} through {end}",
- "rangeExcludingStart": lambda start, end: f"end of {start} through {end}",
- "rangeExcludingEnd": lambda start, end: f"{start} until start of {end}",
- "verticalRange": lambda start, end: f"{start} vertically through {end}",
-}
-
-
-def get_compound_targets():
- list_connective_term = get_spoken_form_from_list(
- "list_connective", "listConnective"
- )
- vertical_range_term = get_spoken_form_from_list("range_type", "verticalRange")
-
- return [
- {
- "id": "listConnective",
- "type": "compoundTargetConnective",
- "variations": [
- {
- "spokenForm": f" {list_connective_term} ",
- "description": " and ",
- },
- ],
- },
- *[
- get_entry(spoken_form, id)
- for spoken_form, id in get_raw_list("range_connective").items()
- ],
- get_entry(vertical_range_term, "verticalRange"),
- ]
-
-
-def get_entry(spoken_form, id):
- formatter = FORMATTERS[id]
-
- return {
- "id": id,
- "type": "compoundTargetConnective",
- "variations": [
- {
- "spokenForm": f" {spoken_form} ",
- "description": formatter("", ""),
- },
- {
- "spokenForm": f"{spoken_form} ",
- "description": formatter("selection", ""),
- },
- ],
- }
diff --git a/cursorless-talon/src/cheatsheet/sections/destinations.py b/cursorless-talon/src/cheatsheet/sections/destinations.py
deleted file mode 100644
index d269fb2e53..0000000000
--- a/cursorless-talon/src/cheatsheet/sections/destinations.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from ..get_list import get_raw_list
-
-
-def get_destinations():
- insertion_modes = {
- **{p: "to" for p in get_raw_list("insertion_mode_to")},
- **get_raw_list("insertion_mode_before_after"),
- }
-
- descriptions = {
- "to": "Replace ",
- "before": "Insert before ",
- "after": "Insert after ",
- }
-
- return [
- {
- "id": f"destination_{id}",
- "type": "destination",
- "variations": [
- {
- "spokenForm": f"{spoken_form} ",
- "description": descriptions[id],
- }
- ],
- }
- for spoken_form, id in insertion_modes.items()
- ]
diff --git a/cursorless-talon/src/cheatsheet/sections/get_scope_visualizer.py b/cursorless-talon/src/cheatsheet/sections/get_scope_visualizer.py
deleted file mode 100644
index 69115fbe1e..0000000000
--- a/cursorless-talon/src/cheatsheet/sections/get_scope_visualizer.py
+++ /dev/null
@@ -1,37 +0,0 @@
-from ..get_list import get_list, get_raw_list, make_readable
-
-
-def get_scope_visualizer():
- show_scope_visualizer = list(get_raw_list("show_scope_visualizer").keys())[0]
- visualization_types = get_raw_list("visualization_type")
-
- return [
- *get_list("hide_scope_visualizer", "command"),
- {
- "id": "show_scope_visualizer",
- "type": "command",
- "variations": [
- {
- "spokenForm": f"{show_scope_visualizer} ",
- "description": "Visualize ",
- },
- *[
- {
- "spokenForm": f"{show_scope_visualizer} {spoken_form}",
- "description": f"Visualize {make_readable(id).lower()} range",
- }
- for spoken_form, id in visualization_types.items()
- ],
- ],
- },
- {
- "id": "show_scope_sidebar",
- "type": "command",
- "variations": [
- {
- "spokenForm": "bar cursorless",
- "description": "Show cursorless sidebar",
- },
- ],
- },
- ]
diff --git a/cursorless-talon/src/cheatsheet/sections/modifiers.py b/cursorless-talon/src/cheatsheet/sections/modifiers.py
deleted file mode 100644
index af8164d312..0000000000
--- a/cursorless-talon/src/cheatsheet/sections/modifiers.py
+++ /dev/null
@@ -1,216 +0,0 @@
-from itertools import chain
-from typing import TypedDict
-
-from ..get_list import get_raw_list, make_dict_readable
-
-MODIFIER_LIST_NAMES = [
- "simple_modifier",
- "interior_modifier",
- "head_tail_modifier",
- "every_scope_modifier",
- "ancestor_scope_modifier",
- "first_modifier",
- "last_modifier",
- "previous_next_modifier",
- "forward_backward_modifier",
- "position",
-]
-
-
-def get_modifiers():
- all_modifiers = {}
- for name in MODIFIER_LIST_NAMES:
- all_modifiers.update(get_raw_list(name))
-
- complex_modifier_ids = [
- "extendThroughStartOf",
- "extendThroughEndOf",
- "every",
- "ancestor",
- "first",
- "last",
- "previous",
- "next",
- "backward",
- "forward",
- ]
- simple_modifiers = {
- key: value
- for key, value in all_modifiers.items()
- if value not in complex_modifier_ids
- }
- complex_modifiers = {
- value: key
- for key, value in all_modifiers.items()
- if value in complex_modifier_ids
- }
-
- return [
- *make_dict_readable(
- "modifier",
- simple_modifiers,
- {
- "excludeInterior": "Bounding paired delimiters",
- "toRawSelection": "No inference",
- "leading": "Leading delimiter range",
- "trailing": "Trailing delimiter range",
- "start": "Empty position at start of target",
- "end": "Empty position at end of target",
- },
- ),
- {
- "id": "extendThroughStartOf",
- "type": "modifier",
- "variations": [
- {
- "spokenForm": complex_modifiers["extendThroughStartOf"],
- "description": "Extend through start of line",
- },
- {
- "spokenForm": f"{complex_modifiers['extendThroughStartOf']} ",
- "description": "Extend through start of ",
- },
- ],
- },
- {
- "id": "extendThroughEndOf",
- "type": "modifier",
- "variations": [
- {
- "spokenForm": complex_modifiers["extendThroughEndOf"],
- "description": "Extend through end of line",
- },
- {
- "spokenForm": f"{complex_modifiers['extendThroughEndOf']} ",
- "description": "Extend through end of ",
- },
- ],
- },
- {
- "id": "containingScope",
- "type": "modifier",
- "variations": [
- {
- "spokenForm": "",
- "description": "Containing instance of ",
- },
- ],
- },
- {
- "id": "every",
- "type": "modifier",
- "variations": [
- {
- "spokenForm": f"{complex_modifiers['every']} ",
- "description": "Every instance of ",
- },
- ],
- },
- {
- "id": "ancestor",
- "type": "modifier",
- "variations": [
- {
- "spokenForm": f"{complex_modifiers['ancestor']} ",
- "description": "Grandparent containing instance of ",
- },
- ],
- },
- {
- "id": "relativeScope",
- "type": "modifier",
- "variations": [
- {
- "spokenForm": f"{complex_modifiers['previous']} ",
- "description": "Previous instance of ",
- },
- {
- "spokenForm": f"{complex_modifiers['next']} ",
- "description": "Next instance of ",
- },
- {
- "spokenForm": f" {complex_modifiers['previous']} ",
- "description": " instance of before target",
- },
- {
- "spokenForm": f" {complex_modifiers['next']} ",
- "description": " instance of after target",
- },
- {
- "spokenForm": f" {complex_modifiers['backward']}",
- "description": "single instance of including target, going backwards",
- },
- {
- "spokenForm": f" {complex_modifiers['forward']}",
- "description": "single instance of including target, going forwards",
- },
- *generateOptionalEvery(
- complex_modifiers["every"],
- {
- "spokenForm": f" s {complex_modifiers['backward']}",
- "description": " instances of including target, going backwards",
- },
- {
- "spokenForm": " s",
- "description": " instances of including target, going forwards",
- },
- {
- "spokenForm": f"{complex_modifiers['previous']} s",
- "description": "previous instances of ",
- },
- {
- "spokenForm": f"{complex_modifiers['next']} s",
- "description": "next instances of ",
- },
- ),
- ],
- },
- {
- "id": "ordinalScope",
- "type": "modifier",
- "variations": [
- {
- "spokenForm": " ",
- "description": " instance of in iteration scope",
- },
- {
- "spokenForm": f" {complex_modifiers['last']} ",
- "description": "-to-last instance of in iteration scope",
- },
- *generateOptionalEvery(
- complex_modifiers["every"],
- {
- "spokenForm": f"{complex_modifiers['first']} s",
- "description": "first instances of in iteration scope",
- },
- {
- "spokenForm": f"{complex_modifiers['last']} s",
- "description": "last instances of in iteration scope",
- },
- ),
- ],
- },
- ]
-
-
-class Entry(TypedDict):
- spokenForm: str
- description: str
-
-
-def generateOptionalEvery(every: str, *entries: Entry) -> list[Entry]:
- return list(
- chain.from_iterable(
- [
- {
- "spokenForm": entry["spokenForm"],
- "description": f"{entry['description']}, as contiguous range",
- },
- {
- "spokenForm": f"{every} {entry['spokenForm']}",
- "description": f"{entry['description']}, as individual targets",
- },
- ]
- for entry in entries
- )
- )
diff --git a/cursorless-talon/src/cheatsheet/sections/scopes.py b/cursorless-talon/src/cheatsheet/sections/scopes.py
deleted file mode 100644
index 462074beae..0000000000
--- a/cursorless-talon/src/cheatsheet/sections/scopes.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from ..get_list import get_lists, get_spoken_form_from_list
-
-
-def get_scopes():
- glyph_spoken_form = get_spoken_form_from_list("glyph_scope_type", "glyph")
- return [
- *get_lists(
- ["scope_type"],
- "scopeType",
- {
- "argumentOrParameter": "Argument",
- "boundedNonWhitespaceSequence": "Non whitespace sequence stopped by surrounding pair delimeters",
- },
- ),
- {
- "id": "glyph",
- "type": "scopeType",
- "variations": [
- {
- "spokenForm": f"{glyph_spoken_form} ",
- "description": "Instance of single character ",
- },
- ],
- },
- ]
diff --git a/cursorless-talon/src/cheatsheet/sections/special_marks.py b/cursorless-talon/src/cheatsheet/sections/special_marks.py
deleted file mode 100644
index 41f2572c38..0000000000
--- a/cursorless-talon/src/cheatsheet/sections/special_marks.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from ..get_list import get_lists, get_raw_list, make_dict_readable
-
-
-def get_special_marks():
- line_direction_marks = make_dict_readable(
- "mark",
- {
- f"{key} ": value
- for key, value in get_raw_list("line_direction").items()
- },
- {
- "lineNumberRelativeUp": "Line number up from cursor",
- "lineNumberRelativeDown": "Line number down from cursor",
- },
- )
-
- return [
- *get_lists(["simple_mark", "unknown_symbol"], "mark"),
- *line_direction_marks,
- ]
diff --git a/cursorless-talon/src/command.py b/cursorless-talon/src/command.py
deleted file mode 100644
index 5d7c94594d..0000000000
--- a/cursorless-talon/src/command.py
+++ /dev/null
@@ -1,103 +0,0 @@
-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 value.__class__.__dict__.items() if k[0] != "_"},
- **value.__dict__,
- }
- return {k: make_serializable(v) for k, v in items.items() if v is not None}
- return value
diff --git a/cursorless-talon/src/conventions.py b/cursorless-talon/src/conventions.py
deleted file mode 100644
index a3495f1f2e..0000000000
--- a/cursorless-talon/src/conventions.py
+++ /dev/null
@@ -1,2 +0,0 @@
-def get_cursorless_list_name(name: str):
- return f"user.cursorless_{name}"
diff --git a/cursorless-talon/src/csv_overrides.py b/cursorless-talon/src/csv_overrides.py
deleted file mode 100644
index f71b0d9097..0000000000
--- a/cursorless-talon/src/csv_overrides.py
+++ /dev/null
@@ -1,471 +0,0 @@
-import csv
-import typing
-from collections import defaultdict
-from collections.abc import Container
-from dataclasses import dataclass
-from datetime import datetime
-from pathlib import Path
-from typing import Callable, Iterable, Optional, TypedDict
-
-from talon import Context, Module, actions, app, fs, settings
-
-from .conventions import get_cursorless_list_name
-from .vendor.inflection import pluralize
-
-SPOKEN_FORM_HEADER = "Spoken form"
-CURSORLESS_IDENTIFIER_HEADER = "Cursorless identifier"
-
-
-mod = Module()
-mod.tag(
- "cursorless_default_vocabulary",
- desc="Use default cursorless vocabulary instead of user custom",
-)
-mod.setting(
- "cursorless_settings_directory",
- type=str,
- default="cursorless-settings",
- desc="The directory to use for cursorless settings csvs relative to talon user directory",
-)
-
-# The global context we use for our lists
-ctx = Context()
-
-# A context that contains default vocabulary, for use in testing
-normalized_ctx = Context()
-normalized_ctx.matches = r"""
-tag: user.cursorless_default_vocabulary
-"""
-
-
-# Maps from Talon list name to a map from spoken form to value
-ListToSpokenForms = dict[str, dict[str, str]]
-
-
-@dataclass
-class SpokenFormEntry:
- list_name: str
- id: str
- spoken_forms: list[str]
-
-
-def init_csv_and_watch_changes(
- filename: str,
- default_values: ListToSpokenForms,
- handle_new_values: Optional[Callable[[list[SpokenFormEntry]], None]] = None,
- *,
- extra_ignored_values: Optional[list[str]] = None,
- extra_allowed_values: Optional[list[str]] = None,
- allow_unknown_values: bool = False,
- default_list_name: Optional[str] = None,
- headers: list[str] = [SPOKEN_FORM_HEADER, CURSORLESS_IDENTIFIER_HEADER],
- no_update_file: bool = False,
- pluralize_lists: Optional[list[str]] = None,
-):
- """
- Initialize a cursorless settings csv, creating it if necessary, and watch
- for changes to the csv. Talon lists will be generated based on the keys of
- `default_values`. For example, if there is a key `foo`, there will be a
- list created called `user.cursorless_foo` that will contain entries from the
- original dict at the key `foo`, updated according to customization in the
- csv at
-
- ```
- actions.path.talon_user() / "cursorless-settings" / filename
- ```
-
- Note that the settings directory location can be customized using the
- `user.cursorless_settings_directory` setting.
-
- Args:
- filename (str): The name of the csv file to be placed in
- `cursorles-settings` dir
- default_values (ListToSpokenForms): The default values for the lists to
- be customized in the given csv
- handle_new_values (Optional[Callable[[list[SpokenFormEntry]], None]]): A
- callback to be called when the lists are updated
- extra_ignored_values (Optional[list[str]]): Don't throw an exception if
- any of these appear as values; just ignore them and don't add them
- to any list
- allow_unknown_values (bool): If unknown values appear, just put them in
- the list
- default_list_name (Optional[str]): If unknown values are
- allowed, put any unknown values in this list
- headers (list[str]): The headers to use for the csv
- no_update_file (bool): Set this to `True` to indicate that we should not
- update the csv. This is used generally in case there was an issue
- coming up with the default set of values so we don't want to persist
- those to disk
- pluralize_lists (list[str]): Create plural version of given lists
- """
- # Don't allow both `extra_allowed_values` and `allow_unknown_values`
- assert not (extra_allowed_values and allow_unknown_values)
-
- # If `extra_allowed_values` or `allow_unknown_values` is given, we need a
- # `default_list_name` to put unknown values in
- assert not (
- (extra_allowed_values or allow_unknown_values) and not default_list_name
- )
-
- if extra_ignored_values is None:
- extra_ignored_values = []
- if extra_allowed_values is None:
- extra_allowed_values = []
- if pluralize_lists is None:
- pluralize_lists = []
-
- file_path = get_full_path(filename)
- super_default_values = get_super_values(default_values)
-
- file_path.parent.mkdir(parents=True, exist_ok=True)
-
- check_for_duplicates(filename, default_values)
- create_default_vocabulary_dicts(default_values, pluralize_lists)
-
- def on_watch(path, flags):
- if file_path.match(path):
- current_values, has_errors = read_file(
- path=file_path,
- headers=headers,
- default_identifiers=super_default_values.values(),
- extra_ignored_values=extra_ignored_values,
- extra_allowed_values=extra_allowed_values,
- allow_unknown_values=allow_unknown_values,
- )
- update_dicts(
- default_values=default_values,
- current_values=current_values,
- extra_ignored_values=extra_ignored_values,
- extra_allowed_values=extra_allowed_values,
- allow_unknown_values=allow_unknown_values,
- default_list_name=default_list_name,
- pluralize_lists=pluralize_lists,
- handle_new_values=handle_new_values,
- )
-
- fs.watch(str(file_path.parent), on_watch)
-
- if file_path.is_file():
- current_values = update_file(
- path=file_path,
- headers=headers,
- default_values=super_default_values,
- extra_ignored_values=extra_ignored_values,
- extra_allowed_values=extra_allowed_values,
- allow_unknown_values=allow_unknown_values,
- no_update_file=no_update_file,
- )
- update_dicts(
- default_values=default_values,
- current_values=current_values,
- extra_ignored_values=extra_ignored_values,
- extra_allowed_values=extra_allowed_values,
- allow_unknown_values=allow_unknown_values,
- default_list_name=default_list_name,
- pluralize_lists=pluralize_lists,
- handle_new_values=handle_new_values,
- )
- else:
- if not no_update_file:
- create_file(file_path, headers, super_default_values)
- update_dicts(
- default_values=default_values,
- current_values=super_default_values,
- extra_ignored_values=extra_ignored_values,
- extra_allowed_values=extra_allowed_values,
- allow_unknown_values=allow_unknown_values,
- default_list_name=default_list_name,
- pluralize_lists=pluralize_lists,
- handle_new_values=handle_new_values,
- )
-
- def unsubscribe():
- fs.unwatch(str(file_path.parent), on_watch)
-
- return unsubscribe
-
-
-def check_for_duplicates(filename, default_values):
- results_map = {}
- for list_name, dict in default_values.items():
- for key, value in dict.items():
- if value in results_map:
- existing_list_name = results_map[value]["list"]
- warning = f"WARNING ({filename}): Value `{value}` duplicated between lists '{existing_list_name}' and '{list_name}'"
- print(warning)
- app.notify(warning)
-
-
-def is_removed(value: str):
- return value.startswith("-")
-
-
-def create_default_vocabulary_dicts(
- default_values: dict[str, dict], pluralize_lists: list[str]
-):
- default_values_updated = {}
- for key, value in default_values.items():
- updated_dict = {}
- for key2, value2 in value.items():
- # Enable deactivated(prefixed with a `-`) items
- active_key = key2[1:] if key2.startswith("-") else key2
- if active_key:
- updated_dict[active_key] = value2
- default_values_updated[key] = updated_dict
- assign_lists_to_context(normalized_ctx, default_values_updated, pluralize_lists)
-
-
-def update_dicts(
- default_values: ListToSpokenForms,
- current_values: dict[str, str],
- extra_ignored_values: list[str],
- extra_allowed_values: list[str],
- allow_unknown_values: bool,
- default_list_name: Optional[str],
- pluralize_lists: list[str],
- handle_new_values: Optional[Callable[[list[SpokenFormEntry]], None]],
-):
- # Create map with all default values
- results_map: dict[str, ResultsListEntry] = {}
- for list_name, obj in default_values.items():
- for spoken, id in obj.items():
- results_map[id] = {"spoken": spoken, "id": id, "list": list_name}
-
- # Update result with current values
- for spoken, id in current_values.items():
- try:
- results_map[id]["spoken"] = spoken
- except KeyError:
- if id in extra_ignored_values:
- pass
- elif allow_unknown_values or id in extra_allowed_values:
- assert default_list_name is not None
- results_map[id] = {
- "spoken": spoken,
- "id": id,
- "list": default_list_name,
- }
- else:
- raise
-
- spoken_form_entries = list(generate_spoken_forms(results_map.values()))
-
- # Assign result to talon context list
- lists: ListToSpokenForms = defaultdict(dict)
- for entry in spoken_form_entries:
- for spoken_form in entry.spoken_forms:
- lists[entry.list_name][spoken_form] = entry.id
- assign_lists_to_context(ctx, lists, pluralize_lists)
-
- if handle_new_values is not None:
- handle_new_values(spoken_form_entries)
-
-
-class ResultsListEntry(TypedDict):
- spoken: str
- id: str
- list: str
-
-
-def generate_spoken_forms(results_list: Iterable[ResultsListEntry]):
- for obj in results_list:
- id = obj["id"]
- spoken = obj["spoken"]
-
- spoken_forms = []
- if not is_removed(spoken):
- for k in spoken.split("|"):
- if id == "pasteFromClipboard" and k.endswith(" to"):
- # FIXME: This is a hack to work around the fact that the
- # spoken form of the `pasteFromClipboard` action used to be
- # "paste to", but now the spoken form is just "paste" and
- # the "to" is part of the positional target. Users who had
- # cursorless before this change would have "paste to" as
- # their spoken form and so would need to say "paste to to".
- k = k[:-3]
- spoken_forms.append(k.strip())
-
- yield SpokenFormEntry(
- list_name=obj["list"],
- id=id,
- spoken_forms=spoken_forms,
- )
-
-
-def assign_lists_to_context(
- ctx: Context,
- lists: ListToSpokenForms,
- pluralize_lists: list[str],
-):
- for list_name, dict in lists.items():
- list_singular_name = get_cursorless_list_name(list_name)
- ctx.lists[list_singular_name] = dict
- if list_name in pluralize_lists:
- list_plural_name = f"{list_singular_name}_plural"
- ctx.lists[list_plural_name] = {pluralize(k): v for k, v in dict.items()}
-
-
-def update_file(
- path: Path,
- headers: list[str],
- default_values: dict[str, str],
- extra_ignored_values: list[str],
- extra_allowed_values: list[str],
- allow_unknown_values: bool,
- no_update_file: bool,
-):
- current_values, has_errors = read_file(
- path=path,
- headers=headers,
- default_identifiers=default_values.values(),
- extra_ignored_values=extra_ignored_values,
- extra_allowed_values=extra_allowed_values,
- allow_unknown_values=allow_unknown_values,
- )
- current_identifiers = current_values.values()
-
- missing = {}
- for key, value in default_values.items():
- if value not in current_identifiers:
- missing[key] = value
-
- if missing:
- if has_errors or no_update_file:
- print(
- "NOTICE: New cursorless features detected, but refusing to update "
- "csv due to errors. Please fix csv errors above and restart talon"
- )
- else:
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
- lines = [
- f"# {timestamp} - New entries automatically added by cursorless",
- *[create_line(key, missing[key]) for key in sorted(missing)],
- ]
- with open(path, "a") as f:
- f.write("\n\n" + "\n".join(lines))
-
- print(f"New cursorless features added to {path.name}")
- for key in sorted(missing):
- print(f"{key}: {missing[key]}")
- print(
- "See release notes for more info: "
- "https://github.com/cursorless-dev/cursorless/blob/main/CHANGELOG.md"
- )
- app.notify("🎉🎉 New cursorless features; see log")
-
- return current_values
-
-
-def create_line(*cells: str):
- return ", ".join(cells)
-
-
-def create_file(path: Path, headers: list[str], default_values: dict):
- lines = [create_line(key, default_values[key]) for key in sorted(default_values)]
- lines.insert(0, create_line(*headers))
- lines.append("")
- path.write_text("\n".join(lines))
-
-
-def csv_error(path: Path, index: int, message: str, value: str):
- """Check that an expected condition is true
-
- Note that we try to continue reading in this case so cursorless doesn't get bricked
-
- Args:
- path (Path): The path of the CSV (for error reporting)
- index (int): The index into the file (for error reporting)
- text (str): The text of the error message to report if condition is false
- """
- print(f"ERROR: {path}:{index+1}: {message} '{value}'")
-
-
-def read_file(
- path: Path,
- headers: list[str],
- default_identifiers: Container[str],
- extra_ignored_values: list[str],
- extra_allowed_values: list[str],
- allow_unknown_values: bool,
-):
- with open(path) as csv_file:
- # Use `skipinitialspace` to allow spaces before quote. `, "a,b"`
- csv_reader = csv.reader(csv_file, skipinitialspace=True)
- rows = list(csv_reader)
-
- result = {}
- used_identifiers = []
- has_errors = False
- seen_headers = False
-
- for i, row in enumerate(rows):
- # Remove trailing whitespaces for each cell
- row = [x.rstrip() for x in row]
- # Exclude empty or comment rows
- if len(row) == 0 or (len(row) == 1 and row[0] == "") or row[0].startswith("#"):
- continue
-
- if not seen_headers:
- seen_headers = True
- if row != headers:
- has_errors = True
- csv_error(path, i, "Malformed header", create_line(*row))
- print(f"Expected '{create_line(*headers)}'")
- continue
-
- if len(row) != len(headers):
- has_errors = True
- csv_error(
- path,
- i,
- f"Malformed csv entry. Expected {len(headers)} columns.",
- create_line(*row),
- )
- continue
-
- key, value = row
-
- if (
- value not in default_identifiers
- and value not in extra_ignored_values
- and value not in extra_allowed_values
- and not allow_unknown_values
- ):
- has_errors = True
- csv_error(path, i, "Unknown identifier", value)
- continue
-
- if value in used_identifiers:
- has_errors = True
- csv_error(path, i, "Duplicate identifier", value)
- continue
-
- result[key] = value
- used_identifiers.append(value)
-
- if has_errors:
- app.notify("Cursorless settings error; see log")
-
- return result, has_errors
-
-
-def get_full_path(filename: str):
- if not filename.endswith(".csv"):
- filename = f"{filename}.csv"
-
- user_dir: Path = actions.path.talon_user()
- settings_directory = Path(
- typing.cast(str, settings.get("user.cursorless_settings_directory"))
- )
-
- if not settings_directory.is_absolute():
- settings_directory = user_dir / settings_directory
-
- return (settings_directory / filename).resolve()
-
-
-def get_super_values(values: ListToSpokenForms):
- result: dict[str, str] = {}
- for value_dict in values.values():
- result.update(value_dict)
- return result
diff --git a/cursorless-talon/src/cursorless.py b/cursorless-talon/src/cursorless.py
deleted file mode 100644
index 9617f51593..0000000000
--- a/cursorless-talon/src/cursorless.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from talon import Module, actions
-
-mod = Module()
-
-mod.tag(
- "cursorless",
- "Application supporting cursorless commands",
-)
-
-
-@mod.action_class
-class Actions:
- def private_cursorless_show_settings_in_ide():
- """Show Cursorless-specific settings in ide"""
-
- def private_cursorless_show_sidebar():
- """Show Cursorless-specific settings in ide"""
-
- def private_cursorless_show_command_statistics():
- """Show Cursorless command statistics"""
- actions.user.private_cursorless_run_rpc_command_no_wait(
- "cursorless.analyzeCommandHistory"
- )
diff --git a/cursorless-talon/src/cursorless.talon b/cursorless-talon/src/cursorless.talon
deleted file mode 100644
index f1ab5aa206..0000000000
--- a/cursorless-talon/src/cursorless.talon
+++ /dev/null
@@ -1,39 +0,0 @@
-mode: command
-mode: user.cursorless_spoken_form_test
-tag: user.cursorless
--
-
- :
- user.private_cursorless_action_or_ide_command(cursorless_action_or_ide_command, cursorless_target)
-
-{user.cursorless_bring_move_action} :
- user.private_cursorless_bring_move(cursorless_bring_move_action, cursorless_bring_move_targets)
-
-{user.cursorless_swap_action} :
- user.private_cursorless_swap(cursorless_swap_targets)
-
-{user.cursorless_paste_action} :
- user.private_cursorless_paste(cursorless_destination)
-
-{user.cursorless_reformat_action} at :
- user.private_cursorless_reformat(cursorless_target, formatters)
-
-{user.cursorless_call_action} on :
- user.private_cursorless_call(cursorless_target_1, cursorless_target_2)
-
- {user.cursorless_wrap_action} :
- user.private_cursorless_wrap_with_paired_delimiter(cursorless_wrap_action, cursorless_target, cursorless_wrapper_paired_delimiter)
-
-{user.cursorless_show_scope_visualizer} [{user.cursorless_visualization_type}]:
- user.private_cursorless_show_scope_visualizer(cursorless_scope_type, cursorless_visualization_type or "content")
-{user.cursorless_hide_scope_visualizer}:
- user.private_cursorless_hide_scope_visualizer()
-
-{user.cursorless_homophone} settings:
- user.private_cursorless_show_settings_in_ide()
-
-bar {user.cursorless_homophone}:
- user.private_cursorless_show_sidebar()
-
-{user.cursorless_homophone} stats:
- user.private_cursorless_show_command_statistics()
diff --git a/cursorless-talon/src/cursorless_command_server.py b/cursorless-talon/src/cursorless_command_server.py
deleted file mode 100644
index 78d30ad78d..0000000000
--- a/cursorless-talon/src/cursorless_command_server.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from typing import Any
-
-from talon import Module, actions
-
-mod = Module()
-
-
-@mod.action_class
-class Actions:
- def private_cursorless_run_rpc_command_and_wait(
- command_id: str, # pyright: ignore [reportGeneralTypeIssues]
- arg1: Any = None,
- arg2: Any = None,
- ):
- """Execute command via rpc and wait for command to finish."""
- try:
- actions.user.run_rpc_command_and_wait(command_id, arg1, arg2)
- except KeyError:
- actions.user.vscode_with_plugin_and_wait(command_id, arg1, arg2)
-
- def private_cursorless_run_rpc_command_no_wait(
- command_id: str, # pyright: ignore [reportGeneralTypeIssues]
- arg1: Any = None,
- arg2: Any = None,
- ):
- """Execute command via rpc and DON'T wait."""
- try:
- actions.user.run_rpc_command(command_id, arg1, arg2)
- except KeyError:
- actions.user.vscode_with_plugin(command_id, arg1, arg2)
-
- def private_cursorless_run_rpc_command_get(
- command_id: str, # pyright: ignore [reportGeneralTypeIssues]
- arg1: Any = None,
- arg2: Any = None,
- ) -> Any:
- """Execute command via rpc and return command output."""
- try:
- return actions.user.run_rpc_command_get(command_id, arg1, arg2)
- except KeyError:
- return actions.user.vscode_get(command_id, arg1, arg2)
diff --git a/cursorless-talon/src/cursorless_global.talon b/cursorless-talon/src/cursorless_global.talon
deleted file mode 100644
index 97675e61ea..0000000000
--- a/cursorless-talon/src/cursorless_global.talon
+++ /dev/null
@@ -1,4 +0,0 @@
-{user.cursorless_homophone} (reference | ref | cheatsheet | cheat sheet):
- user.private_cursorless_cheat_sheet_show_html()
-{user.cursorless_homophone} (instructions | docks | help) | help {user.cursorless_homophone}:
- user.private_cursorless_open_instructions()
diff --git a/cursorless-talon/src/fallback.py b/cursorless-talon/src/fallback.py
deleted file mode 100644
index 55d86ab8bd..0000000000
--- a/cursorless-talon/src/fallback.py
+++ /dev/null
@@ -1,107 +0,0 @@
-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))
-
-
-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 "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}")
diff --git a/cursorless-talon/src/marks/decorated_mark.py b/cursorless-talon/src/marks/decorated_mark.py
deleted file mode 100644
index 3592d4f6cc..0000000000
--- a/cursorless-talon/src/marks/decorated_mark.py
+++ /dev/null
@@ -1,176 +0,0 @@
-from pathlib import Path
-from typing import Any
-
-from talon import Module, actions, cron, fs
-
-from ..csv_overrides import init_csv_and_watch_changes
-from .mark_types import DecoratedSymbol
-
-mod = Module()
-
-mod.list("cursorless_hat_color", desc="Supported hat colors for cursorless")
-mod.list("cursorless_hat_shape", desc="Supported hat shapes for cursorless")
-mod.list(
- "cursorless_unknown_symbol",
- "This list contains the term that is used to refer to any unknown symbol",
-)
-
-
-@mod.capture(rule=" | {user.cursorless_unknown_symbol}")
-def cursorless_grapheme(m) -> str:
- try:
- return m.any_alphanumeric_key
- except AttributeError:
- # NB: This represents unknown char in Unicode. It will be translated
- # to "[unk]" by Cursorless extension.
- return "\ufffd"
-
-
-@mod.capture(
- rule="[{user.cursorless_hat_color}] [{user.cursorless_hat_shape}] "
-)
-def cursorless_decorated_symbol(m) -> DecoratedSymbol:
- """A decorated symbol"""
- hat_color: str = getattr(m, "cursorless_hat_color", "default")
- try:
- hat_style_name = f"{hat_color}-{m.cursorless_hat_shape}"
- except AttributeError:
- hat_style_name = hat_color
- return {
- "type": "decoratedSymbol",
- "symbolColor": hat_style_name,
- "character": m.cursorless_grapheme,
- }
-
-
-DEFAULT_COLOR_ENABLEMENT = {
- "blue": True,
- "green": True,
- "red": True,
- "pink": True,
- "yellow": True,
- "userColor1": False,
- "userColor2": False,
-}
-
-DEFAULT_SHAPE_ENABLEMENT = {
- "ex": False,
- "fox": False,
- "wing": False,
- "hole": False,
- "frame": False,
- "curve": False,
- "eye": False,
- "play": False,
- "bolt": False,
- "crosshairs": False,
-}
-
-# Fall back to full enablement in case of error reading settings file
-# NB: This won't actually enable all the shapes and colors extension-side.
-# It'll just make it so that the user can say them whether or not they are enabled
-FALLBACK_SHAPE_ENABLEMENT = {
- "ex": True,
- "fox": True,
- "wing": True,
- "hole": True,
- "frame": True,
- "curve": True,
- "eye": True,
- "play": True,
- "bolt": True,
- "crosshairs": True,
-}
-FALLBACK_COLOR_ENABLEMENT = DEFAULT_COLOR_ENABLEMENT
-
-unsubscribe_hat_styles: Any = None
-
-
-def setup_hat_styles_csv(hat_colors: dict[str, str], hat_shapes: dict[str, str]):
- global unsubscribe_hat_styles
-
- (
- color_enablement_settings,
- is_color_error,
- ) = actions.user.vscode_get_setting_with_fallback(
- "cursorless.hatEnablement.colors",
- default_value={},
- fallback_value=FALLBACK_COLOR_ENABLEMENT,
- fallback_message="Error finding color enablement; falling back to full enablement",
- )
-
- (
- shape_enablement_settings,
- is_shape_error,
- ) = actions.user.vscode_get_setting_with_fallback(
- "cursorless.hatEnablement.shapes",
- default_value={},
- fallback_value=FALLBACK_SHAPE_ENABLEMENT,
- fallback_message="Error finding shape enablement; falling back to full enablement",
- )
-
- color_enablement = {
- **DEFAULT_COLOR_ENABLEMENT,
- **color_enablement_settings,
- }
- shape_enablement = {
- **DEFAULT_SHAPE_ENABLEMENT,
- **shape_enablement_settings,
- }
-
- active_hat_colors = {
- spoken_form: value
- for spoken_form, value in hat_colors.items()
- if color_enablement[value]
- }
- active_hat_shapes = {
- spoken_form: value
- for spoken_form, value in hat_shapes.items()
- if shape_enablement[value]
- }
-
- if unsubscribe_hat_styles is not None:
- unsubscribe_hat_styles()
-
- unsubscribe_hat_styles = init_csv_and_watch_changes(
- "hat_styles.csv",
- {
- "hat_color": active_hat_colors,
- "hat_shape": active_hat_shapes,
- },
- extra_ignored_values=[*hat_colors.values(), *hat_shapes.values()],
- no_update_file=is_shape_error or is_color_error,
- )
-
- if is_shape_error or is_color_error:
- actions.app.notify("Error reading vscode settings. Restart talon; see log")
-
-
-fast_reload_job = None
-slow_reload_job = None
-
-
-def init_hats(hat_colors: dict[str, str], hat_shapes: dict[str, str]):
- setup_hat_styles_csv(hat_colors, hat_shapes)
-
- vscode_settings_path: Path = actions.user.vscode_settings_path().resolve()
-
- def on_watch(path, flags):
- global fast_reload_job, slow_reload_job
- cron.cancel(fast_reload_job)
- cron.cancel(slow_reload_job)
- fast_reload_job = cron.after(
- "500ms", lambda: setup_hat_styles_csv(hat_colors, hat_shapes)
- )
- slow_reload_job = cron.after(
- "10s", lambda: setup_hat_styles_csv(hat_colors, hat_shapes)
- )
-
- fs.watch(str(vscode_settings_path), on_watch)
-
- def unsubscribe():
- fs.unwatch(str(vscode_settings_path), on_watch)
- if unsubscribe_hat_styles is not None:
- unsubscribe_hat_styles()
-
- return unsubscribe
diff --git a/cursorless-talon/src/marks/lines_number.py b/cursorless-talon/src/marks/lines_number.py
deleted file mode 100644
index a1ff66fe62..0000000000
--- a/cursorless-talon/src/marks/lines_number.py
+++ /dev/null
@@ -1,62 +0,0 @@
-from collections.abc import Callable
-from dataclasses import dataclass
-
-from talon import Module
-
-from ..targets.range_target import RangeConnective
-from .mark_types import LineNumber, LineNumberMark, LineNumberType
-
-mod = Module()
-
-mod.list("cursorless_line_direction", desc="Supported directions for line modifier")
-
-
-@dataclass
-class CustomizableTerm:
- cursorlessIdentifier: str
- type: LineNumberType
- formatter: Callable[[int], int]
-
-
-# NOTE: Please do not change these dicts. Use the CSVs for customization.
-# See https://www.cursorless.org/docs/user/customization/
-directions = [
- CustomizableTerm("lineNumberModulo100", "modulo100", lambda number: number - 1),
- CustomizableTerm("lineNumberRelativeUp", "relative", lambda number: -number),
- CustomizableTerm("lineNumberRelativeDown", "relative", lambda number: number),
-]
-
-directions_map = {d.cursorlessIdentifier: d for d in directions}
-
-
-@mod.capture(
- rule=(
- "{user.cursorless_line_direction} "
- "[ ]"
- )
-)
-def cursorless_line_number(m) -> LineNumber:
- direction = directions_map[m.cursorless_line_direction]
- numbers: list[int] = m.private_cursorless_number_small_list
- anchor = create_line_number_mark(direction.type, direction.formatter(numbers[0]))
- if len(numbers) > 1:
- active = create_line_number_mark(
- direction.type, direction.formatter(numbers[1])
- )
- range_connective: RangeConnective = m.cursorless_range_connective
- return {
- "type": "range",
- "anchor": anchor,
- "active": active,
- "excludeAnchor": range_connective.excludeAnchor,
- "excludeActive": range_connective.excludeActive,
- }
- return anchor
-
-
-def create_line_number_mark(type: LineNumberType, line_number: int) -> LineNumberMark:
- return {
- "type": "lineNumber",
- "lineNumberType": type,
- "lineNumber": line_number,
- }
diff --git a/cursorless-talon/src/marks/mark.py b/cursorless-talon/src/marks/mark.py
deleted file mode 100644
index 18e21abb75..0000000000
--- a/cursorless-talon/src/marks/mark.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from talon import Module
-
-from .mark_types import Mark
-
-mod = Module()
-
-
-@mod.capture(
- rule=(
- " | "
- " |"
- "" # row (ie absolute mod 100), up, down
- )
-)
-def cursorless_mark(m) -> Mark:
- return m[0]
diff --git a/cursorless-talon/src/marks/mark_types.py b/cursorless-talon/src/marks/mark_types.py
deleted file mode 100644
index 3985b7d4f5..0000000000
--- a/cursorless-talon/src/marks/mark_types.py
+++ /dev/null
@@ -1,31 +0,0 @@
-from typing import Literal, TypedDict, Union
-
-
-class DecoratedSymbol(TypedDict):
- type: Literal["decoratedSymbol"]
- symbolColor: str
- character: str
-
-
-SimpleMark = dict[Literal["type"], str]
-
-LineNumberType = Literal["modulo100", "relative"]
-
-
-class LineNumberMark(TypedDict):
- type: Literal["lineNumber"]
- lineNumberType: LineNumberType
- lineNumber: int
-
-
-class LineNumberRange(TypedDict):
- type: Literal["range"]
- anchor: LineNumberMark
- active: LineNumberMark
- excludeAnchor: bool
- excludeActive: bool
-
-
-LineNumber = Union[LineNumberMark, LineNumberRange]
-
-Mark = Union[DecoratedSymbol, SimpleMark, LineNumber]
diff --git a/cursorless-talon/src/marks/simple_mark.py b/cursorless-talon/src/marks/simple_mark.py
deleted file mode 100644
index a98d3d84c9..0000000000
--- a/cursorless-talon/src/marks/simple_mark.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from talon import Module
-
-from .mark_types import SimpleMark
-
-mod = Module()
-
-mod.list("cursorless_simple_mark", desc="Cursorless simple marks")
-
-# Maps from the id we use in the spoken form csv to the modifier type
-# expected by Cursorless extension
-simple_marks = {
- "currentSelection": "cursor",
- "previousTarget": "that",
- "previousSource": "source",
- "nothing": "nothing",
-}
-
-
-@mod.capture(rule="{user.cursorless_simple_mark}")
-def cursorless_simple_mark(m) -> SimpleMark:
- return {
- "type": simple_marks[m.cursorless_simple_mark],
- }
diff --git a/cursorless-talon/src/modifiers/glyph_scope.py b/cursorless-talon/src/modifiers/glyph_scope.py
deleted file mode 100644
index beff4c35ed..0000000000
--- a/cursorless-talon/src/modifiers/glyph_scope.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from talon import Module
-
-mod = Module()
-
-mod.list(
- "cursorless_glyph_scope_type",
- desc="Cursorless glyph scope type",
-)
-mod.list(
- "cursorless_glyph_scope_type_plural",
- desc="Plural version of Cursorless glyph scope type",
-)
-
-
-@mod.capture(rule="{user.cursorless_glyph_scope_type} ")
-def cursorless_glyph_scope_type(m) -> dict[str, str]:
- return {
- "type": "glyph",
- "character": m.any_alphanumeric_key,
- }
-
-
-@mod.capture(
- rule="{user.cursorless_glyph_scope_type_plural} "
-)
-def cursorless_glyph_scope_type_plural(m) -> dict[str, str]:
- return {
- "type": "glyph",
- "character": m.any_alphanumeric_key,
- }
diff --git a/cursorless-talon/src/modifiers/head_tail.py b/cursorless-talon/src/modifiers/head_tail.py
deleted file mode 100644
index c6aa5f061e..0000000000
--- a/cursorless-talon/src/modifiers/head_tail.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from talon import Module
-
-mod = Module()
-
-mod.list(
- "cursorless_head_tail_modifier",
- desc="Cursorless head and tail modifiers",
-)
-
-
-@mod.capture(
- rule=(
- "{user.cursorless_head_tail_modifier} "
- "[] "
- "[]"
- )
-)
-def cursorless_head_tail_modifier(m) -> dict[str, str]:
- """Cursorless head and tail modifier"""
- modifiers = []
-
- try:
- modifiers.append(m.cursorless_interior_modifier)
- except AttributeError:
- pass
-
- try:
- modifiers.append(m.cursorless_head_tail_swallowed_modifier)
- except AttributeError:
- pass
-
- result = {
- "type": m.cursorless_head_tail_modifier,
- }
-
- if modifiers:
- result["modifiers"] = modifiers
-
- return result
diff --git a/cursorless-talon/src/modifiers/interior.py b/cursorless-talon/src/modifiers/interior.py
deleted file mode 100644
index 0ee0845cbe..0000000000
--- a/cursorless-talon/src/modifiers/interior.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from talon import Module
-
-mod = Module()
-
-mod.list(
- "cursorless_interior_modifier",
- desc="Cursorless interior modifier",
-)
-
-
-@mod.capture(rule="{user.cursorless_interior_modifier}")
-def cursorless_interior_modifier(m) -> dict[str, str]:
- """Cursorless interior modifier"""
- return {
- "type": m.cursorless_interior_modifier,
- }
diff --git a/cursorless-talon/src/modifiers/matching_pair_symbol.py b/cursorless-talon/src/modifiers/matching_pair_symbol.py
deleted file mode 100644
index 6760e5663d..0000000000
--- a/cursorless-talon/src/modifiers/matching_pair_symbol.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from typing import Any
-
-from talon import Module
-
-mod = Module()
-
-
-@mod.capture(rule="matching")
-def cursorless_matching_paired_delimiter(m) -> dict[str, Any]:
- return {"modifier": {"type": "matchingPairedDelimiter"}}
diff --git a/cursorless-talon/src/modifiers/modifiers.py b/cursorless-talon/src/modifiers/modifiers.py
deleted file mode 100644
index d63351ab37..0000000000
--- a/cursorless-talon/src/modifiers/modifiers.py
+++ /dev/null
@@ -1,50 +0,0 @@
-from talon import Module
-
-mod = Module()
-
-mod.list(
- "cursorless_simple_modifier",
- desc="Simple cursorless modifiers that only need to specify their type",
-)
-
-
-@mod.capture(rule="{user.cursorless_simple_modifier}")
-def cursorless_simple_modifier(m) -> dict[str, str]:
- """Simple cursorless modifiers that only need to specify their type"""
- return {
- "type": m.cursorless_simple_modifier,
- }
-
-
-# These are the modifiers that will be "swallowed" by the head/tail modifier.
-# For example, saying "head funk" will result in a "head" modifier that will
-# select past the start of the function.
-# Note that we don't include "inside" here, because that requires slightly
-# special treatment to ensure that "head inside round" swallows "inside round"
-# rather than just "inside".
-head_tail_swallowed_modifiers = [
- "", # bounds, just, leading, trailing
- "", # funk, state, class, every funk
- "", # first past second word
- "", # next funk, 3 funks
- "", # matching/pair [curly, round]
-]
-
-modifiers = [
- "", # inside
- "", # head, tail
- "", # start of, end of
- *head_tail_swallowed_modifiers,
-]
-
-
-@mod.capture(rule="|".join(modifiers))
-def cursorless_modifier(m) -> str:
- """Cursorless modifier"""
- return m[0]
-
-
-@mod.capture(rule="|".join(head_tail_swallowed_modifiers))
-def cursorless_head_tail_swallowed_modifier(m) -> str:
- """Cursorless modifier that is swallowed by the head/tail modifier, excluding interior, which requires special treatment"""
- return m[0]
diff --git a/cursorless-talon/src/modifiers/ordinal_scope.py b/cursorless-talon/src/modifiers/ordinal_scope.py
deleted file mode 100644
index 0ff0ac3982..0000000000
--- a/cursorless-talon/src/modifiers/ordinal_scope.py
+++ /dev/null
@@ -1,91 +0,0 @@
-from typing import Any
-
-from talon import Module
-
-from ..targets.range_target import RangeConnective
-
-mod = Module()
-
-mod.list("cursorless_first_modifier", desc="Cursorless first modifiers")
-mod.list("cursorless_last_modifier", desc="Cursorless last modifiers")
-
-
-@mod.capture(
- rule=" | [] {user.cursorless_last_modifier}"
-)
-def ordinal_or_last(m) -> int:
- """An ordinal or the word 'last'"""
- if m[-1] == "last":
- return -getattr(m, "ordinals_small", 1)
- return m.ordinals_small - 1
-
-
-@mod.capture(
- rule=" [ ] "
-)
-def cursorless_ordinal_range(m) -> dict[str, Any]:
- """Ordinal range"""
- anchor = create_ordinal_scope_modifier(
- m.cursorless_scope_type, m.ordinal_or_last_list[0]
- )
- if len(m.ordinal_or_last_list) > 1:
- active = create_ordinal_scope_modifier(
- m.cursorless_scope_type, m.ordinal_or_last_list[1]
- )
- range_connective: RangeConnective = m.cursorless_range_connective
- return {
- "type": "range",
- "anchor": anchor,
- "active": active,
- "excludeAnchor": range_connective.excludeAnchor,
- "excludeActive": range_connective.excludeActive,
- }
- return anchor
-
-
-@mod.capture(
- rule=(
- "[{user.cursorless_every_scope_modifier}] "
- "({user.cursorless_first_modifier} | {user.cursorless_last_modifier}) "
- " "
- ),
-)
-def cursorless_first_last(m) -> dict[str, Any]:
- """First/last `n` scopes; eg "first three funks"""
- is_every = hasattr(m, "cursorless_every_scope_modifier")
- if hasattr(m, "cursorless_first_modifier"):
- return create_ordinal_scope_modifier(
- m.cursorless_scope_type_plural,
- 0,
- m.private_cursorless_number_small,
- is_every,
- )
- return create_ordinal_scope_modifier(
- m.cursorless_scope_type_plural,
- -m.private_cursorless_number_small,
- m.private_cursorless_number_small,
- is_every,
- )
-
-
-@mod.capture(rule=" | ")
-def cursorless_ordinal_scope(m) -> dict[str, Any]:
- """Ordinal ranges such as subwords or characters"""
- return m[0]
-
-
-def create_ordinal_scope_modifier(
- scope_type: dict,
- start: int,
- length: int = 1,
- is_every: bool = False,
-):
- res = {
- "type": "ordinalScope",
- "scopeType": scope_type,
- "start": start,
- "length": length,
- }
- if is_every:
- res["isEvery"] = True
- return res
diff --git a/cursorless-talon/src/modifiers/position.py b/cursorless-talon/src/modifiers/position.py
deleted file mode 100644
index 800ca1f25b..0000000000
--- a/cursorless-talon/src/modifiers/position.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import Any
-
-from talon import Module
-
-mod = Module()
-
-mod.list("cursorless_position", desc='Positions such as "before", "after" etc')
-
-
-@mod.capture(rule="{user.cursorless_position}")
-def cursorless_position_modifier(m) -> dict[str, Any]:
- return {"type": "startOf" if m.cursorless_position == "start" else "endOf"}
diff --git a/cursorless-talon/src/modifiers/relative_scope.py b/cursorless-talon/src/modifiers/relative_scope.py
deleted file mode 100644
index 1fd60ac693..0000000000
--- a/cursorless-talon/src/modifiers/relative_scope.py
+++ /dev/null
@@ -1,104 +0,0 @@
-from typing import Any
-
-from talon import Module
-
-mod = Module()
-
-mod.list("cursorless_previous_next_modifier", desc="Cursorless previous/next modifiers")
-mod.list(
- "cursorless_forward_backward_modifier", desc="Cursorless forward/backward modifiers"
-)
-
-
-@mod.capture(rule="{user.cursorless_previous_next_modifier}")
-def cursorless_relative_direction(m) -> str:
- """Previous/next"""
- return "backward" if m[0] == "previous" else "forward"
-
-
-@mod.capture(
- rule="[] "
-)
-def cursorless_relative_scope_singular(m) -> dict[str, Any]:
- """Relative previous/next singular scope, eg `"next funk"` or `"third next funk"`."""
- return create_relative_scope_modifier(
- m.cursorless_scope_type,
- getattr(m, "ordinals_small", 1),
- 1,
- m.cursorless_relative_direction,
- False,
- )
-
-
-@mod.capture(
- rule="[{user.cursorless_every_scope_modifier}] "
-)
-def cursorless_relative_scope_plural(m) -> dict[str, Any]:
- """Relative previous/next plural scope. `next three funks`"""
- return create_relative_scope_modifier(
- m.cursorless_scope_type_plural,
- 1,
- m.private_cursorless_number_small,
- m.cursorless_relative_direction,
- hasattr(m, "cursorless_every_scope_modifier"),
- )
-
-
-@mod.capture(
- rule="[{user.cursorless_every_scope_modifier}] [{user.cursorless_forward_backward_modifier}]"
-)
-def cursorless_relative_scope_count(m) -> dict[str, Any]:
- """Relative count scope. `three funks`"""
- return create_relative_scope_modifier(
- m.cursorless_scope_type_plural,
- 0,
- m.private_cursorless_number_small,
- getattr(m, "cursorless_forward_backward_modifier", "forward"),
- hasattr(m, "cursorless_every_scope_modifier"),
- )
-
-
-@mod.capture(
- rule=" {user.cursorless_forward_backward_modifier}"
-)
-def cursorless_relative_scope_one_backward(m) -> dict[str, Any]:
- """Take scope backward, eg `funk backward`"""
- return create_relative_scope_modifier(
- m.cursorless_scope_type,
- 0,
- 1,
- m.cursorless_forward_backward_modifier,
- False,
- )
-
-
-@mod.capture(
- rule=(
- " | "
- " | "
- " | "
- ""
- )
-)
-def cursorless_relative_scope(m) -> dict[str, Any]:
- """Previous/next scope"""
- return m[0]
-
-
-def create_relative_scope_modifier(
- scope_type: dict,
- offset: int,
- length: int,
- direction: str,
- is_every: bool,
-) -> dict[str, Any]:
- res = {
- "type": "relativeScope",
- "scopeType": scope_type,
- "offset": offset,
- "length": length,
- "direction": direction,
- }
- if is_every:
- res["isEvery"] = True
- return res
diff --git a/cursorless-talon/src/modifiers/scopes.py b/cursorless-talon/src/modifiers/scopes.py
deleted file mode 100644
index ee1efb9b2c..0000000000
--- a/cursorless-talon/src/modifiers/scopes.py
+++ /dev/null
@@ -1,53 +0,0 @@
-from talon import Module
-
-mod = Module()
-
-mod.list("cursorless_scope_type", desc="Supported scope types")
-mod.list("cursorless_scope_type_plural", desc="Supported plural scope types")
-mod.list(
- "cursorless_custom_regex_scope_type",
- desc="Supported custom regular expression scope types",
-)
-mod.list(
- "cursorless_custom_regex_scope_type_plural",
- desc="Supported plural custom regular expression scope types",
-)
-
-
-@mod.capture(
- rule="{user.cursorless_scope_type} | | {user.cursorless_custom_regex_scope_type}"
-)
-def cursorless_scope_type(m) -> dict[str, str]:
- """Cursorless scope type singular"""
- try:
- return {"type": m.cursorless_scope_type}
- except AttributeError:
- pass
-
- try:
- return m.cursorless_glyph_scope_type
- except AttributeError:
- pass
-
- return {"type": "customRegex", "regex": m.cursorless_custom_regex_scope_type}
-
-
-@mod.capture(
- rule="{user.cursorless_scope_type_plural} | | {user.cursorless_custom_regex_scope_type_plural}"
-)
-def cursorless_scope_type_plural(m) -> dict[str, str]:
- """Cursorless scope type plural"""
- try:
- return {"type": m.cursorless_scope_type_plural}
- except AttributeError:
- pass
-
- try:
- return m.cursorless_glyph_scope_type_plural
- except AttributeError:
- pass
-
- return {
- "type": "customRegex",
- "regex": m.cursorless_custom_regex_scope_type_plural,
- }
diff --git a/cursorless-talon/src/modifiers/simple_scope_modifier.py b/cursorless-talon/src/modifiers/simple_scope_modifier.py
deleted file mode 100644
index cb0d2d4868..0000000000
--- a/cursorless-talon/src/modifiers/simple_scope_modifier.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from typing import Any
-
-from talon import Module
-
-mod = Module()
-
-mod.list(
- "cursorless_every_scope_modifier",
- desc="Cursorless every scope modifiers",
-)
-mod.list(
- "cursorless_ancestor_scope_modifier",
- desc="Cursorless ancestor scope modifiers",
-)
-
-
-@mod.capture(
- rule=(
- "[{user.cursorless_every_scope_modifier} | {user.cursorless_ancestor_scope_modifier}] "
- ""
- ),
-)
-def cursorless_simple_scope_modifier(m) -> dict[str, Any]:
- """Containing scope, every scope, etc"""
- if hasattr(m, "cursorless_every_scope_modifier"):
- return {
- "type": "everyScope",
- "scopeType": m.cursorless_scope_type,
- }
-
- if hasattr(m, "cursorless_ancestor_scope_modifier"):
- return {
- "type": "containingScope",
- "scopeType": m.cursorless_scope_type,
- "ancestorIndex": 1,
- }
-
- return {
- "type": "containingScope",
- "scopeType": m.cursorless_scope_type,
- }
diff --git a/cursorless-talon/src/modifiers/surrounding_pair.py b/cursorless-talon/src/modifiers/surrounding_pair.py
deleted file mode 100644
index 72e1cbddf0..0000000000
--- a/cursorless-talon/src/modifiers/surrounding_pair.py
+++ /dev/null
@@ -1,62 +0,0 @@
-from contextlib import suppress
-from typing import Any
-
-from talon import Context, Module
-
-mod = Module()
-ctx = Context()
-
-
-mod.list(
- "cursorless_delimiter_force_direction",
- desc="Can be used to force an ambiguous delimiter to extend in one direction",
-)
-# FIXME: Remove type ignore once Talon supports list types
-# See https://github.com/talonvoice/talon/issues/654
-ctx.lists["user.cursorless_delimiter_force_direction"] = [ # pyright: ignore [reportArgumentType]
- "left",
- "right",
-]
-
-mod.list(
- "cursorless_surrounding_pair_scope_type",
- desc="Scope types that can function as surrounding pairs",
-)
-
-
-@mod.capture(
- rule=(
- " |"
- "{user.cursorless_surrounding_pair_scope_type}"
- )
-)
-def cursorless_surrounding_pair_scope_type(m) -> str:
- """Surrounding pair scope type"""
- try:
- return m.cursorless_surrounding_pair_scope_type
- except AttributeError:
- return m.cursorless_selectable_paired_delimiter
-
-
-@mod.capture(
- rule="[{user.cursorless_delimiter_force_direction}] "
-)
-def cursorless_surrounding_pair(m) -> dict[str, Any]:
- """Expand to containing surrounding pair"""
- try:
- surrounding_pair_scope_type = m.cursorless_surrounding_pair_scope_type
- except AttributeError:
- surrounding_pair_scope_type = "any"
-
- scope_type = {
- "type": "surroundingPair",
- "delimiter": surrounding_pair_scope_type,
- }
-
- with suppress(AttributeError):
- scope_type["forceDirection"] = m.cursorless_delimiter_force_direction
-
- return {
- "type": "containingScope",
- "scopeType": scope_type,
- }
diff --git a/cursorless-talon/src/number_small.py b/cursorless-talon/src/number_small.py
deleted file mode 100644
index fd800ae232..0000000000
--- a/cursorless-talon/src/number_small.py
+++ /dev/null
@@ -1,46 +0,0 @@
-"""
-This file allows us to use a custom `number_small` capture. See #1021 for more
-info.
-"""
-
-from talon import Context, Module
-
-mod = Module()
-mod.tag(
- "cursorless_custom_number_small",
- "This tag causes Cursorless to use the global capture",
-)
-
-ctx = Context()
-ctx.matches = """
-not tag: user.cursorless_custom_number_small
-"""
-
-
-@mod.capture(rule="")
-def private_cursorless_number_small(m) -> int:
- return m.number_small
-
-
-digit_list = "zero one two three four five six seven eight nine".split()
-teens = "ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen".split()
-tens = "twenty thirty forty fifty sixty seventy eighty ninety".split()
-
-number_small_list = [*digit_list, *teens]
-for ten in tens:
- number_small_list.append(ten)
- number_small_list.extend(f"{ten} {digit}" for digit in digit_list[1:])
-number_small_map = {n: i for i, n in enumerate(number_small_list)}
-
-mod.list("private_cursorless_number_small", desc="List of small numbers")
-# FIXME: Remove type ignore once Talon supports list types
-# See https://github.com/talonvoice/talon/issues/654
-ctx.lists["self.private_cursorless_number_small"] = number_small_map.keys() # pyright: ignore [reportArgumentType]
-
-
-@ctx.capture(
- "user.private_cursorless_number_small",
- rule="{user.private_cursorless_number_small}",
-)
-def override_private_cursorless_number_small(m) -> int:
- return number_small_map[m.private_cursorless_number_small]
diff --git a/cursorless-talon/src/paired_delimiter.py b/cursorless-talon/src/paired_delimiter.py
deleted file mode 100644
index 40126707f1..0000000000
--- a/cursorless-talon/src/paired_delimiter.py
+++ /dev/null
@@ -1,61 +0,0 @@
-from talon import Module
-
-mod = Module()
-
-mod.list(
- "cursorless_wrapper_only_paired_delimiter",
- desc="A paired delimiter that can only be used as a wrapper",
-)
-mod.list(
- "cursorless_selectable_only_paired_delimiter",
- desc="A paired delimiter that can only be used as a scope type",
-)
-mod.list(
- "cursorless_wrapper_selectable_paired_delimiter",
- desc="A paired delimiter that can be used as a scope type and as a wrapper",
-)
-
-# Maps from the id we use in the spoken form csv to the delimiter strings
-paired_delimiters = {
- "curlyBrackets": ["{", "}"],
- "angleBrackets": ["<", ">"],
- "escapedDoubleQuotes": ['\\"', '\\"'],
- "escapedSingleQuotes": ["\\'", "\\'"],
- "escapedParentheses": ["\\(", "\\)"],
- "escapedSquareBrackets": ["\\[", "\\]"],
- "doubleQuotes": ['"', '"'],
- "parentheses": ["(", ")"],
- "backtickQuotes": ["`", "`"],
- "whitespace": [" ", " "],
- "squareBrackets": ["[", "]"],
- "singleQuotes": ["'", "'"],
- "any": ["", ""],
- "dollarSignAndCurlyBrackets": ["${", "}"],
-}
-
-
-@mod.capture(
- rule=(
- "{user.cursorless_wrapper_only_paired_delimiter} |"
- "{user.cursorless_wrapper_selectable_paired_delimiter}"
- )
-)
-def cursorless_wrapper_paired_delimiter(m) -> list[str]:
- try:
- id = m.cursorless_wrapper_only_paired_delimiter
- except AttributeError:
- id = m.cursorless_wrapper_selectable_paired_delimiter
- return paired_delimiters[id]
-
-
-@mod.capture(
- rule=(
- "{user.cursorless_selectable_only_paired_delimiter} |"
- "{user.cursorless_wrapper_selectable_paired_delimiter}"
- )
-)
-def cursorless_selectable_paired_delimiter(m) -> str:
- try:
- return m.cursorless_selectable_only_paired_delimiter
- except AttributeError:
- return m.cursorless_wrapper_selectable_paired_delimiter
diff --git a/cursorless-talon/src/private_api/extract_decorated_marks.py b/cursorless-talon/src/private_api/extract_decorated_marks.py
deleted file mode 100644
index 230e5d18c2..0000000000
--- a/cursorless-talon/src/private_api/extract_decorated_marks.py
+++ /dev/null
@@ -1,47 +0,0 @@
-from typing import Any
-
-from ..actions.bring_move import BringMoveTargets
-from ..actions.swap import SwapTargets
-from ..targets.target_types import (
- ImplicitDestination,
- ImplicitTarget,
- ListDestination,
- ListTarget,
- PrimitiveDestination,
- PrimitiveTarget,
- RangeTarget,
-)
-
-
-def extract_decorated_marks(capture: Any) -> list[Any]:
- match capture:
- case PrimitiveTarget(mark=mark):
- if mark is None or mark["type"] != "decoratedSymbol":
- return []
- return [mark]
- case ImplicitTarget():
- return []
- case RangeTarget(anchor=anchor, active=active):
- return extract_decorated_marks(anchor) + extract_decorated_marks(active)
- case ListTarget(elements=elements):
- return [
- mark for target in elements for mark in extract_decorated_marks(target)
- ]
- case PrimitiveDestination(target=target):
- return extract_decorated_marks(target)
- case ImplicitDestination():
- return []
- case ListDestination(destinations=destinations):
- return [
- mark
- for destination in destinations
- for mark in extract_decorated_marks(destination)
- ]
- case BringMoveTargets(source=source, destination=destination):
- return extract_decorated_marks(source) + extract_decorated_marks(
- destination
- )
- case SwapTargets(target1=target1, target2=target2):
- return extract_decorated_marks(target1) + extract_decorated_marks(target2)
- case _:
- raise TypeError(f"Unknown capture type: {type(capture)}")
diff --git a/cursorless-talon/src/private_api/private_api.py b/cursorless-talon/src/private_api/private_api.py
deleted file mode 100644
index 16665942c2..0000000000
--- a/cursorless-talon/src/private_api/private_api.py
+++ /dev/null
@@ -1,68 +0,0 @@
-from typing import Any, Optional, Union
-
-from talon import Module, actions
-
-from ..targets.target_types import (
- CursorlessTarget,
- ListTarget,
- PrimitiveTarget,
- RangeTarget,
-)
-from .extract_decorated_marks import extract_decorated_marks
-
-mod = Module()
-
-
-@mod.action_class
-class MiscActions:
- def cursorless_private_extract_decorated_marks(capture: Any) -> list[dict]:
- """Cursorless private api: Extract all decorated marks from a Talon capture"""
- return extract_decorated_marks(capture)
-
-
-@mod.action_class
-class TargetBuilderActions:
- """Cursorless private api low-level target builder actions"""
-
- def cursorless_private_build_primitive_target(
- modifiers: list[dict], # pyright: ignore [reportGeneralTypeIssues]
- mark: Optional[dict],
- ) -> PrimitiveTarget:
- """Cursorless private api low-level target builder: Create a primitive target"""
- return PrimitiveTarget(mark, modifiers)
-
- def cursorless_private_build_list_target(
- elements: list[Union[PrimitiveTarget, RangeTarget]], # pyright: ignore [reportGeneralTypeIssues]
- ) -> Union[PrimitiveTarget, RangeTarget, ListTarget]:
- """Cursorless private api low-level target builder: Create a list target"""
- if len(elements) == 1:
- return elements[0]
-
- return ListTarget(elements)
-
-
-@mod.action_class
-class TargetActions:
- def cursorless_private_target_nothing() -> PrimitiveTarget:
- """Cursorless private api: Creates the "nothing" target"""
- return PrimitiveTarget({"type": "nothing"}, [])
-
-
-@mod.action_class
-class ActionActions:
- def cursorless_private_action_highlight(
- target: CursorlessTarget, # pyright: ignore [reportGeneralTypeIssues]
- highlightId: Optional[str] = None,
- ) -> None:
- """Cursorless private api: Highlights a target"""
- payload = {
- "name": "highlight",
- "target": target,
- }
-
- if highlightId is not None:
- payload["highlightId"] = highlightId
-
- actions.user.private_cursorless_command_and_wait(
- payload,
- )
diff --git a/cursorless-talon/src/scope_visualizer.py b/cursorless-talon/src/scope_visualizer.py
deleted file mode 100644
index 423796bacf..0000000000
--- a/cursorless-talon/src/scope_visualizer.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from talon import Module, actions
-
-mod = Module()
-mod.list("cursorless_show_scope_visualizer", desc="Show scope visualizer")
-mod.list("cursorless_hide_scope_visualizer", desc="Hide scope visualizer")
-mod.list(
- "cursorless_visualization_type",
- desc='Cursorless visualization type, e.g. "removal" or "iteration"',
-)
-
-
-@mod.action_class
-class Actions:
- def private_cursorless_show_scope_visualizer(
- scope_type: dict, # pyright: ignore [reportGeneralTypeIssues]
- visualization_type: str,
- ):
- """Shows scope visualizer"""
- actions.user.private_cursorless_run_rpc_command_no_wait(
- "cursorless.showScopeVisualizer", scope_type, visualization_type
- )
-
- def private_cursorless_hide_scope_visualizer():
- """Hides scope visualizer"""
- actions.user.private_cursorless_run_rpc_command_no_wait(
- "cursorless.hideScopeVisualizer"
- )
diff --git a/cursorless-talon/src/snippet_cursorless.talon b/cursorless-talon/src/snippet_cursorless.talon
deleted file mode 100644
index ffc28e20b4..0000000000
--- a/cursorless-talon/src/snippet_cursorless.talon
+++ /dev/null
@@ -1,14 +0,0 @@
-mode: command
-mode: user.cursorless_spoken_form_test
-tag: user.cursorless
-and not tag: user.cursorless_use_community_snippets
--
-
-{user.cursorless_insert_snippet_action} :
- user.private_cursorless_insert_snippet(cursorless_insertion_snippet)
-
-{user.cursorless_insert_snippet_action} {user.cursorless_insertion_snippet_single_phrase} [{user.cursorless_phrase_terminator}]:
- user.private_cursorless_insert_snippet_with_phrase(cursorless_insertion_snippet_single_phrase, text)
-
-{user.cursorless_wrapper_snippet} {user.cursorless_wrap_action} :
- user.private_cursorless_wrap_with_snippet(cursorless_wrap_action, cursorless_target, cursorless_wrapper_snippet)
diff --git a/cursorless-talon/src/snippets.py b/cursorless-talon/src/snippets.py
deleted file mode 100644
index 1a28fb1df8..0000000000
--- a/cursorless-talon/src/snippets.py
+++ /dev/null
@@ -1,224 +0,0 @@
-from dataclasses import dataclass
-from typing import Any, Optional, Union
-
-from talon import Module, actions
-
-from .targets.target_types import (
- CursorlessDestination,
- CursorlessTarget,
- ImplicitDestination,
-)
-
-
-@dataclass
-class InsertionSnippet:
- name: str
- destination: CursorlessDestination
-
-
-@dataclass
-class CommunityInsertionSnippet:
- body: str
- scopes: list[str] | None = None
-
-
-@dataclass
-class CommunityWrapperSnippet:
- body: str
- variable_name: str
- scope: str | None = None
-
-
-mod = Module()
-
-mod.list("cursorless_insert_snippet_action", desc="Cursorless insert snippet action")
-
-# Deprecated tag; we should probably remove this and notify users that they
-# should get rid of it, but I don't think it's worth the effort right now
-mod.tag(
- "cursorless_experimental_snippets",
- desc="tag for enabling experimental snippet support",
-)
-
-mod.tag(
- "cursorless_use_community_snippets",
- "If active use community snippets instead of Cursorless snippets",
-)
-
-mod.list("cursorless_wrapper_snippet", desc="Cursorless wrapper snippet")
-mod.list(
- "cursorless_insertion_snippet_no_phrase",
- desc="Cursorless insertion snippets that don't accept a phrase",
-)
-mod.list(
- "cursorless_insertion_snippet_single_phrase",
- desc="Cursorless insertion snippet that can accept a single phrase",
-)
-mod.list("cursorless_phrase_terminator", "Contains term used to terminate a phrase")
-
-
-@mod.capture(
- rule="({user.cursorless_insertion_snippet_no_phrase} | {user.cursorless_insertion_snippet_single_phrase}) []"
-)
-def cursorless_insertion_snippet(m) -> InsertionSnippet:
- try:
- name = m.cursorless_insertion_snippet_no_phrase
- except AttributeError:
- name = m.cursorless_insertion_snippet_single_phrase.split(".")[0]
-
- try:
- destination = m.cursorless_destination
- except AttributeError:
- destination = ImplicitDestination()
-
- return InsertionSnippet(name, destination)
-
-
-def wrap_with_snippet(snippet_description: dict, target: CursorlessTarget):
- actions.user.private_cursorless_command_and_wait(
- {
- "name": "wrapWithSnippet",
- "snippetDescription": snippet_description,
- "target": target,
- },
- )
-
-
-def insert_snippet(snippet_description: dict, destination: CursorlessDestination):
- actions.user.private_cursorless_command_and_wait(
- {
- "name": "insertSnippet",
- "snippetDescription": snippet_description,
- "destination": destination,
- },
- )
-
-
-def insert_named_snippet(
- name: str,
- destination: CursorlessDestination,
- substitutions: Optional[dict] = None,
-):
- snippet: dict = {
- "type": "named",
- "name": name,
- }
- if substitutions is not None:
- snippet["substitutions"] = substitutions
- insert_snippet(snippet, destination)
-
-
-def insert_custom_snippet(
- body: str,
- destination: CursorlessDestination,
- scope_types: Optional[list[dict]] = None,
-):
- snippet: dict = {
- "type": "custom",
- "body": body,
- }
-
- if scope_types:
- snippet["scopeTypes"] = scope_types
-
- insert_snippet(snippet, destination)
-
-
-@mod.action_class
-class Actions:
- def private_cursorless_insert_snippet(insertion_snippet: InsertionSnippet): # pyright: ignore [reportGeneralTypeIssues]
- """Execute Cursorless insert snippet action"""
- insert_named_snippet(
- insertion_snippet.name,
- insertion_snippet.destination,
- )
-
- def private_cursorless_insert_snippet_with_phrase(
- snippet_description: str, # pyright: ignore [reportGeneralTypeIssues]
- text: str,
- ):
- """Cursorless: Insert snippet with phrase """
- snippet_name, snippet_variable = snippet_description.split(".")
- insert_named_snippet(
- snippet_name,
- ImplicitDestination(),
- {snippet_variable: text},
- )
-
- def cursorless_insert_snippet_by_name(name: str): # pyright: ignore [reportGeneralTypeIssues]
- """Cursorless: Insert named snippet """
- insert_named_snippet(
- name,
- ImplicitDestination(),
- )
-
- def cursorless_insert_snippet(
- body: str, # pyright: ignore [reportGeneralTypeIssues]
- destination: CursorlessDestination = ImplicitDestination(),
- scope_type: Optional[Union[str, list[str]]] = None,
- ):
- """Cursorless: Insert custom snippet """
- if isinstance(scope_type, str):
- scope_type = [scope_type]
-
- if scope_type is not None:
- scope_types = [{"type": st} for st in scope_type]
- else:
- scope_types = None
-
- insert_custom_snippet(body, destination, scope_types)
-
- def cursorless_wrap_with_snippet_by_name(
- name: str, # pyright: ignore [reportGeneralTypeIssues]
- variable_name: str,
- target: CursorlessTarget,
- ):
- """Cursorless: Wrap target with a named snippet """
- wrap_with_snippet(
- {
- "type": "named",
- "name": name,
- "variableName": variable_name,
- },
- target,
- )
-
- def cursorless_wrap_with_snippet(
- body: str, # pyright: ignore [reportGeneralTypeIssues]
- target: CursorlessTarget,
- variable_name: Optional[str] = None,
- scope: Optional[str] = None,
- ):
- """Cursorless: Wrap target with custom snippet """
- snippet_arg: dict[str, Any] = {
- "type": "custom",
- "body": body,
- }
- if scope is not None:
- snippet_arg["scopeType"] = {"type": scope}
- if variable_name is not None:
- snippet_arg["variableName"] = variable_name
- wrap_with_snippet(
- snippet_arg,
- target,
- )
-
- def private_cursorless_insert_community_snippet(
- name: str, # pyright: ignore [reportGeneralTypeIssues]
- destination: CursorlessDestination,
- ):
- """Cursorless: Insert community snippet """
- snippet: CommunityInsertionSnippet = actions.user.get_insertion_snippet(name)
- actions.user.cursorless_insert_snippet(
- snippet.body, destination, snippet.scopes
- )
-
- def private_cursorless_wrap_with_community_snippet(
- name: str, # pyright: ignore [reportGeneralTypeIssues]
- target: CursorlessTarget,
- ):
- """Cursorless: Wrap target with community snippet """
- snippet: CommunityWrapperSnippet = actions.user.get_wrapper_snippet(name)
- actions.user.cursorless_wrap_with_snippet(
- snippet.body, target, snippet.variable_name, snippet.scope
- )
diff --git a/cursorless-talon/src/snippets_community.talon b/cursorless-talon/src/snippets_community.talon
deleted file mode 100644
index f94e491f83..0000000000
--- a/cursorless-talon/src/snippets_community.talon
+++ /dev/null
@@ -1,13 +0,0 @@
-mode: command
-mode: user.cursorless_spoken_form_test
-tag: user.cursorless
-and tag: user.cursorless_use_community_snippets
--
-
-# These snippets are defined in community
-
-{user.cursorless_insert_snippet_action} {user.snippet} :
- user.private_cursorless_insert_community_snippet(snippet, cursorless_destination)
-
-{user.snippet_wrapper} {user.cursorless_wrap_action} :
- user.private_cursorless_wrap_with_community_snippet(snippet_wrapper, cursorless_target)
diff --git a/cursorless-talon/src/spoken_forms.json b/cursorless-talon/src/spoken_forms.json
deleted file mode 100644
index 4322a3e6ff..0000000000
--- a/cursorless-talon/src/spoken_forms.json
+++ /dev/null
@@ -1,277 +0,0 @@
-{
- "NOTE FOR USERS": "Please don't edit this json file; see https://www.cursorless.org/docs/user/customization",
- "actions.csv": {
- "simple_action": {
- "bottom": "scrollToBottom",
- "break": "breakLine",
- "break point": "toggleLineBreakpoint",
- "carve": "cutToClipboard",
- "center": "scrollToCenter",
- "change": "clearAndSetSelection",
- "chuck": "remove",
- "clone up": "insertCopyBefore",
- "clone": "insertCopyAfter",
- "comment": "toggleLineComment",
- "copy": "copyToClipboard",
- "crown": "scrollToTop",
- "decrement": "decrement",
- "dedent": "outdentLine",
- "define": "revealDefinition",
- "drink": "editNewLineBefore",
- "drop": "insertEmptyLineBefore",
- "extract": "extractVariable",
- "float": "insertEmptyLineAfter",
- "fold": "foldRegion",
- "follow": "followLink",
- "give": "deselect",
- "highlight": "highlight",
- "hover": "showHover",
- "increment": "increment",
- "indent": "indentLine",
- "inspect": "showDebugHover",
- "join": "joinLines",
- "post": "setSelectionAfter",
- "pour": "editNewLineAfter",
- "pre": "setSelectionBefore",
- "puff": "insertEmptyLinesAround",
- "quick fix": "showQuickFix",
- "reference": "showReferences",
- "rename": "rename",
- "reverse": "reverseTargets",
- "scout": "findInDocument",
- "scout all": "findInWorkspace",
- "shuffle": "randomizeTargets",
- "snippet make": "generateSnippet",
- "sort": "sortTargets",
- "take": "setSelection",
- "type deaf": "revealTypeDefinition",
- "unfold": "unfoldRegion"
- },
- "callback_action": {
- "phones": "nextHomophone"
- },
- "paste_action": { "paste": "pasteFromClipboard" },
- "bring_move_action": {
- "bring": "replaceWithTarget",
- "move": "moveToTarget"
- },
- "swap_action": { "swap": "swapTargets" },
- "wrap_action": { "wrap": "wrapWithPairedDelimiter", "repack": "rewrap" },
- "insert_snippet_action": { "snippet": "insertSnippet" },
- "reformat_action": { "format": "applyFormatter" },
- "call_action": { "call": "callAsFunction" }
- },
- "target_connectives.csv": {
- "range_connective": {
- "between": "rangeExclusive",
- "past": "rangeInclusive",
- "-": "rangeExcludingStart",
- "until": "rangeExcludingEnd"
- },
- "list_connective": { "and": "listConnective" },
- "swap_connective": { "with": "swapConnective" },
- "insertion_mode_to": { "to": "sourceDestinationConnective" }
- },
- "modifiers.csv": {
- "simple_modifier": {
- "bounds": "excludeInterior",
- "just": "toRawSelection",
- "leading": "leading",
- "trailing": "trailing",
- "content": "keepContentFilter",
- "empty": "keepEmptyFilter",
- "its": "inferPreviousMark",
- "visible": "visible"
- },
- "every_scope_modifier": { "every": "every" },
- "ancestor_scope_modifier": { "grand": "ancestor" },
- "interior_modifier": {
- "inside": "interiorOnly"
- },
- "head_tail_modifier": {
- "head": "extendThroughStartOf",
- "tail": "extendThroughEndOf"
- },
- "range_type": {
- "slice": "verticalRange"
- },
- "first_modifier": { "first": "first" },
- "last_modifier": { "last": "last" },
- "previous_next_modifier": { "previous": "previous", "next": "next" },
- "forward_backward_modifier": {
- "forward": "forward",
- "backward": "backward"
- }
- },
- "positions.csv": {
- "position": {
- "start of": "start",
- "end of": "end"
- },
- "insertion_mode_before_after": {
- "before": "before",
- "after": "after"
- }
- },
- "modifier_scope_types.csv": {
- "scope_type": {
- "arg": "argumentOrParameter",
- "attribute": "attribute",
- "call": "functionCall",
- "callee": "functionCallee",
- "class name": "className",
- "class": "class",
- "comment": "comment",
- "funk name": "functionName",
- "funk": "namedFunction",
- "if state": "ifStatement",
- "instance": "instance",
- "item": "collectionItem",
- "key": "collectionKey",
- "lambda": "anonymousFunction",
- "list": "list",
- "map": "map",
- "name": "name",
- "regex": "regularExpression",
- "section": "section",
- "-one section": "sectionLevelOne",
- "-two section": "sectionLevelTwo",
- "-three section": "sectionLevelThree",
- "-four section": "sectionLevelFour",
- "-five section": "sectionLevelFive",
- "-six section": "sectionLevelSix",
- "selector": "selector",
- "state": "statement",
- "branch": "branch",
- "type": "type",
- "value": "value",
- "condition": "condition",
- "unit": "unit",
- "element": "xmlElement",
- "tags": "xmlBothTags",
- "start tag": "xmlStartTag",
- "end tag": "xmlEndTag",
- "part": "part",
- "chapter": "chapter",
- "subsection": "subSection",
- "subsubsection": "subSubSection",
- "paragraph": "namedParagraph",
- "subparagraph": "subParagraph",
- "environment": "environment",
- "command": "command",
- "char": "character",
- "sub": "word",
- "token": "token",
- "identifier": "identifier",
- "line": "line",
- "sentence": "sentence",
- "block": "paragraph",
- "file": "document",
- "paint": "nonWhitespaceSequence",
- "short paint": "boundedNonWhitespaceSequence",
- "link": "url",
- "cell": "notebookCell"
- },
- "surrounding_pair_scope_type": {
- "string": "string"
- },
- "glyph_scope_type": {
- "glyph": "glyph"
- }
- },
- "paired_delimiters.csv": {
- "selectable_only_paired_delimiter": { "pair": "any" },
- "wrapper_only_paired_delimiter": { "void": "whitespace" },
- "wrapper_selectable_paired_delimiter": {
- "curly": "curlyBrackets",
- "diamond": "angleBrackets",
- "escaped quad": "escapedDoubleQuotes",
- "escaped twin": "escapedSingleQuotes",
- "escaped round": "escapedParentheses",
- "escaped box": "escapedSquareBrackets",
- "quad": "doubleQuotes",
- "round": "parentheses",
- "skis": "backtickQuotes",
- "box": "squareBrackets",
- "twin": "singleQuotes"
- }
- },
- "special_marks.csv": {
- "simple_mark": {
- "this": "currentSelection",
- "that": "previousTarget",
- "source": "previousSource",
- "nothing": "nothing"
- },
- "unknown_symbol": { "special": "unknownSymbol" },
- "line_direction": {
- "row": "lineNumberModulo100",
- "up": "lineNumberRelativeUp",
- "down": "lineNumberRelativeDown"
- }
- },
- "scope_visualizer.csv": {
- "show_scope_visualizer": { "visualize": "showScopeVisualizer" },
- "hide_scope_visualizer": { "visualize nothing": "hideScopeVisualizer" },
- "visualization_type": {
- "removal": "removal",
- "iteration": "iteration"
- }
- },
- "experimental/experimental_actions.csv": {
- "experimental_action": {
- "-from": "experimental.setInstanceReference"
- }
- },
- "experimental/wrapper_snippets.csv": {
- "wrapper_snippet": {
- "else": "ifElseStatement.alternative",
- "funk": "functionDeclaration.body",
- "if else": "ifElseStatement.consequence",
- "if": "ifStatement.consequence",
- "try": "tryCatchStatement.body",
- "link": "link.text"
- }
- },
- "experimental/insertion_snippets.csv": {
- "insertion_snippet_no_phrase": {
- "if": "ifStatement",
- "if else": "ifElseStatement",
- "try": "tryCatchStatement"
- }
- },
- "experimental/insertion_snippets_single_phrase.csv": {
- "insertion_snippet_single_phrase": {
- "funk": "functionDeclaration.name",
- "link": "link.text"
- }
- },
- "experimental/miscellaneous.csv": {
- "phrase_terminator": { "over": "phraseTerminator" }
- },
- "experimental/actions_custom.csv": {},
- "experimental/regex_scope_types.csv": {},
- "hat_styles.csv": {
- "hat_color": {
- "blue": "blue",
- "green": "green",
- "red": "red",
- "pink": "pink",
- "yellow": "yellow",
- "navy": "userColor1",
- "apricot": "userColor2"
- },
- "hat_shape": {
- "ex": "ex",
- "fox": "fox",
- "wing": "wing",
- "hole": "hole",
- "frame": "frame",
- "curve": "curve",
- "eye": "eye",
- "play": "play",
- "cross": "crosshairs",
- "bolt": "bolt"
- }
- }
-}
diff --git a/cursorless-talon/src/spoken_forms.py b/cursorless-talon/src/spoken_forms.py
deleted file mode 100644
index 30ad16498d..0000000000
--- a/cursorless-talon/src/spoken_forms.py
+++ /dev/null
@@ -1,185 +0,0 @@
-import json
-from pathlib import Path
-from typing import Callable, Concatenate, ParamSpec, TypeVar
-
-from talon import app, fs
-
-from .csv_overrides import (
- SPOKEN_FORM_HEADER,
- ListToSpokenForms,
- SpokenFormEntry,
- init_csv_and_watch_changes,
-)
-from .marks.decorated_mark import init_hats
-from .spoken_forms_output import SpokenFormsOutput
-
-JSON_FILE = Path(__file__).parent / "spoken_forms.json"
-disposables: list[Callable] = []
-
-
-P = ParamSpec("P")
-R = TypeVar("R")
-
-
-def auto_construct_defaults(
- spoken_forms: dict[str, ListToSpokenForms],
- handle_new_values: Callable[[str, list[SpokenFormEntry]], None],
- f: Callable[
- Concatenate[str, ListToSpokenForms, Callable[[list[SpokenFormEntry]], None], P],
- R,
- ],
-):
- """
- Decorator that automatically constructs the default values for the
- `default_values` parameter of `f` based on the spoken forms in
- `spoken_forms`, by extracting the value at the key given by the csv
- filename.
-
- Note that we only ever pass `init_csv_and_watch_changes` as `f`. The
- reason we have this decorator is so that we can destructure the kwargs
- of `init_csv_and_watch_changes` to remove the `default_values` parameter.
-
- Args:
- spoken_forms (dict[str, ListToSpokenForms]): The spoken forms
- handle_new_values (Callable[[ListToSpokenForms], None]): A callback to be called when the lists are updated
- f (Callable[Concatenate[str, ListToSpokenForms, P], R]): Will always be `init_csv_and_watch_changes`
- """
-
- def ret(filename: str, *args: P.args, **kwargs: P.kwargs) -> R:
- default_values = spoken_forms[filename]
- return f(
- filename,
- default_values,
- lambda new_values: handle_new_values(filename, new_values),
- *args,
- **kwargs,
- )
-
- return ret
-
-
-# Maps from Talon list name to the type of the value in that list, e.g.
-# `pairedDelimiter` or `simpleScopeTypeType`
-# FIXME: This is a hack until we generate spoken_forms.json from Typescript side
-# At that point we can just include its type as part of that file
-LIST_TO_TYPE_MAP = {
- "wrapper_selectable_paired_delimiter": "pairedDelimiter",
- "selectable_only_paired_delimiter": "pairedDelimiter",
- "wrapper_only_paired_delimiter": "pairedDelimiter",
- "surrounding_pair_scope_type": "pairedDelimiter",
- "scope_type": "simpleScopeTypeType",
- "glyph_scope_type": "complexScopeTypeType",
- "custom_regex_scope_type": "customRegex",
-}
-
-
-def update():
- global disposables
-
- for disposable in disposables:
- disposable()
-
- with open(JSON_FILE, encoding="utf-8") as file:
- spoken_forms = json.load(file)
-
- initialized = False
-
- # Maps from csv name to list of SpokenFormEntry
- custom_spoken_forms: dict[str, list[SpokenFormEntry]] = {}
- spoken_forms_output = SpokenFormsOutput()
- spoken_forms_output.init()
-
- def update_spoken_forms_output():
- spoken_forms_output.write(
- [
- {
- "type": LIST_TO_TYPE_MAP[entry.list_name],
- "id": entry.id,
- "spokenForms": entry.spoken_forms,
- }
- for spoken_form_list in custom_spoken_forms.values()
- for entry in spoken_form_list
- if entry.list_name in LIST_TO_TYPE_MAP
- ]
- )
-
- def handle_new_values(csv_name: str, values: list[SpokenFormEntry]):
- custom_spoken_forms[csv_name] = values
- if initialized:
- # On first run, we just do one update at the end, so we suppress
- # writing until we get there
- update_spoken_forms_output()
-
- handle_csv = auto_construct_defaults(
- spoken_forms, handle_new_values, init_csv_and_watch_changes
- )
-
- disposables = [
- handle_csv("actions.csv"),
- handle_csv("target_connectives.csv"),
- handle_csv("modifiers.csv"),
- handle_csv("positions.csv"),
- handle_csv("paired_delimiters.csv"),
- handle_csv("special_marks.csv"),
- handle_csv("scope_visualizer.csv"),
- handle_csv("experimental/experimental_actions.csv"),
- handle_csv("experimental/miscellaneous.csv"),
- handle_csv(
- "modifier_scope_types.csv",
- pluralize_lists=["scope_type", "glyph_scope_type"],
- extra_allowed_values=[
- "private.fieldAccess",
- "private.switchStatementSubject",
- ],
- default_list_name="scope_type",
- ),
- handle_csv(
- "experimental/wrapper_snippets.csv",
- allow_unknown_values=True,
- default_list_name="wrapper_snippet",
- ),
- handle_csv(
- "experimental/insertion_snippets.csv",
- allow_unknown_values=True,
- default_list_name="insertion_snippet_no_phrase",
- ),
- handle_csv(
- "experimental/insertion_snippets_single_phrase.csv",
- allow_unknown_values=True,
- default_list_name="insertion_snippet_single_phrase",
- ),
- handle_csv(
- "experimental/actions_custom.csv",
- headers=[SPOKEN_FORM_HEADER, "VSCode command"],
- allow_unknown_values=True,
- default_list_name="custom_action",
- ),
- handle_csv(
- "experimental/regex_scope_types.csv",
- headers=[SPOKEN_FORM_HEADER, "Regex"],
- allow_unknown_values=True,
- default_list_name="custom_regex_scope_type",
- pluralize_lists=["custom_regex_scope_type"],
- ),
- init_hats(
- spoken_forms["hat_styles.csv"]["hat_color"],
- spoken_forms["hat_styles.csv"]["hat_shape"],
- ),
- ]
-
- update_spoken_forms_output()
- initialized = True
-
-
-def on_watch(path, flags):
- if JSON_FILE.match(path):
- update()
-
-
-def on_ready():
- update()
-
- fs.watch(str(JSON_FILE.parent), on_watch)
-
-
-app.register("ready", on_ready)
diff --git a/cursorless-talon/src/spoken_forms_output.py b/cursorless-talon/src/spoken_forms_output.py
deleted file mode 100644
index 20f1f8623b..0000000000
--- a/cursorless-talon/src/spoken_forms_output.py
+++ /dev/null
@@ -1,48 +0,0 @@
-import json
-from pathlib import Path
-from typing import TypedDict
-
-from talon import app
-
-SPOKEN_FORMS_OUTPUT_PATH = Path.home() / ".cursorless" / "state.json"
-STATE_JSON_VERSION_NUMBER = 0
-
-
-class SpokenFormEntry(TypedDict):
- type: str
- id: str
- spokenForms: list[str]
-
-
-class SpokenFormsOutput:
- """
- Writes spoken forms to a json file for use by the Cursorless vscode extension
- """
-
- def init(self):
- try:
- SPOKEN_FORMS_OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True)
- except Exception:
- error_message = (
- f"Error creating spoken form dir {SPOKEN_FORMS_OUTPUT_PATH.parent}"
- )
- print(error_message)
- app.notify(error_message)
-
- def write(self, spoken_forms: list[SpokenFormEntry]):
- with open(SPOKEN_FORMS_OUTPUT_PATH, "w", encoding="UTF-8") as out:
- try:
- out.write(
- json.dumps(
- {
- "version": STATE_JSON_VERSION_NUMBER,
- "spokenForms": spoken_forms,
- }
- )
- )
- except Exception:
- error_message = (
- f"Error writing spoken form json {SPOKEN_FORMS_OUTPUT_PATH}"
- )
- print(error_message)
- app.notify(error_message)
diff --git a/cursorless-talon/src/targets/destination.py b/cursorless-talon/src/targets/destination.py
deleted file mode 100644
index 85ef3a517c..0000000000
--- a/cursorless-talon/src/targets/destination.py
+++ /dev/null
@@ -1,70 +0,0 @@
-from typing import Union
-
-from talon import Context, Module, actions
-
-from .target_types import ListDestination, PrimitiveDestination
-
-mod = Module()
-
-mod.list(
- "cursorless_insertion_mode_before_after",
- desc="Cursorless insertion mode before/after",
-)
-mod.list("cursorless_insertion_mode_to", desc="Cursorless insertion mode to")
-mod.tag(
- "cursorless_disable_legacy_destination",
- desc="Disabled the Cursorless legacy destination(to after) support",
-)
-
-ctx = Context()
-ctx.matches = r"""
-tag: user.cursorless_disable_legacy_destination
-"""
-
-
-# DEPRECATED @ 2023-08-01
-@mod.capture(
- rule="([{user.cursorless_insertion_mode_to}] {user.cursorless_insertion_mode_before_after}) | {user.cursorless_insertion_mode_to}"
-)
-def cursorless_insertion_mode(m) -> str:
- try:
- before_after = m.cursorless_insertion_mode_before_after
- if hasattr(m, "cursorless_insertion_mode_to"):
- words = m._unmapped
- actions.app.notify(
- f"'{' '.join(words)}' is deprecated. Please just say '{words[-1]}'"
- )
- return before_after
- except AttributeError:
- return "to"
-
-
-@ctx.capture(
- "user.cursorless_insertion_mode",
- rule="{user.cursorless_insertion_mode_before_after} | {user.cursorless_insertion_mode_to}",
-)
-def cursorless_insertion_mode_ctx(m) -> str:
- try:
- return m.cursorless_insertion_mode_before_after
- except AttributeError:
- return "to"
-
-
-@mod.capture(
- rule=(
- " "
- "({user.cursorless_list_connective} )*"
- )
-)
-def cursorless_destination(m) -> Union[ListDestination, PrimitiveDestination]:
- destinations = [
- PrimitiveDestination(insertion_mode, target)
- for insertion_mode, target in zip(
- m.cursorless_insertion_mode_list, m.cursorless_target_list
- )
- ]
-
- if len(destinations) == 1:
- return destinations[0]
-
- return ListDestination(destinations)
diff --git a/cursorless-talon/src/targets/primitive_target.py b/cursorless-talon/src/targets/primitive_target.py
deleted file mode 100644
index 0051a69e48..0000000000
--- a/cursorless-talon/src/targets/primitive_target.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from talon import Module
-
-from .target_types import PrimitiveTarget
-
-mod = Module()
-
-
-@mod.capture(
- rule=(
- "+ [] | "
- )
-)
-def cursorless_primitive_target(m) -> PrimitiveTarget:
- return PrimitiveTarget(
- getattr(m, "cursorless_mark", None),
- getattr(m, "cursorless_modifier_list", None),
- )
diff --git a/cursorless-talon/src/targets/range_target.py b/cursorless-talon/src/targets/range_target.py
deleted file mode 100644
index 37dbab69dc..0000000000
--- a/cursorless-talon/src/targets/range_target.py
+++ /dev/null
@@ -1,66 +0,0 @@
-from dataclasses import dataclass
-from typing import Optional
-
-from talon import Module
-
-from .target_types import ImplicitTarget, PrimitiveTarget, RangeTarget, RangeTargetType
-
-mod = Module()
-
-mod.list(
- "cursorless_range_connective",
- desc="A range joiner that indicates whether to include or exclude anchor and active",
-)
-
-
-@dataclass
-class RangeConnective:
- excludeAnchor: bool
- excludeActive: bool
-
-
-@dataclass
-class RangeConnectiveWithType:
- connective: RangeConnective
- type: Optional[RangeTargetType]
-
-
-@mod.capture(rule="{user.cursorless_range_connective}")
-def cursorless_range_connective(m) -> RangeConnective:
- return RangeConnective(
- m.cursorless_range_connective in ["rangeExclusive", "rangeExcludingStart"],
- m.cursorless_range_connective in ["rangeExclusive", "rangeExcludingEnd"],
- )
-
-
-@mod.capture(
- rule="[] | "
-)
-def cursorless_range_connective_with_type(m) -> RangeConnectiveWithType:
- return RangeConnectiveWithType(
- getattr(m, "cursorless_range_connective", RangeConnective(False, False)),
- getattr(m, "cursorless_range_type", None),
- )
-
-
-@mod.capture(
- rule=(
- "[] "
- )
-)
-def cursorless_range_target(m) -> RangeTarget:
- primitive_targets: list[PrimitiveTarget] = m.cursorless_primitive_target_list
- range_connective_with_type: RangeConnectiveWithType = (
- m.cursorless_range_connective_with_type
- )
- range_connective = range_connective_with_type.connective
-
- anchor = ImplicitTarget() if len(primitive_targets) == 1 else primitive_targets[0]
-
- return RangeTarget(
- anchor,
- primitive_targets[-1],
- range_connective.excludeAnchor,
- range_connective.excludeActive,
- range_connective_with_type.type,
- )
diff --git a/cursorless-talon/src/targets/range_type.py b/cursorless-talon/src/targets/range_type.py
deleted file mode 100644
index a6f3c21a33..0000000000
--- a/cursorless-talon/src/targets/range_type.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from talon import Module
-
-mod = Module()
-
-mod.list(
- "cursorless_range_type",
- desc="A range modifier that indicates the specific type of the range",
-)
-
-# Maps from the id we use in the spoken form csv to the modifier type
-# expected by Cursorless extension
-range_type_map = {
- "verticalRange": "vertical",
-}
-
-
-@mod.capture(rule="{user.cursorless_range_type}")
-def cursorless_range_type(m) -> str:
- """Range type modifier"""
- return range_type_map[m.cursorless_range_type]
diff --git a/cursorless-talon/src/targets/target.py b/cursorless-talon/src/targets/target.py
deleted file mode 100644
index 417d8c1c4b..0000000000
--- a/cursorless-talon/src/targets/target.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from typing import Union
-
-from talon import Module
-
-from .target_types import ListTarget, PrimitiveTarget, RangeTarget
-
-mod = Module()
-
-
-mod.list(
- "cursorless_list_connective",
- desc="A list joiner",
-)
-
-
-@mod.capture(
- rule=(" | ")
-)
-def cursorless_primitive_or_range_target(m) -> Union[RangeTarget, PrimitiveTarget]:
- return m[0]
-
-
-@mod.capture(
- rule=(
- " "
- "({user.cursorless_list_connective} )*"
- )
-)
-def cursorless_target(m) -> Union[ListTarget, RangeTarget, PrimitiveTarget]:
- targets = m.cursorless_primitive_or_range_target_list
-
- if len(targets) == 1:
- return targets[0]
-
- return ListTarget(targets)
diff --git a/cursorless-talon/src/targets/target_types.py b/cursorless-talon/src/targets/target_types.py
deleted file mode 100644
index 05800e52d2..0000000000
--- a/cursorless-talon/src/targets/target_types.py
+++ /dev/null
@@ -1,72 +0,0 @@
-from dataclasses import dataclass
-from typing import Any, Literal, Optional, Union
-
-from ..marks.mark_types import Mark
-
-RangeTargetType = Literal["vertical"]
-
-
-@dataclass
-class PrimitiveTarget:
- type = "primitive"
- mark: Optional[Mark]
- modifiers: Optional[list[dict[str, Any]]]
-
-
-@dataclass
-class ImplicitTarget:
- type = "implicit"
-
-
-@dataclass
-class RangeTarget:
- type = "range"
- anchor: Union[PrimitiveTarget, ImplicitTarget]
- active: PrimitiveTarget
- excludeAnchor: bool
- excludeActive: bool
- rangeType: Optional[RangeTargetType]
-
-
-@dataclass
-class ListTarget:
- type = "list"
- elements: list[Union[PrimitiveTarget, RangeTarget]]
-
-
-CursorlessTarget = Union[
- ListTarget,
- RangeTarget,
- PrimitiveTarget,
- ImplicitTarget,
-]
-CursorlessExplicitTarget = Union[
- ListTarget,
- RangeTarget,
- PrimitiveTarget,
-]
-
-
-@dataclass
-class PrimitiveDestination:
- type = "primitive"
- insertionMode: Literal["to", "before", "after"]
- target: Union[ListTarget, RangeTarget, PrimitiveTarget]
-
-
-@dataclass
-class ImplicitDestination:
- type = "implicit"
-
-
-@dataclass
-class ListDestination:
- type = "list"
- destinations: list[PrimitiveDestination]
-
-
-CursorlessDestination = Union[
- ListDestination,
- PrimitiveDestination,
- ImplicitDestination,
-]
diff --git a/cursorless-talon/src/terms.py b/cursorless-talon/src/terms.py
deleted file mode 100644
index d41727af64..0000000000
--- a/cursorless-talon/src/terms.py
+++ /dev/null
@@ -1,21 +0,0 @@
-"""
-Stores terms that are used in many different places
-"""
-
-from talon import Context, Module
-
-mod = Module()
-ctx = Context()
-
-mod.list(
- "cursorless_homophone",
- "Various alternative pronunciations of 'cursorless' to improve accuracy",
-)
-
-# FIXME: Remove type ignore once Talon supports list types
-# See https://github.com/talonvoice/talon/issues/654
-ctx.lists["user.cursorless_homophone"] = [ # pyright: ignore [reportArgumentType]
- "cursorless",
- "cursor less",
- "cursor list",
-]
diff --git a/cursorless-talon/src/vendor/__init__.py b/cursorless-talon/src/vendor/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/cursorless-talon/src/vendor/inflection.py b/cursorless-talon/src/vendor/inflection.py
deleted file mode 100644
index 19310db360..0000000000
--- a/cursorless-talon/src/vendor/inflection.py
+++ /dev/null
@@ -1,175 +0,0 @@
-# From https://github.com/jpvanhal/inflection/blob/b00d4d348b32ef5823221b20ee4cbd1d2d924462/inflection/__init__.py
-# License https://github.com/jpvanhal/inflection/blob/b00d4d348b32ef5823221b20ee4cbd1d2d924462/LICENSE
-import re
-
-PLURALS = [
- (r"(?i)(quiz)$", r"\1zes"),
- (r"(?i)^(oxen)$", r"\1"),
- (r"(?i)^(ox)$", r"\1en"),
- (r"(?i)(m|l)ice$", r"\1ice"),
- (r"(?i)(m|l)ouse$", r"\1ice"),
- (r"(?i)(passer)s?by$", r"\1sby"),
- (r"(?i)(matr|vert|ind)(?:ix|ex)$", r"\1ices"),
- (r"(?i)(x|ch|ss|sh)$", r"\1es"),
- (r"(?i)([^aeiouy]|qu)y$", r"\1ies"),
- (r"(?i)(hive)$", r"\1s"),
- (r"(?i)([lr])f$", r"\1ves"),
- (r"(?i)([^f])fe$", r"\1ves"),
- (r"(?i)sis$", "ses"),
- (r"(?i)([ti])a$", r"\1a"),
- (r"(?i)([ti])um$", r"\1a"),
- (r"(?i)(buffal|potat|tomat)o$", r"\1oes"),
- (r"(?i)(bu)s$", r"\1ses"),
- (r"(?i)(alias|status)$", r"\1es"),
- (r"(?i)(octop|vir)i$", r"\1i"),
- (r"(?i)(octop|vir)us$", r"\1i"),
- (r"(?i)^(ax|test)is$", r"\1es"),
- (r"(?i)s$", "s"),
- (r"$", "s"),
-]
-
-
-SINGULARS = [
- (r"(?i)(database)s$", r"\1"),
- (r"(?i)(quiz)zes$", r"\1"),
- (r"(?i)(matr)ices$", r"\1ix"),
- (r"(?i)(vert|ind)ices$", r"\1ex"),
- (r"(?i)(passer)sby$", r"\1by"),
- (r"(?i)^(ox)en", r"\1"),
- (r"(?i)(alias|status)(es)?$", r"\1"),
- (r"(?i)(octop|vir)(us|i)$", r"\1us"),
- (r"(?i)^(a)x[ie]s$", r"\1xis"),
- (r"(?i)(cris|test)(is|es)$", r"\1is"),
- (r"(?i)(shoe)s$", r"\1"),
- (r"(?i)(o)es$", r"\1"),
- (r"(?i)(bus)(es)?$", r"\1"),
- (r"(?i)(m|l)ice$", r"\1ouse"),
- (r"(?i)(x|ch|ss|sh)es$", r"\1"),
- (r"(?i)(m)ovies$", r"\1ovie"),
- (r"(?i)(s)eries$", r"\1eries"),
- (r"(?i)([^aeiouy]|qu)ies$", r"\1y"),
- (r"(?i)([lr])ves$", r"\1f"),
- (r"(?i)(tive)s$", r"\1"),
- (r"(?i)(hive)s$", r"\1"),
- (r"(?i)([^f])ves$", r"\1fe"),
- (r"(?i)(t)he(sis|ses)$", r"\1hesis"),
- (r"(?i)(s)ynop(sis|ses)$", r"\1ynopsis"),
- (r"(?i)(p)rogno(sis|ses)$", r"\1rognosis"),
- (r"(?i)(p)arenthe(sis|ses)$", r"\1arenthesis"),
- (r"(?i)(d)iagno(sis|ses)$", r"\1iagnosis"),
- (r"(?i)(b)a(sis|ses)$", r"\1asis"),
- (r"(?i)(a)naly(sis|ses)$", r"\1nalysis"),
- (r"(?i)([ti])a$", r"\1um"),
- (r"(?i)(n)ews$", r"\1ews"),
- (r"(?i)(ss)$", r"\1"),
- (r"(?i)s$", ""),
-]
-
-
-UNCOUNTABLES = {
- "equipment",
- "fish",
- "information",
- "jeans",
- "money",
- "rice",
- "series",
- "sheep",
- "species",
-}
-
-
-def _irregular(singular: str, plural: str) -> None:
- """
- A convenience function to add appropriate rules to plurals and singular
- for irregular words.
-
- :param singular: irregular word in singular form
- :param plural: irregular word in plural form
- """
-
- def caseinsensitive(string: str) -> str:
- return "".join("[" + char + char.upper() + "]" for char in string)
-
- if singular[0].upper() == plural[0].upper():
- PLURALS.insert(0, (rf"(?i)({singular[0]}){singular[1:]}$", r"\1" + plural[1:]))
- PLURALS.insert(0, (rf"(?i)({plural[0]}){plural[1:]}$", r"\1" + plural[1:]))
- SINGULARS.insert(0, (rf"(?i)({plural[0]}){plural[1:]}$", r"\1" + singular[1:]))
- else:
- PLURALS.insert(
- 0,
- (
- rf"{singular[0].upper()}{caseinsensitive(singular[1:])}$",
- plural[0].upper() + plural[1:],
- ),
- )
- PLURALS.insert(
- 0,
- (
- rf"{singular[0].lower()}{caseinsensitive(singular[1:])}$",
- plural[0].lower() + plural[1:],
- ),
- )
- PLURALS.insert(
- 0,
- (
- rf"{plural[0].upper()}{caseinsensitive(plural[1:])}$",
- plural[0].upper() + plural[1:],
- ),
- )
- PLURALS.insert(
- 0,
- (
- rf"{plural[0].lower()}{caseinsensitive(plural[1:])}$",
- plural[0].lower() + plural[1:],
- ),
- )
- SINGULARS.insert(
- 0,
- (
- rf"{plural[0].upper()}{caseinsensitive(plural[1:])}$",
- singular[0].upper() + singular[1:],
- ),
- )
- SINGULARS.insert(
- 0,
- (
- rf"{plural[0].lower()}{caseinsensitive(plural[1:])}$",
- singular[0].lower() + singular[1:],
- ),
- )
-
-
-def pluralize(word: str) -> str:
- """
- Return the plural form of a word.
-
- Examples::
-
- >>> pluralize("posts")
- 'posts'
- >>> pluralize("octopus")
- 'octopi'
- >>> pluralize("sheep")
- 'sheep'
- >>> pluralize("CamelOctopus")
- 'CamelOctopi'
-
- """
- if not word or word.lower() in UNCOUNTABLES:
- return word
- else:
- for rule, replacement in PLURALS:
- if re.search(rule, word):
- return re.sub(rule, replacement, word)
- return word
-
-
-_irregular("person", "people")
-_irregular("man", "men")
-_irregular("human", "humans")
-_irregular("child", "children")
-_irregular("sex", "sexes")
-_irregular("move", "moves")
-_irregular("cow", "kine")
-_irregular("zombie", "zombies")
diff --git a/cursorless-talon/src/vendor/jstyleson.py b/cursorless-talon/src/vendor/jstyleson.py
deleted file mode 100644
index 607ef6b9a0..0000000000
--- a/cursorless-talon/src/vendor/jstyleson.py
+++ /dev/null
@@ -1,137 +0,0 @@
-# From https://github.com/linjackson78/jstyleson/blob/8c47cc9e665b3b1744cccfaa7a650de5f3c575dd/jstyleson.py
-# License https://github.com/linjackson78/jstyleson/blob/8c47cc9e665b3b1744cccfaa7a650de5f3c575dd/LICENSE
-import json
-
-
-def dispose(json_str):
- """Clear all comments in json_str.
-
- Clear JS-style comments like // and /**/ in json_str.
- Accept a str or unicode as input.
-
- Args:
- json_str: A json string of str or unicode to clean up comment
-
- Returns:
- str: The str without comments (or unicode if you pass in unicode)
- """
- result_str = list(json_str)
- escaped = False
- normal = True
- sl_comment = False
- ml_comment = False
- quoted = False
-
- a_step_from_comment = False
- a_step_from_comment_away = False
-
- former_index = None
-
- for index, char in enumerate(json_str):
- if escaped: # We have just met a '\'
- escaped = False
- continue
-
- if a_step_from_comment: # We have just met a '/'
- if char != "/" and char != "*":
- a_step_from_comment = False
- normal = True
- continue
-
- if a_step_from_comment_away: # We have just met a '*'
- if char != "/":
- a_step_from_comment_away = False
-
- if char == '"':
- if normal and not escaped:
- # We are now in a string
- quoted = True
- normal = False
- elif quoted and not escaped:
- # We are now out of a string
- quoted = False
- normal = True
-
- elif char == "\\":
- # '\' should not take effect in comment
- if normal or quoted:
- escaped = True
-
- elif char == "/":
- if a_step_from_comment:
- # Now we are in single line comment
- a_step_from_comment = False
- sl_comment = True
- normal = False
- former_index = index - 1
- elif a_step_from_comment_away:
- # Now we are out of comment
- a_step_from_comment_away = False
- normal = True
- ml_comment = False
- for i in range(former_index, index + 1):
- result_str[i] = ""
-
- elif normal:
- # Now we are just one step away from comment
- a_step_from_comment = True
- normal = False
-
- elif char == "*":
- if a_step_from_comment:
- # We are now in multi-line comment
- a_step_from_comment = False
- ml_comment = True
- normal = False
- former_index = index - 1
- elif ml_comment:
- a_step_from_comment_away = True
- elif char == "\n":
- if sl_comment:
- sl_comment = False
- normal = True
- for i in range(former_index, index + 1):
- result_str[i] = ""
- elif char == "]" or char == "}":
- if normal:
- _remove_last_comma(result_str, index)
-
- # To remove single line comment which is the last line of json
- if sl_comment:
- sl_comment = False
- normal = True
- for i in range(former_index, len(json_str)):
- result_str[i] = ""
-
- # Show respect to original input if we are in python2
- return ("" if isinstance(json_str, str) else "").join(result_str)
-
-
-# There may be performance suffer backtracking the last comma
-def _remove_last_comma(str_list, before_index):
- i = before_index - 1
- while str_list[i].isspace() or not str_list[i]:
- i -= 1
-
- # This is the first none space char before before_index
- if str_list[i] == ",":
- str_list[i] = ""
-
-
-# Below are just some wrapper function around the standard json module.
-
-
-def loads(text, **kwargs):
- return json.loads(dispose(text), **kwargs)
-
-
-def load(fp, **kwargs):
- return loads(fp.read(), **kwargs)
-
-
-def dumps(obj, **kwargs):
- return json.dumps(obj, **kwargs)
-
-
-def dump(obj, fp, **kwargs):
- json.dump(obj, fp, **kwargs)
diff --git a/cursorless-talon/src/versions.py b/cursorless-talon/src/versions.py
deleted file mode 100644
index 056299a93a..0000000000
--- a/cursorless-talon/src/versions.py
+++ /dev/null
@@ -1 +0,0 @@
-COMMAND_VERSION = 7