Skip to content

Commit 2a68c81

Browse files
Support insertSnippet action (#304)
* Support insertSnippet action Handle action modifiers before positional modifiers * Fixes from Andreas PR review session * Tweaks from PR feedback * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Make halt overridable * Clean up snippet error handling * Throw error if multiple snippet definitions match a context * Improve comment * Update comment * Cleanup * More cleanup * Add doc string * Support cascading scope types for snippets * Update doc string * Removed duplicate test * Restructure tests * At link to issue Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 329f251 commit 2a68c81

File tree

63 files changed

+1885
-335
lines changed

Some content is hidden

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

63 files changed

+1885
-335
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"functionDeclaration": {
3+
"definitions": [
4+
{
5+
"scope": {
6+
"langIds": [
7+
"typescript",
8+
"typescriptreact",
9+
"javascript",
10+
"javascriptreact"
11+
]
12+
},
13+
"body": [
14+
"function $name($parameterList) {",
15+
"\t$body",
16+
"}"
17+
],
18+
"variables": {
19+
"name": {
20+
"formatter": "camelCase"
21+
}
22+
}
23+
},
24+
{
25+
"scope": {
26+
"langIds": [
27+
"python"
28+
]
29+
},
30+
"body": [
31+
"def $name($parameterList):",
32+
"\t$body"
33+
],
34+
"variables": {
35+
"name": {
36+
"formatter": "snakeCase"
37+
}
38+
}
39+
}
40+
],
41+
"description": "Function declaration",
42+
"variables": {
43+
"body": {
44+
"wrapperScopeType": "statement"
45+
}
46+
},
47+
"insertionScopeTypes": [
48+
"namedFunction",
49+
"statement",
50+
"line"
51+
]
52+
}
53+
}

cursorless-snippets/ifElseStatement.cursorless-snippets

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
"alternative": {
4646
"wrapperScopeType": "statement"
4747
}
48-
}
48+
},
49+
"insertionScopeTypes": [
50+
"statement"
51+
]
4952
}
5053
}

cursorless-snippets/ifStatement.cursorless-snippets

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838
"consequence": {
3939
"wrapperScopeType": "statement"
4040
}
41-
}
41+
},
42+
"insertionScopeTypes": [
43+
"statement"
44+
]
4245
}
4346
}

cursorless-snippets/tryCatchStatement.cursorless-snippets

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
"exceptBody": {
4646
"wrapperScopeType": "statement"
4747
}
48-
}
48+
},
49+
"insertionScopeTypes": [
50+
"statement"
51+
]
4952
}
5053
}

cursorless-talon/src/actions/actions.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ def vscode_command_no_wait(command_id: str, target: dict, command_options: dict
8686
"swap_action": {"swap": "swapTargets"},
8787
"move_bring_action": {"bring": "replaceWithTarget", "move": "moveToTarget"},
8888
"wrap_action": {"wrap": "wrapWithPairedDelimiter", "repack": "rewrap"},
89+
"insert_snippet_action": {"snippet": "insertSnippet"},
8990
"reformat_action": {"format": "applyFormatter"},
9091
}
9192

cursorless-talon/src/actions/wrap.py

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,12 @@
11
from typing import Any
22

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

5-
from ..csv_overrides import init_csv_and_watch_changes
65
from ..paired_delimiter import paired_delimiters_map
76

87
mod = Module()
98

10-
mod.tag(
11-
"cursorless_experimental_snippets",
12-
desc="tag for enabling experimental snippet support",
13-
)
14-
159
mod.list("cursorless_wrap_action", desc="Cursorless wrap action")
16-
mod.list("cursorless_wrapper_snippet", desc="Cursorless wrapper snippet")
17-
18-
experimental_snippets_ctx = Context()
19-
experimental_snippets_ctx.matches = r"""
20-
tag: user.cursorless_experimental_snippets
21-
"""
22-
23-
24-
# NOTE: Please do not change these dicts. Use the CSVs for customization.
25-
# See https://www.cursorless.org/docs/user/customization/
26-
wrapper_snippets = {
27-
"else": "ifElseStatement.alternative",
28-
"if else": "ifElseStatement.consequence",
29-
"if": "ifStatement.consequence",
30-
"try": "tryCatchStatement.body",
31-
"link": "link.text",
32-
}
3310

3411

