Skip to content

Commit e50ded4

Browse files
committed
Support html and old cheatsheet side by side
1 parent 17c6ee7 commit e50ded4

File tree

10 files changed

+322
-162
lines changed

10 files changed

+322
-162
lines changed

cursorless-talon/src/cheatsheet/cheat_sheet.py

Lines changed: 8 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import math
2-
import tempfile
32
import webbrowser
4-
from pathlib import Path
53
from typing import Optional
64

75
from talon import Module, actions, cron, skia, ui
@@ -291,68 +289,18 @@ def draw_value(self, canvas, text):
291289
self.w = max(self.w, rect.width)
292290

293291

294-
cheatsheet_out_dir = Path(tempfile.mkdtemp())
295-
296-
297292
@mod.action_class
298293
class Actions:
299294
def cursorless_cheat_sheet_toggle():
300295
"""Toggle cursorless cheat sheet"""
301-
cheatsheet_out_path = cheatsheet_out_dir / "cheatsheet.html"
302-
actions.user.vscode_with_plugin_and_wait(
303-
"cursorless.showCheatsheet",
304-
actions.user.cursorless_cheat_sheet_get_json(),
305-
str(cheatsheet_out_path),
306-
)
307-
webbrowser.open(cheatsheet_out_path.as_uri())
308-
309-
def cursorless_cheat_sheet_get_json():
310-
"""Get cursorless cheat sheet json"""
311-
return {
312-
"sections": [
313-
{
314-
"name": "Actions",
315-
"id": "actions",
316-
"items": get_actions(),
317-
},
318-
{
319-
"name": "Scopes",
320-
"id": "scopes",
321-
"items": get_scopes(),
322-
},
323-
{
324-
"name": "Paired delimiters",
325-
"id": "paired-delimiters",
326-
"items": get_lists(
327-
[
328-
"wrapper_only_paired_delimiter",
329-
"wrapper_selectable_paired_delimiter",
330-
"selectable_only_paired_delimiter",
331-
]
332-
),
333-
},
334-
{
335-
"name": "Special marks",
336-
"id": "special-marks",
337-
"items": get_list("special_mark"),
338-
},
339-
{
340-
"name": "Positions",
341-
"id": "positions",
342-
"items": get_list("position"),
343-
},
344-
{
345-
"name": "Colors",
346-
"id": "colors",
347-
"items": get_list("hat_color"),
348-
},
349-
{
350-
"name": "Shapes",
351-
"id": "shapes",
352-
"items": get_list("hat_shape"),
353-
},
354-
]
355-
}
296+
global cheat_sheet
297+
if cheat_sheet:
298+
actions.mode.disable("user.cursorless_cheat_sheet")
299+
cheat_sheet.close()
300+
cheat_sheet = None
301+
else:
302+
cheat_sheet = CheatSheet()
303+
actions.mode.enable("user.cursorless_cheat_sheet")
356304

357305
def cursorless_open_instructions():
358306
"""Open web page with cursorless instructions"""

cursorless-talon/src/cheatsheet/get_list.py

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,38 +10,34 @@ def get_list(name, descriptions=None):
1010
descriptions = {}
1111

1212
items = get_raw_list(name)
13-
item_dict = items if isinstance(items, dict) else {item: item for item in items}
14-
15-
return make_dict_readable(name, item_dict, descriptions)
13+
if isinstance(items, dict):
14+
make_dict_readable(items, descriptions)
15+
return items
1616

1717

1818
def get_lists(names: list[str], descriptions=None):
19-
20-
return [item for name in names for item in get_list(name, descriptions)]
19+
items = sorted(
20+
item for name in names for item in get_list(name, descriptions).items()
21+
)
22+
return {key: value for key, value in items}
2123

2224

2325
def get_raw_list(name):
2426
cursorless_list_name = get_cursorless_list_name(name)
2527
return registry.lists[cursorless_list_name][0].copy()
2628

2729

28-
def make_dict_readable(type: str, dict, descriptions=None):
30+
def make_dict_readable(dict, descriptions=None):
2931
if descriptions is None:
3032
descriptions = {}
3133

