Skip to content

Commit 568c201

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

File tree

10 files changed

+327
-167
lines changed

10 files changed

+327
-167
lines changed

cursorless-talon/src/cheatsheet/cheat_sheet.py

Lines changed: 8 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
import math
2-
import tempfile
3-
import webbrowser
4-
from pathlib import Path
52
from typing import Optional
63

74
from talon import Module, actions, cron, skia, ui
@@ -291,72 +288,18 @@ def draw_value(self, canvas, text):
291288
self.w = max(self.w, rect.width)
292289

293290

294-
cheatsheet_out_dir = Path(tempfile.mkdtemp())
295-
296-
297291
@mod.action_class
298292
class Actions:
299293
def cursorless_cheat_sheet_toggle():
300294
"""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-
}
356-
357-
def cursorless_open_instructions():
358-
"""Open web page with cursorless instructions"""
359-
webbrowser.open(instructions_url)
295+
global cheat_sheet
296+
if cheat_sheet:
297+
actions.mode.disable("user.cursorless_cheat_sheet")
298+
cheat_sheet.close()
299+
cheat_sheet = None
300+
else:
301+
cheat_sheet = CheatSheet()
302+
actions.mode.enable("user.cursorless_cheat_sheet")
360303

361304

362305
def get_y(canvas):

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: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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+
instructions_url = "https://www.cursorless.org/docs/"
15+
16+
17+
@mod.action_class
18+
class Actions:
19+
def cursorless_cheat_sheet_show_html():
20+
"""Show new cursorless html cheat sheet"""
21+
cheatsheet_out_path = cheatsheet_out_dir / "cheatsheet.html"
22+
actions.user.vscode_with_plugin_and_wait(
23+
"cursorless.showCheatsheet",
24+
actions.user.cursorless_cheat_sheet_get_json(),
25+
str(cheatsheet_out_path),
26+
)
27+
webbrowser.open(cheatsheet_out_path.as_uri())
28+
29+
def cursorless_cheat_sheet_get_json():
30+
"""Get cursorless cheat sheet json"""
31+
return {
32+
"sections": [
33+
{
34+
"name": "Actions",
35+
"id": "actions",
36+
"items": get_actions(),
37+
},
38+
{
39+
"name": "Scopes",
40+
"id": "scopes",
41+
"items": get_scopes(),
42+
},
43+
{
44+
"name": "Paired delimiters",
45+
"id": "paired-delimiters",
46+
"items": get_lists(
47+
[
48+
"wrapper_only_paired_delimiter",
49+
"wrapper_selectable_paired_delimiter",
50+
"selectable_only_paired_delimiter",
51+
]
52+
),
53+
},
54+
{
55+
"name": "Special marks",
56+
"id": "special-marks",
57+
"items": get_list("special_mark"),
58+
},
59+
{
60+
"name": "Positions",
61+
"id": "positions",
62+
"items": get_list("position"),
63+
},
64+
{
65+
"name": "Colors",
66+
"id": "colors",
67+
"items": get_list("hat_color"),
68+
},
69+
{
70+
"name": "Shapes",
71+
"id": "shapes",
72+
"items": get_list("hat_shape"),
73+
},
74+
]
75+
}
76+
77+
def cursorless_open_instructions():
78+
"""Open web page with cursorless instructions"""
79+
webbrowser.open(instructions_url)
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)