|
1 |
| -from pathlib import Path |
2 | 1 | from typing import Any
|
3 | 2 |
|
4 |
| -from talon import Module, actions, cron, fs |
5 |
| - |
6 |
| -from ..csv_overrides import init_csv_and_watch_changes |
| 3 | +from talon import Module |
7 | 4 |
|
8 | 5 | mod = Module()
|
9 | 6 |
|
10 |
| -mod.list("cursorless_hat_color", desc="Supported hat colors for cursorless") |
11 |
| -mod.list("cursorless_hat_shape", desc="Supported hat shapes for cursorless") |
12 |
| -mod.list("cursorless_special_mark", desc="Cursorless special marks") |
13 |
| -mod.list( |
14 |
| - "cursorless_unknown_symbol", |
15 |
| - "This list contains the term that is used to refer to any unknown symbol", |
16 |
| -) |
17 |
| - |
18 |
| -# Maps from the id we use in the spoken form csv to the modifier type |
19 |
| -# expected by Cursorless extension |
20 |
| -special_marks = { |
21 |
| - "currentSelection": "cursor", |
22 |
| - "previousTarget": "that", |
23 |
| - "previousSource": "source", |
24 |
| - "nothing": "nothing", |
25 |
| -} |
26 |
| - |
27 |
| - |
28 |
| -@mod.capture(rule="<user.any_alphanumeric_key> | {user.cursorless_unknown_symbol}") |
29 |
| -def cursorless_grapheme(m) -> str: |
30 |
| - try: |
31 |
| - return m.any_alphanumeric_key |
32 |
| - except AttributeError: |
33 |
| - # NB: This represents unknown char in Unicode. It will be translated |
34 |
| - # to "[unk]" by Cursorless extension. |
35 |
| - return "\uFFFD" |
36 |
| - |
37 |
| - |
38 |
| -@mod.capture( |
39 |
| - rule="[{user.cursorless_hat_color} | gray] [{user.cursorless_hat_shape}] <user.cursorless_grapheme>" |
40 |
| -) |
41 |
| -def cursorless_decorated_symbol(m) -> dict[str, Any]: |
42 |
| - """A decorated symbol""" |
43 |
| - if m[0] == "gray": |
44 |
| - actions.app.notify( |
45 |
| - "The color 'gray' is the default and doesn't need to be spoken out loud. Just say eg 'take air' instead of 'take gray air'" |
46 |
| - ) |
47 |
| - hat_color = getattr(m, "cursorless_hat_color", "default") |
48 |
| - try: |
49 |
| - hat_style_name = f"{hat_color}-{m.cursorless_hat_shape}" |
50 |
| - except AttributeError: |
51 |
| - hat_style_name = hat_color |
52 |
| - return { |
53 |
| - "type": "decoratedSymbol", |
54 |
| - "symbolColor": hat_style_name, |
55 |
| - "character": m.cursorless_grapheme, |
56 |
| - } |
57 |
| - |
58 | 7 |
|
59 | 8 | @mod.capture(
|
60 | 9 | rule=(
|
61 | 10 | "<user.cursorless_decorated_symbol> | "
|
62 |
| - "{user.cursorless_special_mark} |" |
| 11 | + "<user.cursorless_simple_mark> |" |
63 | 12 | "<user.cursorless_line_number>" # row (ie absolute mod 100), up, down
|
64 | 13 | )
|
65 | 14 | )
|
66 | 15 | def cursorless_mark(m) -> dict[str, Any]:
|
67 |
| - try: |
68 |
| - return m.cursorless_decorated_symbol |
69 |
| - except AttributeError: |
70 |
| - pass |
71 |
| - try: |
72 |
| - return {"type": special_marks[m.cursorless_special_mark]} |
73 |
| - except AttributeError: |
74 |
| - pass |
75 |
| - return m.cursorless_line_number |
76 |
| - |
77 |
| - |
78 |
| -DEFAULT_COLOR_ENABLEMENT = { |
79 |
| - "blue": True, |
80 |
| - "green": True, |
81 |
| - "red": True, |
82 |
| - "pink": True, |
83 |
| - "yellow": True, |
84 |
| - "userColor1": False, |
85 |
| - "userColor2": False, |
86 |
| -} |
87 |
| - |
88 |
| -DEFAULT_SHAPE_ENABLEMENT = { |
89 |
| - "ex": False, |
90 |
| - "fox": False, |
91 |
| - "wing": False, |
92 |
| - "hole": False, |
93 |
| - "frame": False, |
94 |
| - "curve": False, |
95 |
| - "eye": False, |
96 |
| - "play": False, |
97 |
| - "bolt": False, |
98 |
| - "crosshairs": False, |
99 |
| -} |
100 |
| - |
101 |
| -# Fall back to full enablement in case of error reading settings file |
102 |
| -# NB: This won't actually enable all the shapes and colors extension-side. |
103 |
| -# It'll just make it so that the user can say them whether or not they are enabled |
104 |
| -FALLBACK_SHAPE_ENABLEMENT = { |
105 |
| - "ex": True, |
106 |
| - "fox": True, |
107 |
| - "wing": True, |
108 |
| - "hole": True, |
109 |
| - "frame": True, |
110 |
| - "curve": True, |
111 |
| - "eye": True, |
112 |
| - "play": True, |
113 |
| - "bolt": True, |
114 |
| - "crosshairs": True, |
115 |
| -} |
116 |
| -FALLBACK_COLOR_ENABLEMENT = DEFAULT_COLOR_ENABLEMENT |
117 |
| - |
118 |
| -unsubscribe_hat_styles = None |
119 |
| - |
120 |
| - |
121 |
| -def setup_hat_styles_csv(hat_colors: dict, hat_shapes: dict): |
122 |
| - global unsubscribe_hat_styles |
123 |
| - |
124 |
| - ( |
125 |
| - color_enablement_settings, |
126 |
| - is_color_error, |
127 |
| - ) = actions.user.vscode_get_setting_with_fallback( |
128 |
| - "cursorless.hatEnablement.colors", |
129 |
| - default_value={}, |
130 |
| - fallback_value=FALLBACK_COLOR_ENABLEMENT, |
131 |
| - fallback_message="Error finding color enablement; falling back to full enablement", |
132 |
| - ) |
133 |
| - |
134 |
| - ( |
135 |
| - shape_enablement_settings, |
136 |
| - is_shape_error, |
137 |
| - ) = actions.user.vscode_get_setting_with_fallback( |
138 |
| - "cursorless.hatEnablement.shapes", |
139 |
| - default_value={}, |
140 |
| - fallback_value=FALLBACK_SHAPE_ENABLEMENT, |
141 |
| - fallback_message="Error finding shape enablement; falling back to full enablement", |
142 |
| - ) |
143 |
| - |
144 |
| - color_enablement = { |
145 |
| - **DEFAULT_COLOR_ENABLEMENT, |
146 |
| - **color_enablement_settings, |
147 |
| - } |
148 |
| - shape_enablement = { |
149 |
| - **DEFAULT_SHAPE_ENABLEMENT, |
150 |
| - **shape_enablement_settings, |
151 |
| - } |
152 |
| - |
153 |
| - active_hat_colors = { |
154 |
| - spoken_form: value |
155 |
| - for spoken_form, value in hat_colors.items() |
156 |
| - if color_enablement[value] |
157 |
| - } |
158 |
| - active_hat_shapes = { |
159 |
| - spoken_form: value |
160 |
| - for spoken_form, value in hat_shapes.items() |
161 |
| - if shape_enablement[value] |
162 |
| - } |
163 |
| - |
164 |
| - if unsubscribe_hat_styles is not None: |
165 |
| - unsubscribe_hat_styles() |
166 |
| - |
167 |
| - unsubscribe_hat_styles = init_csv_and_watch_changes( |
168 |
| - "hat_styles.csv", |
169 |
| - { |
170 |
| - "hat_color": active_hat_colors, |
171 |
| - "hat_shape": active_hat_shapes, |
172 |
| - }, |
173 |
| - [*hat_colors.values(), *hat_shapes.values()], |
174 |
| - no_update_file=is_shape_error or is_color_error, |
175 |
| - ) |
176 |
| - |
177 |
| - if is_shape_error or is_color_error: |
178 |
| - actions.app.notify("Error reading vscode settings. Restart talon; see log") |
179 |
| - |
180 |
| - |
181 |
| -fast_reload_job = None |
182 |
| -slow_reload_job = None |
183 |
| - |
184 |
| - |
185 |
| -def init_marks(hat_colors: dict, hat_shapes: dict): |
186 |
| - setup_hat_styles_csv(hat_colors, hat_shapes) |
187 |
| - |
188 |
| - vscode_settings_path: Path = actions.user.vscode_settings_path().resolve() |
189 |
| - |
190 |
| - def on_watch(path, flags): |
191 |
| - global fast_reload_job, slow_reload_job |
192 |
| - cron.cancel(fast_reload_job) |
193 |
| - cron.cancel(slow_reload_job) |
194 |
| - fast_reload_job = cron.after( |
195 |
| - "500ms", lambda: setup_hat_styles_csv(hat_colors, hat_shapes) |
196 |
| - ) |
197 |
| - slow_reload_job = cron.after( |
198 |
| - "10s", lambda: setup_hat_styles_csv(hat_colors, hat_shapes) |
199 |
| - ) |
200 |
| - |
201 |
| - fs.watch(str(vscode_settings_path), on_watch) |
202 |
| - |
203 |
| - def unsubscribe(): |
204 |
| - fs.unwatch(str(vscode_settings_path), on_watch) |
205 |
| - if unsubscribe_hat_styles is not None: |
206 |
| - unsubscribe_hat_styles() |
207 |
| - |
208 |
| - return unsubscribe |
| 16 | + return m[0] |
0 commit comments