Skip to content

Commit 663934c

Browse files
Merge branch 'main' into bash-lang
2 parents ee9c2c1 + aa76859 commit 663934c

File tree

332 files changed

+8600
-2846
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

332 files changed

+8600
-2846
lines changed

.github/CODEOWNERS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
* @pokey @AndreasArvidsson
2+
3+
*keyboard* @pokey @josharian
4+
*Keyboard* @pokey @josharian

.pre-commit-config.yaml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ repos:
3030
- id: detect-private-key
3131
- id: end-of-file-fixer
3232
exclude_types: [svg]
33-
exclude: patches/.*\.patch
33+
exclude: ^patches/.*\.patch$|\.scope$
3434
- id: fix-byte-order-marker
3535
- id: forbid-submodules
3636
- id: mixed-line-ending
@@ -71,16 +71,16 @@ repos:
7171
files: ^packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/.*/[^/]*\.yml$
7272
language: system
7373
entry: pnpm exec ./packages/common/scripts/my-ts-node.js packages/cursorless-engine/src/scripts/transformRecordedTests/index.ts --check-marks
74-
- repo: https://github.com/ikamensh/flynt/
75-
rev: "0.78"
74+
- repo: https://github.com/ikamensh/flynt
75+
rev: "1.0.1"
7676
hooks:
7777
- id: flynt
78-
- repo: https://github.com/charliermarsh/ruff-pre-commit
79-
rev: "v0.0.260"
78+
- repo: https://github.com/astral-sh/ruff-pre-commit
79+
rev: v0.1.11
8080
hooks:
8181
- id: ruff
8282
args: [--fix, --exit-non-zero-on-fix]
83-
- repo: https://github.com/psf/black
84-
rev: 23.3.0
83+
- repo: https://github.com/psf/black-pre-commit-mirror
84+
rev: 24.1.1
8585
hooks:
8686
- id: black

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<img alt="Tests" src="https://img.shields.io/github/actions/workflow/status/cursorless-dev/cursorless-vscode/test.yml?branch=main&logo=github&label=tests" />
1111
</a>
1212
<a href="https://github.com/cursorless-dev/cursorless/graphs/contributors" target="_blank">
13-
<img alt="Maintenance" src="https://img.shields.io/maintenance/yes/2023.svg?logo=data:image/svg%2bxml;base64,PHN2ZyBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhcyIgZGF0YS1pY29uPSJ3cmVuY2giIGNsYXNzPSJzdmctaW5saW5lLS1mYSBmYS13cmVuY2ggZmEtdy0xNiIgcm9sZT0iaW1nIgogICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+CiAgICA8cGF0aCBmaWxsPSJ3aGl0ZSIgZD0iTTUwNy43MyAxMDkuMWMtMi4yNC05LjAzLTEzLjU0LTEyLjA5LTIwLjEyLTUuNTFsLTc0LjM2IDc0LjM2LTY3Ljg4LTExLjMxLTExLjMxLTY3Ljg4IDc0LjM2LTc0LjM2YzYuNjItNi42MiAzLjQzLTE3LjktNS42Ni0yMC4xNi00Ny4zOC0xMS43NC05OS41NS45MS0xMzYuNTggMzcuOTMtMzkuNjQgMzkuNjQtNTAuNTUgOTcuMS0zNC4wNSAxNDcuMkwxOC43NCA0MDIuNzZjLTI0Ljk5IDI0Ljk5LTI0Ljk5IDY1LjUxIDAgOTAuNSAyNC45OSAyNC45OSA2NS41MSAyNC45OSA5MC41IDBsMjEzLjIxLTIxMy4yMWM1MC4xMiAxNi43MSAxMDcuNDcgNS42OCAxNDcuMzctMzQuMjIgMzcuMDctMzcuMDcgNDkuNy04OS4zMiAzNy45MS0xMzYuNzN6TTY0IDQ3MmMtMTMuMjUgMC0yNC0xMC43NS0yNC0yNCAwLTEzLjI2IDEwLjc1LTI0IDI0LTI0czI0IDEwLjc0IDI0IDI0YzAgMTMuMjUtMTAuNzUgMjQtMjQgMjR6Ij48L3BhdGg+Cjwvc3ZnPg==" />
13+
<img alt="Maintenance" src="https://img.shields.io/maintenance/yes/2024.svg?logo=data:image/svg%2bxml;base64,PHN2ZyBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhcyIgZGF0YS1pY29uPSJ3cmVuY2giIGNsYXNzPSJzdmctaW5saW5lLS1mYSBmYS13cmVuY2ggZmEtdy0xNiIgcm9sZT0iaW1nIgogICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+CiAgICA8cGF0aCBmaWxsPSJ3aGl0ZSIgZD0iTTUwNy43MyAxMDkuMWMtMi4yNC05LjAzLTEzLjU0LTEyLjA5LTIwLjEyLTUuNTFsLTc0LjM2IDc0LjM2LTY3Ljg4LTExLjMxLTExLjMxLTY3Ljg4IDc0LjM2LTc0LjM2YzYuNjItNi42MiAzLjQzLTE3LjktNS42Ni0yMC4xNi00Ny4zOC0xMS43NC05OS41NS45MS0xMzYuNTggMzcuOTMtMzkuNjQgMzkuNjQtNTAuNTUgOTcuMS0zNC4wNSAxNDcuMkwxOC43NCA0MDIuNzZjLTI0Ljk5IDI0Ljk5LTI0Ljk5IDY1LjUxIDAgOTAuNSAyNC45OSAyNC45OSA2NS41MSAyNC45OSA5MC41IDBsMjEzLjIxLTIxMy4yMWM1MC4xMiAxNi43MSAxMDcuNDcgNS42OCAxNDcuMzctMzQuMjIgMzcuMDctMzcuMDcgNDkuNy04OS4zMiAzNy45MS0xMzYuNzN6TTY0IDQ3MmMtMTMuMjUgMC0yNC0xMC43NS0yNC0yNCAwLTEzLjI2IDEwLjc1LTI0IDI0LTI0czI0IDEwLjc0IDI0IDI0YzAgMTMuMjUtMTAuNzUgMjQtMjQgMjR6Ij48L3BhdGg+Cjwvc3ZnPg==" />
1414
</a>
1515
<a href="https://github.com/cursorless-dev/cursorless/blob/main/LICENSE" target="_blank">
1616
<img alt="License: MIT" src="https://img.shields.io/github/license/cursorless-dev/cursorless-vscode?color=success&logo=data:image/svg%2bxml;base64,PHN2ZyBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhcyIgZGF0YS1pY29uPSJiYWxhbmNlLXNjYWxlIiBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtYmFsYW5jZS1zY2FsZSBmYS13LTIwIiByb2xlPSJpbWciCiAgICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NDAgNTEyIj4KICAgIDxwYXRoIGZpbGw9IndoaXRlIiBkPSJNMjU2IDMzNmgtLjAyYzAtMTYuMTggMS4zNC04LjczLTg1LjA1LTE4MS41MS0xNy42NS0zNS4yOS02OC4xOS0zNS4zNi04NS44NyAwQy0yLjA2IDMyOC43NS4wMiAzMjAuMzMuMDIgMzM2SDBjMCA0NC4xOCA1Ny4zMSA4MCAxMjggODBzMTI4LTM1LjgyIDEyOC04MHpNMTI4IDE3Nmw3MiAxNDRINTZsNzItMTQ0em01MTEuOTggMTYwYzAtMTYuMTggMS4zNC04LjczLTg1LjA1LTE4MS41MS0xNy42NS0zNS4yOS02OC4xOS0zNS4zNi04NS44NyAwLTg3LjEyIDE3NC4yNi04NS4wNCAxNjUuODQtODUuMDQgMTgxLjUxSDM4NGMwIDQ0LjE4IDU3LjMxIDgwIDEyOCA4MHMxMjgtMzUuODIgMTI4LTgwaC0uMDJ6TTQ0MCAzMjBsNzItMTQ0IDcyIDE0NEg0NDB6bTg4IDEyOEgzNTJWMTUzLjI1YzIzLjUxLTEwLjI5IDQxLjE2LTMxLjQ4IDQ2LjM5LTU3LjI1SDUyOGM4Ljg0IDAgMTYtNy4xNiAxNi0xNlY0OGMwLTguODQtNy4xNi0xNi0xNi0xNkgzODMuNjRDMzY5LjA0IDEyLjY4IDM0Ni4wOSAwIDMyMCAwcy00OS4wNCAxMi42OC02My42NCAzMkgxMTJjLTguODQgMC0xNiA3LjE2LTE2IDE2djMyYzAgOC44NCA3LjE2IDE2IDE2IDE2aDEyOS42MWM1LjIzIDI1Ljc2IDIyLjg3IDQ2Ljk2IDQ2LjM5IDU3LjI1VjQ0OEgxMTJjLTguODQgMC0xNiA3LjE2LTE2IDE2djMyYzAgOC44NCA3LjE2IDE2IDE2IDE2aDQxNmM4Ljg0IDAgMTYtNy4xNiAxNi0xNnYtMzJjMC04Ljg0LTcuMTYtMTYtMTYtMTZ6Ij48L3BhdGg+Cjwvc3ZnPg==" />
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
tags: [enhancement]
3+
pullRequest: 2236
4+
---
5+
6+
- Added increment action. Will increment a number. eg `"increment this"` to change `1` to `2`.
7+
8+
- Added decrement action. Will decrement a number. eg `"decrement this"` to change `2` to `1`.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
tags: [enhancement]
3+
pullRequest: 2235
4+
---
5+
6+
- Fall back to text-based Talon actions when editor is not focused. This allows you to say things like "take token", "bring air", etc, when in the terminal, search bar, etc.

