Skip to content

Commit eb35e6d

Browse files
committed
Support rich object to snippet action
1 parent 890c41c commit eb35e6d

File tree

18 files changed

+790
-60
lines changed

18 files changed

+790
-60
lines changed

cursorless-talon/src/actions/wrap.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from dataclasses import dataclass
2-
from typing import Literal
2+
from typing import Literal, Union
33

44
from talon import Module, actions
55

@@ -9,7 +9,7 @@
99
@dataclass
1010
class Wrapper:
1111
type: Literal["pairedDelimiter", "snippet"]
12-
extra_args: list[str]
12+
extra_args: list[Union[str, dict]]
1313

1414

1515
mod = Module()
@@ -30,7 +30,27 @@ def cursorless_wrapper(m) -> Wrapper:
3030
extra_args=[paired_delimiter_info.left, paired_delimiter_info.right],
3131
)
3232
except AttributeError:
33-
return Wrapper(type="snippet", extra_args=[m.cursorless_wrapper_snippet])
33+
snippet_name, placeholder_name = parse_snippet_location(
34+
m.cursorless_wrapper_snippet
35+
)
36+
return Wrapper(
37+
type="snippet",
38+
extra_args=[
39+
{
40+
"type": "named",
41+
"name": snippet_name,
42+
"variableName": placeholder_name,
43+
}
44+
],
45+
)
46+
47+
48+
# here is the above function in python:
49+
def parse_snippet_location(snippet_location: str) -> tuple[str, str]:
50+
[snippet_name, placeholder_name] = snippet_location.split(".")
51+
if snippet_name is None or placeholder_name is None:
52+
raise Exception("Snippet location missing '.'")
53+
return (snippet_name, placeholder_name)
3454

3555

3656
# Maps from (action_type, wrapper_type) to action name

cursorless-talon/src/command.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
mod = Module()
1313

1414
CURSORLESS_COMMAND_ID = "cursorless.command"
15-
CURSORLESS_COMMAND_VERSION = 4
15+
CURSORLESS_COMMAND_VERSION = 5
1616
last_phrase = None
1717

1818

cursorless-talon/src/cursorless_snippets.talon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ tag: user.cursorless
88
user.cursorless_single_target_command(cursorless_insert_snippet_action, cursorless_positional_target, cursorless_insertion_snippet)
99

1010
{user.cursorless_insert_snippet_action} {user.cursorless_insertion_snippet_single_phrase} <user.text> [{user.cursorless_phrase_terminator}]:
11-
user.cursorless_insert_snippet_with_phrase(cursorless_insert_snippet_action, cursorless_insertion_snippet_single_phrase, text)
11+
user.private_cursorless_insert_snippet_with_phrase(cursorless_insert_snippet_action, cursorless_insertion_snippet_single_phrase, text)

cursorless-talon/src/snippets.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Any, Optional
2+
13
from talon import Module, actions, app
24

35
from .csv_overrides import init_csv_and_watch_changes
@@ -65,7 +67,7 @@ def cursorless_insertion_snippet(m) -> str:
6567