32-
return [
33-
{
34-
"identifier": value,
35-
"type": type,
36-
"spokenForms": [
37-
{
38-
"spokenForm": key,
39-
"description": descriptions.get(value, make_readable(value)),
40-
}
41-
],
42-
}
43-
for key, value in dict.items()
44-
]
34+
for k in dict:
35+
desc = dict[k]
36+
if desc in descriptions:
37+
desc = descriptions[desc]
38+
else:
39+
desc = make_readable(desc)
40+
dict[k] = desc
4541

4642

4743
def make_readable(text):

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

Lines changed: 16 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -31,84 +31,21 @@ def get_actions():
3131
get_raw_list("source_destination_connective").keys()
3232
)[0]
3333

34-
return [
35-
*make_dict_readable(
36-
"action",
37-
simple_actions,
38-
{
39-
"callAsFunction": "Call T on S",
40-
},
41-
),
34+
make_dict_readable(
35+
simple_actions,
4236
{
43-
"identifier": "replaceWithTarget",
44-
"type": "action",
45-
"spokenForms": [
46-
{
47-
"spokenForm": f"{complex_actions['replaceWithTarget']} <T1> {source_destination_connective} <T2>",
48-
"description": "Replace T2 with T1",
49-
},
50-
{
51-
"spokenForm": f"{complex_actions['replaceWithTarget']} <T>",
52-
"description": "Replace S with T",
53-
},
54-
],
37+
"callAsFunction": "Call T on S",
5538
},
56-
{
57-
"identifier": "moveToTarget",
58-
"type": "action",
59-
"spokenForms": [
60-
{
61-
"spokenForm": f"{complex_actions['moveToTarget']} <T1> {source_destination_connective} <T2>",
62-
"description": "Move T1 to T2",
63-
},
64-
{
65-
"spokenForm": f"{complex_actions['moveToTarget']} <T>",
66-
"description": "Move T to S",
67-
},
68-
],
69-
},
70-
{
71-
"identifier": "swapTargets",
72-
"type": "action",
73-
"spokenForms": [
74-
{
75-
"spokenForm": f"{complex_actions['swapTargets']} <T1> {swap_connective} <T2>",
76-
"description": "Swap T1 with T2",
77-
},
78-
{
79-
"spokenForm": f"{complex_actions['swapTargets']} {swap_connective} <T>",
80-
"description": "Swap S with T",
81-
},
82-
],
83-
},
84-
{
85-
"identifier": "applyFormatter",
86-
"type": "action",
87-
"spokenForms": [
88-
{
89-
"spokenForm": f"{complex_actions['applyFormatter']} <F> at <T>",
90-
"description": "Reformat T as F",
91-
}
92-
],
93-
},
94-
{
95-
"identifier": "wrapWithPairedDelimiter",
96-
"type": "action",
97-
"spokenForms": [
98-
{
99-
"spokenForm": f"<P> {complex_actions['wrapWithPairedDelimiter']} <T>",
100-
"description": "Wrap T with P",
101-
}
102-
],
103-
},
104-
{
105-
"identifier": "rewrap",
106-
"type": "action",
107-
"spokenForms": [
108-
{
109-
"spokenForm": f"<P> {complex_actions['rewrap']} <T>",
110-
"description": "Rewrap T with P",
111-
}
112-
],
113-
},
114-
]
39+
)
40+
return {
41+
**simple_actions,
42+
f"{complex_actions['replaceWithTarget']} <T1> {source_destination_connective} <T2>": "Replace T2 with T1",
43+
f"{complex_actions['replaceWithTarget']} <T>": "Replace S with T",
44+
f"{complex_actions['moveToTarget']} <T1> {source_destination_connective} <T2>": "Move T1 to T2",
45+
f"{complex_actions['moveToTarget']} <T>": "Move T to S",
46+
f"{complex_actions['swapTargets']} <T1> {swap_connective} <T2>": "Swap T1 with T2",
47+
f"{complex_actions['swapTargets']} {swap_connective} <T>": "Swap S with T",
48+
f"{complex_actions['applyFormatter']} <F> at <T>": "Reformat T as F",
49+
f"<P> {complex_actions['wrapWithPairedDelimiter']} <T>": "Wrap T with P",
50+
f"<P> {complex_actions['rewrap']} <T>": "Rewrap T with P",
51+
}

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33