3512
@mod.capture(
@@ -72,18 +49,3 @@ def cursorless_wrap(action_type: str, targets: dict, cursorless_wrapper: dict):
7249
actions.user.cursorless_single_target_command_with_arg_list(
7350
action, targets, cursorless_wrapper["extra_args"]
7451
)
75-
76-
77-
def on_ready():
78-
init_csv_and_watch_changes(
79-
"experimental/wrapper_snippets",
80-
{
81-
"wrapper_snippet": wrapper_snippets,
82-
},
83-
allow_unknown_values=True,
84-
default_list_name="wrapper_snippet",
85-
ctx=experimental_snippets_ctx,
86-
)
87-
88-
89-
app.register("ready", on_ready)

cursorless-talon/src/command.py

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

33
from talon import Module, actions, speech_system
44

5+
from .primitive_target import IMPLICIT_TARGET
6+
57
mod = Module()
68

79
last_phrase = None
@@ -47,17 +49,7 @@ def cursorless_single_target_command_no_wait(
4749
)
4850

4951
def cursorless_single_target_command_with_arg_list(
50-
action: str, target: str, args: list[Any]
51-
):
52-
"""Execute single-target cursorless command with argument list"""
53-
actions.user.cursorless_single_target_command(
54-
action,
55-
target,
56-
*args,
57-
)
58-
59-
def cursorless_single_target_command_with_arg_list(
60-
action: str, target: str, args: list[Any]
52+
action: str, target: dict, args: list[Any]
6153
):
6254
"""Execute single-target cursorless command with argument list"""
6355
actions.user.cursorless_single_target_command(
@@ -83,12 +75,23 @@ def cursorless_single_target_command_get(
8375
),
8476
)
8577

78+
def cursorless_implicit_target_command(
79+
action: str,
80+
arg1: Any = NotSet,
81+
arg2: Any = NotSet,
82+
arg3: Any = NotSet,
83+
):
84+
"""Execute cursorless command with implicit target"""
85+
actions.user.cursorless_single_target_command(
86+
action, IMPLICIT_TARGET, arg1, arg2, arg3
87+
)
88+
8689
def cursorless_multiple_target_command(
8790
action: str,
8891
targets: list[dict],
89-
arg1: any = NotSet,
90-
arg2: any = NotSet,
91-
arg3: any = NotSet,
92+
arg1: Any = NotSet,
93+
arg2: Any = NotSet,
94+
arg3: Any = NotSet,
9295
):
9396
"""Execute multi-target cursorless command"""
9497
actions.user.vscode_with_plugin_and_wait(
@@ -103,9 +106,9 @@ def cursorless_multiple_target_command(
103106
def cursorless_multiple_target_command_no_wait(
104107
action: str,
105108
targets: list[dict],
106-
arg1: any = NotSet,
107-
arg2: any = NotSet,
108-
arg3: any = NotSet,
109+
arg1: Any = NotSet,
110+
arg2: Any = NotSet,
111+
arg3: Any = NotSet,
109112
):
110113
"""Execute multi-target cursorless command"""
111114
actions.user.vscode_with_plugin(
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
app: vscode
2+
tag: user.cursorless_experimental_snippets
3+
-
4+
5+
{user.cursorless_insert_snippet_action} <user.cursorless_insertion_snippet>:
6+
user.cursorless_implicit_target_command(cursorless_insert_snippet_action, cursorless_insertion_snippet)
7+
8+
{user.cursorless_insert_snippet_action} <user.cursorless_insertion_snippet> <user.cursorless_positional_target>:
9+
user.cursorless_single_target_command(cursorless_insert_snippet_action, cursorless_positional_target, cursorless_insertion_snippet)
10+
11+
{user.cursorless_insert_snippet_action} {user.cursorless_insertion_snippet_single_phrase} <user.text> [{user.cursorless_phrase_terminator}]:
12+
user.cursorless_insert_snippet_with_phrase(cursorless_insert_snippet_action, cursorless_insertion_snippet_single_phrase, text)

cursorless-talon/src/snippets.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
from talon import Context, Module, actions, app
2+
3+
from .csv_overrides import init_csv_and_watch_changes
4+
5+
mod = Module()
6+
mod.list("cursorless_insert_snippet_action", desc="Cursorless insert snippet action")
7+
8+
mod.tag(
9+
"cursorless_experimental_snippets",
10+
desc="tag for enabling experimental snippet support",
11+
)
12+
13+
mod.list("cursorless_wrapper_snippet", desc="Cursorless wrapper snippet")
14+
mod.list(
15+
"cursorless_insertion_snippet_no_phrase",
16+
desc="Cursorless insertion snippets that don't accept a phrase",
17+
)
18+
mod.list(
19+
"cursorless_insertion_snippet_single_phrase",
20+
desc="Cursorless insertion snippet that can accept a single phrase",
21+
)
22+
mod.list("cursorless_phrase_terminator", "Contains term used to terminate a phrase")
23+
24+
25+
@mod.capture(
26+
rule="{user.cursorless_insertion_snippet_no_phrase} | {user.cursorless_insertion_snippet_single_phrase}"
27+
)
28+
def cursorless_insertion_snippet(m) -> str:
29+
try:
30+
return m.cursorless_insertion_snippet_no_phrase
31+
except AttributeError:
32+
pass
33+
34+
return m.cursorless_insertion_snippet_single_phrase.split(".")[0]
35+
36+
37+
experimental_snippets_ctx = Context()
38+
experimental_snippets_ctx.matches = r"""
39+
tag: user.cursorless_experimental_snippets
40+
"""
41+
42+
43+
# NOTE: Please do not change these dicts. Use the CSVs for customization.
44+
# See https://www.cursorless.org/docs/user/customization/
45+
wrapper_snippets = {
46+
"else": "ifElseStatement.alternative",
47+
"funk": "functionDeclaration.body",
48+
"if else": "ifElseStatement.consequence",
49+
"if": "ifStatement.consequence",
50+
"try": "tryCatchStatement.body",
51+
"link": "link.text",
52+
}
53+
54+
# NOTE: Please do not change these dicts. Use the CSVs for customization.
55+
# See https://www.cursorless.org/docs/user/customization/
56+
insertion_snippets_no_phrase = {
57+
"if": "ifStatement",
58+
"if else": "ifElseStatement",
59+
"try": "tryCatchStatement",
60+
}
61+
62+
# NOTE: Please do not change these dicts. Use the CSVs for customization.
63+
# See https://www.cursorless.org/docs/user/customization/
64+
insertion_snippets_single_phrase = {
65+
"funk": "functionDeclaration.name",
66+
"link": "link.text",
67+
}
68+
69+
70+
@mod.action_class
71+
class Actions:
72+
def cursorless_insert_snippet_with_phrase(
73+
action: str, snippet_description: str, text: str
74+
):
75+
"""Perform cursorless wrap action"""
76+
snippet_name, snippet_variable = snippet_description.split(".")
77+
actions.user.cursorless_implicit_target_command(
78+
action, snippet_name, {snippet_variable: text}
79+
)
80+
81+
82+
def on_ready():
83+
init_csv_and_watch_changes(
84+
"experimental/wrapper_snippets",
85+
{
86+
"wrapper_snippet": wrapper_snippets,
87+
},
88+
allow_unknown_values=True,
89+
default_list_name="wrapper_snippet",
90+
ctx=experimental_snippets_ctx,
91+
)
92+
init_csv_and_watch_changes(
93+
"experimental/insertion_snippets",
94+
{
95+
"insertion_snippet_no_phrase": insertion_snippets_no_phrase,
96+
},
97+
allow_unknown_values=True,
98+
default_list_name="insertion_snippet_no_phrase",
99+
ctx=experimental_snippets_ctx,
100+
)
101+
init_csv_and_watch_changes(
102+
"experimental/insertion_snippets_single_phrase",
103+
{
104+
"insertion_snippet_single_phrase": insertion_snippets_single_phrase,
105+
},
106+
allow_unknown_values=True,
107+
default_list_name="insertion_snippet_single_phrase",
108+
ctx=experimental_snippets_ctx,
109+
)
110+
init_csv_and_watch_changes(
111+
"experimental/miscellaneous",
112+
{
113+
"phrase_terminator": {"over": "phraseTerminator"},
114+
},
115+
ctx=experimental_snippets_ctx,
116+
)
117+
118+
119+
app.register("ready", on_ready)

docs/user/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,9 +412,9 @@ The rewrap command, mapped to `"repack"` by default, can be used to swap a given
412412

413413
See [paired delimiters](#paired-delimiters) for a list of possible wrappers.
414414

415-
#### \[experimental\] Wrap with snippet
415+
### \[experimental\] Snippets
416416

417-
See [experimental documentation](experimental/wrapper-snippets.md).
417+
See [experimental documentation](experimental/snippets.md).
418418

419419
### Show definition/reference/quick fix
420420

0 commit comments

Comments
 (0)