cursorless-talon/src/cheatsheet/get_list.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,21 @@ def get_raw_list(name: str) -> Mapping[str, str]:
4040
return registry.lists[cursorless_list_name][0].copy()
4141

4242

43+
def get_spoken_form_from_list(list_name: str, value: str) -> str:
44+
"""Get the spoken form of a value from a list.
45+
46+
Args:
47+
list_name (str): The name of the list.
48+
value (str): The value to look up.
49+
50+
Returns:
51+
str: The spoken form of the value.
52+
"""
53+
return next(
54+
spoken_form for spoken_form, v in get_raw_list(list_name).items() if v == value
55+
)
56+
57+
4358
def make_dict_readable(
4459
type: str, dict: Mapping[str, str], descriptions: Mapping[str, str]
4560
) -> list[ListItemDescriptor]:

cursorless-talon/src/cheatsheet/sections/compound_targets.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from ..get_list import get_raw_list
1+
from ..get_list import get_raw_list, get_spoken_form_from_list
22

33
FORMATTERS = {
44
"rangeExclusive": lambda start, end: f"between {start} and {end}",
@@ -10,16 +10,10 @@
1010

1111

1212
def get_compound_targets():
13-
list_connective_term = next(
14-
spoken_form
15-
for spoken_form, value in get_raw_list("list_connective").items()
16-
if value == "listConnective"
17-
)
18-
vertical_range_term = next(
19-
spoken_form
20-
for spoken_form, value in get_raw_list("range_type").items()
21-
if value == "verticalRange"
13+
list_connective_term = get_spoken_form_from_list(
14+
"list_connective", "listConnective"
2215
)
16+
vertical_range_term = get_spoken_form_from_list("range_type", "verticalRange")
2317

2418
return [
2519
{

cursorless-talon/src/cheatsheet/sections/scopes.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
from ..get_list import get_lists, get_raw_list
1+
from ..get_list import get_lists, get_spoken_form_from_list
22

33

44
def get_scopes():
5-
complex_scopes = get_raw_list("glyph_scope_type")
5+
glyph_spoken_form = get_spoken_form_from_list("glyph_scope_type", "glyph")
66
return [
77
*get_lists(
88
["scope_type"],
@@ -17,7 +17,7 @@ def get_scopes():
1717
"type": "scopeType",
1818
"variations": [
1919
{
20-
"spokenForm": f"{complex_scopes['glyph']} <character>",
20+
"spokenForm": f"{glyph_spoken_form} <character>",
2121
"description": "Instance of single character <character>",
2222
},
2323
],

cursorless-talon/src/command.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33

44
from talon import Module, actions, speech_system
55

6+
from .fallback import perform_fallback
7+
from .versions import COMMAND_VERSION
8+
69

710
@dataclasses.dataclass
811
class CursorlessCommand:
9-
version = 6
12+
version = COMMAND_VERSION
1013
spokenForm: str
1114
usePrePhraseSnapshot: bool
1215
action: dict
@@ -30,10 +33,12 @@ def on_phrase(d):
3033
class Actions:
3134
def private_cursorless_command_and_wait(action: dict):
3235
"""Execute cursorless command and wait for it to finish"""
33-
actions.user.private_cursorless_run_rpc_command_and_wait(
36+
response = actions.user.private_cursorless_run_rpc_command_get(
3437
CURSORLESS_COMMAND_ID,
3538
construct_cursorless_command(action),
3639
)
40+
if "fallback" in response:
41+
perform_fallback(response["fallback"])
3742

3843
def private_cursorless_command_no_wait(action: dict):
3944
"""Execute cursorless command without waiting"""
@@ -44,10 +49,15 @@ def private_cursorless_command_no_wait(action: dict):
4449

4550
def private_cursorless_command_get(action: dict):
4651
"""Execute cursorless command and return result"""
47-
return actions.user.private_cursorless_run_rpc_command_get(
52+
response = actions.user.private_cursorless_run_rpc_command_get(
4853
CURSORLESS_COMMAND_ID,
4954
construct_cursorless_command(action),
5055
)
56+
if "fallback" in response:
57+
return perform_fallback(response["fallback"])
58+
if "returnValue" in response:
59+
return response["returnValue"]
60+
return None
5161

5262

5363
def construct_cursorless_command(action: dict) -> dict:

cursorless-talon/src/csv_overrides.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from pathlib import Path
77
from typing import Callable, Iterable, Optional, TypedDict
88

9-
from talon import Context, Module, actions, app, fs
9+
from talon import Context, Module, actions, app, fs, settings
1010

1111
from .conventions import get_cursorless_list_name
1212
from .vendor.inflection import pluralize
@@ -20,7 +20,7 @@
2020
"cursorless_default_vocabulary",
2121
desc="Use default cursorless vocabulary instead of user custom",
2222
)
23-
cursorless_settings_directory = mod.setting(
23+
mod.setting(
2424
"cursorless_settings_directory",
2525
type=str,
2626
default="cursorless-settings",
@@ -453,7 +453,7 @@ def get_full_path(filename: str):
453453
filename = f"{filename}.csv"
454454

455455
user_dir: Path = actions.path.talon_user()
456-
settings_directory = Path(cursorless_settings_directory.get())
456+
settings_directory = Path(settings.get("user.cursorless_settings_directory"))
457457

458458
if not settings_directory.is_absolute():
459459
settings_directory = user_dir / settings_directory

cursorless-talon/src/cursorless.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from talon import Module
1+
from talon import Module, actions
22

33
mod = Module()
44

@@ -15,3 +15,9 @@ def private_cursorless_show_settings_in_ide():
1515

1616
def private_cursorless_show_sidebar():
1717
"""Show Cursorless-specific settings in ide"""
18+
19+
def private_cursorless_show_command_statistics():
20+
"""Show Cursorless command statistics"""
21+
actions.user.private_cursorless_run_rpc_command_no_wait(
22+
"cursorless.analyzeCommandHistory"
23+
)

cursorless-talon/src/cursorless.talon

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,6 @@ tag: user.cursorless
4343

4444
bar {user.cursorless_homophone}:
4545
user.private_cursorless_show_sidebar()
46+
47+
{user.cursorless_homophone} stats:
48+
user.private_cursorless_show_command_statistics()

cursorless-talon/src/fallback.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
from typing import Callable
2+
3+
from talon import actions
4+
5+
from .versions import COMMAND_VERSION
6+
7+
# This ensures that we remember to update fallback if the response payload changes
8+
assert COMMAND_VERSION == 7
9+
10+
action_callbacks = {
11+
"getText": lambda: [actions.edit.selected_text()],
12+
"setSelection": actions.skip,
13+
"setSelectionBefore": actions.edit.left,
14+
"setSelectionAfter": actions.edit.right,
15+
"copyToClipboard": actions.edit.copy,
16+
"cutToClipboard": actions.edit.cut,
17+
"pasteFromClipboard": actions.edit.paste,
18+
"clearAndSetSelection": actions.edit.delete,
19+
"remove": actions.edit.delete,
20+
"editNewLineBefore": actions.edit.line_insert_up,
21+
"editNewLineAfter": actions.edit.line_insert_down,
22+
}
23+
24+
modifier_callbacks = {
25+
"extendThroughStartOf.line": actions.user.select_line_start,
26+
"extendThroughEndOf.line": actions.user.select_line_end,
27+
"containingScope.document": actions.edit.select_all,
28+
"containingScope.paragraph": actions.edit.select_paragraph,
29+
"containingScope.line": actions.edit.select_line,
30+
"containingScope.token": actions.edit.select_word,
31+
}
32+
33+
34+
def call_as_function(callee: str):
35+
wrap_with_paired_delimiter(f"{callee}(", ")")
36+
37+
38+
def wrap_with_paired_delimiter(left: str, right: str):
39+
selected = actions.edit.selected_text()
40+
actions.insert(f"{left}{selected}{right}")
41+
for _ in right:
42+
actions.edit.left()
43+
44+
45+
def containing_token_if_empty():
46+
if actions.edit.selected_text() == "":
47+
actions.edit.select_word()
48+
49+
50+
def perform_fallback(fallback: dict):
51+
try:
52+
modifier_callbacks = get_modifier_callbacks(fallback)
53+
action_callback = get_action_callback(fallback)
54+
for callback in reversed(modifier_callbacks):
55+
callback()
56+
return action_callback()
57+
except ValueError as ex:
58+
actions.app.notify(str(ex))
59+
60+
61+
def get_action_callback(fallback: dict) -> Callable:
62+
action = fallback["action"]
63+
64+
if action in action_callbacks:
65+
return action_callbacks[action]
66+
67+
match action:
68+
case "insert":
69+
return lambda: actions.insert(fallback["text"])
70+
case "callAsFunction":
71+
return lambda: call_as_function(fallback["callee"])
72+
case "wrapWithPairedDelimiter":
73+
return lambda: wrap_with_paired_delimiter(
74+
fallback["left"], fallback["right"]
75+
)
76+
77+
raise ValueError(f"Unknown Cursorless fallback action: {action}")
78+
79+
80+
def get_modifier_callbacks(fallback: dict) -> list[Callable]:
81+
return [get_modifier_callback(modifier) for modifier in fallback["modifiers"]]
82+
83+
84+
def get_modifier_callback(modifier: dict) -> Callable:
85+
modifier_type = modifier["type"]
86+
87+
match modifier_type:
88+
case "containingTokenIfEmpty":
89+
return containing_token_if_empty
90+
case "containingScope":
91+
scope_type_type = modifier["scopeType"]["type"]
92+
return get_simple_modifier_callback(f"{modifier_type}.{scope_type_type}")
93+
case "extendThroughStartOf":
94+
if "modifiers" not in modifier:
95+
return get_simple_modifier_callback(f"{modifier_type}.line")
96+
case "extendThroughEndOf":
97+
if "modifiers" not in modifier:
98+
return get_simple_modifier_callback(f"{modifier_type}.line")
99+
100+
raise ValueError(f"Unknown Cursorless fallback modifier: {modifier_type}")
101+
102+
103+
def get_simple_modifier_callback(key: str) -> Callable:
104+
try:
105+
return modifier_callbacks[key]
106+
except KeyError:
107+
raise ValueError(f"Unknown Cursorless fallback modifier: {key}")

cursorless-talon/src/number_small.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
This file allows us to use a custom `number_small` capture. See #1021 for more
33
info.
44
"""
5+
56
from talon import Context, Module
67

78
mod = Module()

cursorless-talon/src/spoken_forms.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"comment": "toggleLineComment",
1515
"copy": "copyToClipboard",
1616
"crown": "scrollToTop",
17+
"decrement": "decrement",
1718
"dedent": "outdentLine",
1819
"define": "revealDefinition",
1920
"drink": "editNewLineBefore",
@@ -25,6 +26,7 @@
2526
"give": "deselect",
2627
"highlight": "highlight",
2728
"hover": "showHover",
29+
"increment": "increment",
2830
"indent": "indentLine",
2931
"inspect": "showDebugHover",
3032
"join": "joinLines",

cursorless-talon/src/terms.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""
22
Stores terms that are used in many different places
33
"""
4+
45
from talon import Context, Module
56

67
mod = Module()

cursorless-talon/src/versions.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
COMMAND_VERSION = 7

0 commit comments

Comments
 (0)