44
def get_scopes():
5-
return get_lists(
6-
["scope_type", "subtoken_scope_type"],
7-
{"argumentOrParameter": "Argument"},
8-
)
5+
return {
6+
**get_lists(
7+
["scope_type", "subtoken_scope_type"],
8+
{"argumentOrParameter": "Argument"},
9+
),
10+
"<P>": "Paired delimiter",
11+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import tempfile
2+
import webbrowser
3+
from pathlib import Path
4+
5+
from talon import Module, actions
6+
7+
from .get_list import get_list, get_lists
8+
from .sections.actions import get_actions
9+
from .sections.scopes import get_scopes
10+
11+
mod = Module()
12+
13+
cheatsheet_out_dir = Path(tempfile.mkdtemp())
14+
15+
16+
@mod.action_class
17+
class Actions:
18+
def cursorless_cheat_sheet_show_html():
19+
"""Show new cursorless html cheat sheet"""
20+
cheatsheet_out_path = cheatsheet_out_dir / "cheatsheet.html"
21+
actions.user.vscode_with_plugin_and_wait(
22+
"cursorless.showCheatsheet",
23+
actions.user.cursorless_cheat_sheet_get_json(),
24+
str(cheatsheet_out_path),
25+
)
26+
webbrowser.open(cheatsheet_out_path.as_uri())
27+
28+
def cursorless_cheat_sheet_get_json():
29+
"""Get cursorless cheat sheet json"""
30+
return {
31+
"sections": [
32+
{
33+
"name": "Actions",
34+
"id": "actions",
35+
"items": get_actions(),
36+
},
37+
{
38+
"name": "Scopes",
39+
"id": "scopes",
40+
"items": get_scopes(),
41+
},
42+
{
43+
"name": "Paired delimiters",
44+
"id": "paired-delimiters",
45+
"items": get_lists(
46+
[
47+
"wrapper_only_paired_delimiter",
48+
"wrapper_selectable_paired_delimiter",
49+
"selectable_only_paired_delimiter",
50+
]
51+
),
52+
},
53+
{
54+
"name": "Special marks",
55+
"id": "special-marks",
56+
"items": get_list("special_mark"),
57+
},
58+
{
59+
"name": "Positions",
60+
"id": "positions",
61+
"items": get_list("position"),
62+
},
63+
{
64+
"name": "Colors",
65+
"id": "colors",
66+
"items": get_list("hat_color"),
67+
},
68+
{
69+
"name": "Shapes",
70+
"id": "shapes",
71+
"items": get_list("hat_shape"),
72+
},
73+
]
74+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import re
2+
3+
from talon import registry
4+
5+
from ..conventions import get_cursorless_list_name
6+
7+
8+
def get_list(name, descriptions=None):
9+
if descriptions is None:
10+
descriptions = {}
11+
12+
items = get_raw_list(name)
13+
item_dict = items if isinstance(items, dict) else {item: item for item in items}
14+
15+
return make_dict_readable(name, item_dict, descriptions)
16+
17+
18+
def get_lists(names: list[str], descriptions=None):
19+
20+
return [item for name in names for item in get_list(name, descriptions)]
21+
22+
23+
def get_raw_list(name):
24+
cursorless_list_name = get_cursorless_list_name(name)
25+
return registry.lists[cursorless_list_name][0].copy()
26+
27+
28+
def make_dict_readable(type: str, dict, descriptions=None):
29+
if descriptions is None:
30+
descriptions = {}
31+
32+
return [
33+
{
34+
"identifier": value,
35+
"type": type,
36+
"spokenForms": [
37+
{
38+
"spokenForm": key,
39+
"description": descriptions.get(value, make_readable(value)),
40+
}
41+
],
42+
}
43+
for key, value in dict.items()
44+
]
45+
46+
47+
def make_readable(text):
48+
text = text.replace(".", " ")
49+
return de_camel(text).lower().capitalize()
50+
51+
52+
def de_camel(text: str) -> str:
53+
"""Replacing camelCase boundaries with blank space"""
54+
return re.sub(
55+
r"(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?<=[a-zA-Z])(?=[0-9])|(?<=[0-9])(?=[a-zA-Z])",
56+
" ",
57+
text,
58+
)

0 commit comments

Comments
 (0)