Skip to content

Commit 7d13e77

Browse files
Support forced language mode for snippets
1 parent 0128a03 commit 7d13e77

File tree

8 files changed

+198
-107
lines changed

8 files changed

+198
-107
lines changed

cursorless-talon/src/command.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ def make_serializable(value: Any) -> Any:
9494
return {k: make_serializable(v) for k, v in value.items()}
9595
if isinstance(value, list):
9696
return [make_serializable(v) for v in value]
97+
if isinstance(value, staticmethod):
98+
return None
9799
if dataclasses.is_dataclass(value):
98100
items = {
99101
**{

cursorless-talon/src/cursorless.talon

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ tag: user.cursorless
2424
<user.cursorless_wrapper_paired_delimiter> {user.cursorless_wrap_action} <user.cursorless_target>:
2525
user.private_cursorless_wrap_with_paired_delimiter(cursorless_wrap_action, cursorless_target, cursorless_wrapper_paired_delimiter)
2626

27+
{user.cursorless_insert_snippet_action} {user.snippet} <user.cursorless_destination>:
28+
user.private_cursorless_insert_community_snippet(snippet, cursorless_destination)
29+
30+
{user.snippet_wrapper} {user.cursorless_wrap_action} <user.cursorless_target>:
31+
user.private_cursorless_wrap_with_community_snippet(snippet_wrapper, cursorless_target)
32+
2733
{user.cursorless_show_scope_visualizer} <user.cursorless_scope_type> [{user.cursorless_visualization_type}]:
2834
user.private_cursorless_show_scope_visualizer(cursorless_scope_type, cursorless_visualization_type or "content")
2935
{user.cursorless_hide_scope_visualizer}:

cursorless-talon/src/snippets.talon

Lines changed: 0 additions & 12 deletions
This file was deleted.

cursorless-talon/src/snippet_types.py renamed to cursorless-talon/src/snippets/snippet_types.py

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,41 @@
11
from dataclasses import dataclass
22

3-
from .targets.target_types import CursorlessDestination, CursorlessTarget
3+
from ..targets.target_types import CursorlessDestination, CursorlessTarget
4+
5+
# Scope types
46

57

68
@dataclass
79
class ScopeType:
810
type: str
911

1012

13+
def to_scope_types(scope_types: str | list[str] | None) -> list[ScopeType] | None:
14+
if isinstance(scope_types, str):
15+
return [ScopeType(scope_types)]
16+
elif scope_types is not None:
17+
return [ScopeType(st) for st in scope_types]
18+
return None
19+
20+
21+
# Community types
22+
23+
24+
@dataclass
25+
class CommunityInsertionSnippet:
26+
body: str
27+
languages: list[str] | None = None
28+
scopes: list[str] | None = None
29+
30+
31+
@dataclass
32+
class CommunityWrapperSnippet:
33+
body: str
34+
variable_name: str
35+
languages: list[str] | None
36+
scope: str | None
37+
38+
1139
# Insertion snippets
1240

1341

@@ -19,6 +47,19 @@ class CustomInsertionSnippet:
1947
languages: list[str] | None
2048
substitutions: dict[str, str] | None
2149

50+
@staticmethod
51+
def create(
52+
snippet: CommunityInsertionSnippet,
53+
substitutions: dict[str, str] | None,
54+
):
55+
return CustomInsertionSnippet(
56+
snippet.body,
57+
to_scope_types(snippet.scopes),
58+
# languages will be missing if the user has an older version of community
59+
snippet.languages if hasattr(snippet, "languages") else None,
60+
substitutions=substitutions,
61+
)
62+
2263

2364
@dataclass
2465
class ListInsertionSnippet:
@@ -45,6 +86,16 @@ class CustomWrapperSnippet:
4586
scopeType: ScopeType | None
4687
languages: list[str] | None
4788

89+
@staticmethod
90+
def create(snippet: CommunityWrapperSnippet):
91+
return CustomWrapperSnippet(
92+
snippet.body,
93+
snippet.variable_name,
94+
ScopeType(snippet.scope) if snippet.scope else None,
95+
# languages will be missing if the user has an older version of community
96+
snippet.languages if hasattr(snippet, "languages") else None,
97+
)
98+
4899

49100
@dataclass
50101
class ListWrapperSnippet:
@@ -57,21 +108,3 @@ class WrapperSnippetAction:
57108
name = "wrapWithSnippet"
58109
snippetDescription: CustomWrapperSnippet | ListWrapperSnippet
59110
target: CursorlessTarget
60-
61-
62-
# Community types
63-
64-
65-
@dataclass
66-
class CommunityInsertionSnippet:
67-
body: str
68-
languages: list[str] | None = None
69-
scopes: list[str] | None = None
70-
71-
72-
@dataclass
73-
class CommunityWrapperSnippet:
74-
body: str
75-
variable_name: str
76-
languages: list[str] | None
77-
scope: str | None

cursorless-talon/src/snippets.py renamed to cursorless-talon/src/snippets/snippets.py

Lines changed: 61 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,35 @@
22

33
from talon import Context, Module, actions
44

5+
from ..targets.target_types import (
6+
CursorlessDestination,
7+
CursorlessTarget,
8+
ImplicitDestination,
9+
)
510
from .snippet_types import (
6-
CommunityInsertionSnippet,
7-
CommunityWrapperSnippet,
811
CustomInsertionSnippet,
912
CustomWrapperSnippet,
1013
InsertSnippetAction,
1114
ListInsertionSnippet,
1215
ListWrapperSnippet,
1316
ScopeType,
1417
WrapperSnippetAction,
18+
to_scope_types,
1519
)
16-
from .targets.target_types import (
17-
CursorlessDestination,
18-
CursorlessTarget,
19-
ImplicitDestination,
20+
from .snippets_get import (
21+
get_custom_insertion_snippets,
22+
get_custom_wrapper_snippet,
23+
get_insertion_snippet,
24+
get_wrapper_snippet,
2025
)
2126

2227
mod = Module()
2328
ctx = Context()
2429

30+
ctx.matches = r"""
31+
not tag: user.code_language_forced
32+
"""
33+
2534
mod.list("cursorless_insert_snippet_action", desc="Cursorless insert snippet action")
2635

2736

@@ -47,50 +56,63 @@ def insert_community_snippet(
4756
name: str,
4857
substitutions: dict[str, str] | None,
4958
destination: CursorlessDestination,
59+
use_list: bool,
5060
):
51-
snippets: list[CommunityInsertionSnippet] = get_insertion_snippets(name)
52-
snippet = ListInsertionSnippet(
53-
substitutions,
54-
[
55-
CustomInsertionSnippet(
56-
s.body,
57-
to_scope_types(s.scopes),
58-
# languages will be missing if the user has an older version of community
59-
s.languages if hasattr(s, "languages") else None,
60-
substitutions=None,
61-
)
62-
for s in snippets
63-
],
61+
snippet = (
62+
get_insertion_snippet(name, substitutions)
63+
if use_list
64+
else get_custom_insertion_snippets(name, substitutions)
6465
)
6566
insert_snippet(snippet, destination)
6667

6768

68-
def insert_community_wrapper_snippet(name: str, target: CursorlessTarget):
69-
snippets: list[CommunityWrapperSnippet] = get_wrapper_snippets(name)
70-
snippet = ListWrapperSnippet(
71-
[
72-
CustomWrapperSnippet(
73-
s.body,
74-
s.variable_name,
75-
ScopeType(s.scope) if s.scope else None,
76-
s.languages if hasattr(s, "languages") else None,
77-
)
78-
for s in snippets
79-
],
69+
def wrap_with_community_snippet(
70+
name: str,
71+
target: CursorlessTarget,
72+
use_list: bool,
73+
):
74+
snippet = (
75+
get_wrapper_snippet(name) if use_list else get_custom_wrapper_snippet(name)
8076
)
8177
wrap_with_snippet(snippet, target)
8278

8379

80+
# These actions use list snippets since no language mode is forced
81+
82+
8483
@ctx.action_class("user")
8584
class UserActions:
8685
def insert_snippet_by_name(
8786
name: str, # pyright: ignore [reportGeneralTypeIssues]
87+
# Don't add optional; We need to match the type in community
8888
substitutions: dict[str, str] = None,
8989
):
9090
insert_community_snippet(
9191
name,
9292
substitutions,
9393
ImplicitDestination(),
94+
use_list=True,
95+
)
96+
97+
def private_cursorless_insert_community_snippet(
98+
name: str, # pyright: ignore [reportGeneralTypeIssues]
99+
destination: CursorlessDestination,
100+
):
101+
insert_community_snippet(
102+
name,
103+
substitutions=None,
104+
destination=destination,
105+
use_list=True,
106+
)
107+
108+
def private_cursorless_wrap_with_community_snippet(
109+
name: str, # pyright: ignore [reportGeneralTypeIssues]
110+
target: CursorlessTarget,
111+
):
112+
wrap_with_community_snippet(
113+
name,
114+
target,
115+
use_list=True,
94116
)
95117

96118

@@ -125,6 +147,8 @@ def cursorless_wrap_with_snippet(
125147
)
126148
wrap_with_snippet(snippet, target)
127149

150+
# These actions use a single custom snippets since a language mode is forced
151+
128152
def private_cursorless_insert_community_snippet(
129153
name: str, # pyright: ignore [reportGeneralTypeIssues]
130154
destination: CursorlessDestination,
@@ -134,39 +158,16 @@ def private_cursorless_insert_community_snippet(
134158
name,
135159
substitutions=None,
136160
destination=destination,
161+
use_list=False,
137162
)
138163

139164
def private_cursorless_wrap_with_community_snippet(
140165
name: str, # pyright: ignore [reportGeneralTypeIssues]
141166
target: CursorlessTarget,
142167
):
143168
"""Cursorless: Wrap target with community snippet <name>"""
144-
insert_community_wrapper_snippet(name, target)
145-
146-
147-
def to_scope_types(scope_types: str | list[str] | None) -> list[ScopeType] | None:
148-
if isinstance(scope_types, str):
149-
return [ScopeType(scope_types)]
150-
elif scope_types is not None:
151-
return [ScopeType(st) for st in scope_types]
152-
return None
153-
154-
155-
def get_insertion_snippets(name: str) -> list[CommunityInsertionSnippet]:
156-
try:
157-
return actions.user.get_insertion_snippets(name)
158-
except Exception as ex:
159-
if isinstance(ex, KeyError):
160-
snippet = actions.user.get_insertion_snippet(name)
161-
return [snippet]
162-
raise
163-
164-
165-
def get_wrapper_snippets(name: str) -> list[CommunityWrapperSnippet]:
166-
try:
167-
return actions.user.get_wrapper_snippets(name)
168-
except Exception as ex:
169-
if isinstance(ex, KeyError):
170-
snippet = actions.user.get_wrapper_snippet(name)
171-
return [snippet]
172-
raise
169+
wrap_with_community_snippet(
170+
name,
171+
target,
172+
use_list=False,
173+
)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from talon import actions
2+
3+
from .snippet_types import (
4+
CommunityInsertionSnippet,
5+
CommunityWrapperSnippet,
6+
CustomInsertionSnippet,
7+
CustomWrapperSnippet,
8+
ListInsertionSnippet,
9+
ListWrapperSnippet,
10+
)
11+
12+
13+
def get_insertion_snippet(
14+
name: str,
15+
substitutions: dict[str, str] | None,
16+
) -> ListInsertionSnippet | CustomInsertionSnippet:
17+
try:
18+
return get_list_insertion_snippets(name, substitutions)
19+
except Exception as ex:
20+
if isinstance(ex, KeyError):
21+
return get_custom_insertion_snippets(name, substitutions)
22+
raise
23+
24+
25+
def get_list_insertion_snippets(
26+
name: str,
27+
substitutions: dict[str, str] | None,
28+
) -> ListInsertionSnippet:
29+
snippets: list[CommunityInsertionSnippet] = actions.user.get_insertion_snippets(
30+
name
31+
)
32+
return ListInsertionSnippet(
33+
substitutions,
34+
[CustomInsertionSnippet.create(s, substitutions=None) for s in snippets],
35+
)
36+
37+
38+
def get_custom_insertion_snippets(
39+
name: str,
40+
substitutions: dict[str, str] | None,
41+
) -> CustomInsertionSnippet:
42+
snippet: CommunityInsertionSnippet = actions.user.get_insertion_snippet(name)
43+
return CustomInsertionSnippet.create(snippet, substitutions)
44+
45+
46+
def get_wrapper_snippet(name: str) -> ListWrapperSnippet | CustomWrapperSnippet:
47+
try:
48+
return get_list_wrapper_snippet(name)
49+
except Exception as ex:
50+
if isinstance(ex, KeyError):
51+
return get_custom_wrapper_snippet(name)
52+
raise
53+
54+
55+
def get_list_wrapper_snippet(name: str) -> ListWrapperSnippet:
56+
snippets: list[CommunityWrapperSnippet] = actions.user.get_wrapper_snippets(name)
57+
return ListWrapperSnippet(
58+
[CustomWrapperSnippet.create(s) for s in snippets],
59+
)
60+
61+
62+
def get_custom_wrapper_snippet(name: str) -> CustomWrapperSnippet:
63+
snippet: CommunityWrapperSnippet = actions.user.get_wrapper_snippet(name)
64+
return CustomWrapperSnippet.create(snippet)

0 commit comments

Comments
 (0)