6668
@mod.action_class
6769
class Actions:
68-
def cursorless_insert_snippet_with_phrase(
70+
def private_cursorless_insert_snippet_with_phrase(
6971
action: str, snippet_description: str, text: str
7072
):
7173
"""Perform cursorless wrap action"""
@@ -74,6 +76,51 @@ def cursorless_insert_snippet_with_phrase(
7476
action, snippet_name, {snippet_variable: text}
7577
)
7678

79+
def cursorless_insert_named_snippet(name: str, target: Optional[dict] = None):
80+
"""Inserts a named snippet"""
81+
82+
def cursorless_insert_custom_snippet(
83+
body: str, target: Optional[dict] = None, scope: Optional[str] = None
84+
):
85+
"""Inserts a custom snippet"""
86+
# Note that we convert scope to a proper scopeType; technically it is a scopeTypeType
87+
88+
def cursorless_wrap_with_named_snippet(name: str, variable_name: str, target: dict):
89+
"""Wrap target with a named snippet"""
90+
actions.user.cursorless_single_target_command_with_arg_list(
91+
"wrapWithSnippet",
92+
target,
93+
[
94+
{
95+
"type": "named",
96+
"name": name,
97+
"variableName": variable_name,
98+
}
99+
],
100+
)
101+
102+
def cursorless_wrap_with_custom_snippet(
103+
body: str,
104+
target: dict,
105+
variable_name: Optional[str] = None,
106+
scope: Optional[str] = None,
107+
):
108+
"""Wrap target with a custom snippet"""
109+
snippet_arg: dict[str, Any] = {
110+
"type": "custom",
111+
"body": body,
112+
}
113+
if scope is not None:
114+
snippet_arg["scopeType"] = {"type": scope}
115+
if variable_name is not None:
116+
snippet_arg["variableName"] = variable_name
117+
118+
actions.user.cursorless_single_target_command_with_arg_list(
119+
"wrapWithSnippet",
120+
target,
121+
[snippet_arg],
122+
)
123+
77124

78125
def on_ready():
79126
init_csv_and_watch_changes(

packages/common/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,9 @@ export * from "./types/command/ActionCommand";
6969
export * from "./types/command/legacy/CommandV0V1.types";
7070
export * from "./types/command/legacy/CommandV2.types";
7171
export * from "./types/command/legacy/CommandV3.types";
72+
export * from "./types/command/legacy/CommandV4.types";
7273
export * from "./types/command/legacy/targetDescriptorV2.types";
73-
export * from "./types/command/CommandV4.types";
74+
export * from "./types/command/CommandV5.types";
7475
export * from "./types/command/legacy/PartialTargetDescriptorV3.types";
7576
export * from "./types/CommandServerApi";
7677
export * from "./util/itertools";

packages/common/src/types/command/CommandV4.types.ts renamed to packages/common/src/types/command/CommandV5.types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import type { PartialTargetDescriptor } from "./PartialTargetDescriptor.types";
22
import type { ActionCommand } from "./ActionCommand";
33

4-
export interface CommandV4 {
4+
export interface CommandV5 {
55
/**
66
* The version number of the command API
77
*/
8-
version: 4;
8+
version: 5;
99

1010
/**
1111
* The spoken form of the command if issued from a voice command system
Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
import type { ActionCommand } from "./ActionCommand";
2+
import type { CommandV5 } from "./CommandV5.types";
23
import type { CommandV0, CommandV1 } from "./legacy/CommandV0V1.types";
34
import type { CommandV2 } from "./legacy/CommandV2.types";
45
import type { CommandV3 } from "./legacy/CommandV3.types";
5-
import type { CommandV4 } from "./CommandV4.types";
6+
import type { CommandV4 } from "./legacy/CommandV4.types";
67

78
export type CommandComplete = Required<Omit<CommandLatest, "spokenForm">> &
89
Pick<CommandLatest, "spokenForm"> & { action: Required<ActionCommand> };
910

10-
export const LATEST_VERSION = 4 as const;
11+
export const LATEST_VERSION = 5 as const;
1112

1213
export type CommandLatest = Command & {
1314
version: typeof LATEST_VERSION;
1415
};
1516

16-
export type Command = CommandV0 | CommandV1 | CommandV2 | CommandV3 | CommandV4;
17+
export type Command =
18+
| CommandV0
19+
| CommandV1
20+
| CommandV2
21+
| CommandV3
22+
| CommandV4
23+
| CommandV5;
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { PartialTargetDescriptorV4 } from "./PartialTargetDescriptorV4.types";
2+
3+
type ActionType =
4+
| "callAsFunction"
5+
| "clearAndSetSelection"
6+
| "copyToClipboard"
7+
| "cutToClipboard"
8+
| "deselect"
9+
| "editNew"
10+
| "editNewLineAfter"
11+
| "editNewLineBefore"
12+
| "executeCommand"
13+
| "extractVariable"
14+
| "findInWorkspace"
15+
| "foldRegion"
16+
| "followLink"
17+
| "generateSnippet"
18+
| "getText"
19+
| "highlight"
20+
| "indentLine"
21+
| "insertCopyAfter"
22+
| "insertCopyBefore"
23+
| "insertEmptyLineAfter"
24+
| "insertEmptyLineBefore"
25+
| "insertEmptyLinesAround"
26+
| "insertSnippet"
27+
| "moveToTarget"
28+
| "outdentLine"
29+
| "pasteFromClipboard"
30+
| "randomizeTargets"
31+
| "remove"
32+
| "rename"
33+
| "replace"
34+
| "replaceWithTarget"
35+
| "revealDefinition"
36+
| "revealTypeDefinition"
37+
| "reverseTargets"
38+
| "rewrapWithPairedDelimiter"
39+
| "scrollToBottom"
40+
| "scrollToCenter"
41+
| "scrollToTop"
42+
| "setSelection"
43+
| "setSelectionAfter"
44+
| "setSelectionBefore"
45+
| "showDebugHover"
46+
| "showHover"
47+
| "showQuickFix"
48+
| "showReferences"
49+
| "sortTargets"
50+
| "swapTargets"
51+
| "toggleLineBreakpoint"
52+
| "toggleLineComment"
53+
| "unfoldRegion"
54+
| "wrapWithPairedDelimiter"
55+
| "wrapWithSnippet";
56+
57+
export interface ActionCommandV4 {
58+
/**
59+
* The action to run
60+
*/
61+
name: ActionType;
62+
63+
/**
64+
* A list of arguments expected by the given action.
65+
*/
66+
args?: unknown[];
67+
}
68+
69+
export interface CommandV4 {
70+
/**
71+
* The version number of the command API
72+
*/
73+
version: 4;
74+
75+
/**
76+
* The spoken form of the command if issued from a voice command system
77+
*/
78+
spokenForm?: string;
79+
80+
/**
81+
* If the command is issued from a voice command system, this boolean indicates
82+
* whether we should use the pre phrase snapshot. Only set this to true if the
83+
* voice command system issues a pre phrase signal at the start of every
84+
* phrase.
85+
*/
86+
usePrePhraseSnapshot: boolean;
87+
88+
action: ActionCommandV4;
89+
90+
/**
91+
* A list of targets expected by the action. Inference will be run on the
92+
* targets
93+
*/
94+
targets: PartialTargetDescriptorV4[];
95+
}

0 commit comments

Comments
 (0)