Skip to content

Commit 5beb2ef

Browse files
AndreasArvidssonpre-commit-ci[bot]pokey
authored andcommitted
Support complex relative grammar (cursorless-dev#987)
* Added support for plural lists * Updated relative scope modifier grammar * Added ordinals to relative modifier * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Added plural list for regex scopes * Update cursorless-talon/src/modifiers/relative_scope.py Co-authored-by: Pokey Rule <[email protected]> * Update cursorless-talon/src/modifiers/relative_scope.py Co-authored-by: Pokey Rule <[email protected]> * Added link to inflection license * Use plural scope type in ordinal scope * Added support for <ordinal> last scope grammar * Clean up * Added words to csv * Updated modifier grammar * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated legend * Added docs * Update defaults.json * Start tweaking modifier cheatsheet * More cheatsheet updates * More tweaks * Cleanup cheatsheet representation * Some talon cleanup * Change formatting for cheatsheet captures * Fix captures in cheatsheet * More capture cleanup * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Revert ts config * Revert old cheatsheet changes * Fixes * Tweak docs * More doc tweaks * Add diagram * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Pokey Rule <[email protected]>
1 parent 8f784ab commit 5beb2ef

File tree

10 files changed

+455
-66
lines changed

10 files changed

+455
-66
lines changed

src/cheatsheet_html/sections/actions.py

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def get_actions():
1616
"rewrap",
1717
]
1818
simple_actions = {
19-
f"{key} <T>": value
19+
f"{key} <target>": value
2020
for key, value in all_actions.items()
2121
if value not in multiple_target_action_names
2222
}
@@ -36,20 +36,20 @@ def get_actions():
3636
"action",
3737
simple_actions,
3838
{
39-
"callAsFunction": "Call T on S",
39+
"callAsFunction": "Call <target> on selection",
4040
},
4141
),
4242
{
4343
"id": "replaceWithTarget",
4444
"type": "action",
4545
"variations": [
4646
{
47-
"spokenForm": f"{complex_actions['replaceWithTarget']} <T1> {source_destination_connective} <T2>",
48-
"description": "Replace T2 with T1",
47+
"spokenForm": f"{complex_actions['replaceWithTarget']} <target 1> {source_destination_connective} <target 2>",
48+
"description": "Replace <target 2> with <target 1>",
4949
},
5050
{
51-
"spokenForm": f"{complex_actions['replaceWithTarget']} <T>",
52-
"description": "Replace S with T",
51+
"spokenForm": f"{complex_actions['replaceWithTarget']} <target>",
52+
"description": "Replace selection with <target>",
5353
},
5454
],
5555
},
@@ -58,12 +58,12 @@ def get_actions():
5858
"type": "action",
5959
"variations": [
6060
{
61-
"spokenForm": f"{complex_actions['moveToTarget']} <T1> {source_destination_connective} <T2>",
62-
"description": "Move T1 to T2",
61+
"spokenForm": f"{complex_actions['moveToTarget']} <target 1> {source_destination_connective} <target 2>",
62+
"description": "Move <target 1> to <target 2>",
6363
},
6464
{
65-
"spokenForm": f"{complex_actions['moveToTarget']} <T>",
66-
"description": "Move T to S",
65+
"spokenForm": f"{complex_actions['moveToTarget']} <target>",
66+
"description": "Move <target> to selection",
6767
},
6868
],
6969
},
@@ -72,12 +72,12 @@ def get_actions():
7272
"type": "action",
7373
"variations": [
7474
{
75-
"spokenForm": f"{complex_actions['swapTargets']} <T1> {swap_connective} <T2>",
76-
"description": "Swap T1 with T2",
75+
"spokenForm": f"{complex_actions['swapTargets']} <target 1> {swap_connective} <target 2>",
76+
"description": "Swap <target 1> with <target 2>",
7777
},
7878
{
79-
"spokenForm": f"{complex_actions['swapTargets']} {swap_connective} <T>",
80-
"description": "Swap S with T",
79+
"spokenForm": f"{complex_actions['swapTargets']} {swap_connective} <target>",
80+
"description": "Swap selection with <target>",
8181
},
8282
],
8383
},
@@ -86,8 +86,8 @@ def get_actions():
8686
"type": "action",
8787
"variations": [
8888
{
89-
"spokenForm": f"{complex_actions['applyFormatter']} <F> at <T>",
90-
"description": "Reformat T as F",
89+
"spokenForm": f"{complex_actions['applyFormatter']} <formatter> at <target>",
90+
"description": "Reformat <target> as <formatter>",
9191
}
9292
],
9393
},
@@ -96,8 +96,8 @@ def get_actions():
9696
"type": "action",
9797
"variations": [
9898
{
99-
"spokenForm": f"<P> {complex_actions['wrapWithPairedDelimiter']} <T>",
100-
"description": "Wrap T with P",
99+
"spokenForm": f"<pair> {complex_actions['wrapWithPairedDelimiter']} <target>",
100+
"description": "Wrap <target> with <pair>",
101101
}
102102
],
103103
},
@@ -106,8 +106,8 @@ def get_actions():
106106
"type": "action",
107107
"variations": [
108108
{
109-
"spokenForm": f"<P> {complex_actions['rewrap']} <T>",
110-
"description": "Rewrap T with P",
109+
"spokenForm": f"<pair> {complex_actions['rewrap']} <target>",
110+
"description": "Rewrap <target> with <pair>",
111111
}
112112
],
113113
},

src/cheatsheet_html/sections/compound_targets.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ def get_compound_targets():
2727
"type": "compoundTargetConnective",
2828
"variations": [
2929
{
30-
"spokenForm": f"<T1> {list_connective_term} <T2>",
31-
"description": "T1 and T2",
30+
"spokenForm": f"<target 1> {list_connective_term} <target 2>",
31+
"description": "<target 1> and <target 2>",
3232
},
3333
],
3434
},
@@ -48,12 +48,12 @@ def get_entry(spoken_form, id):
4848
"type": "compoundTargetConnective",
4949
"variations": [
5050
{
51-
"spokenForm": f"<T1> {spoken_form} <T2>",
52-
"description": formatter("T1", "T2"),
51+
"spokenForm": f"<target 1> {spoken_form} <target 2>",
52+
"description": formatter("<target 1>", "<target 2>"),
5353
},
5454
{
55-
"spokenForm": f"{spoken_form} <T>",
56-
"description": formatter("S", "T"),
55+
"spokenForm": f"{spoken_form} <target>",
56+
"description": formatter("selection", "<target>"),
5757
},
5858
],
5959
}

src/cheatsheet_html/sections/modifiers.py

Lines changed: 95 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
"simple_modifier",
55
"interior_modifier",
66
"head_tail_modifier",
7+
"simple_scope_modifier",
8+
"first_modifier",
9+
"last_modifier",
10+
"previous_next_modifier",
11+
"backward_modifier",
712
]
813

914

@@ -15,6 +20,12 @@ def get_modifiers():
1520
complex_modifier_ids = [
1621
"extendThroughStartOf",
1722
"extendThroughEndOf",
23+
"every",
24+
"first",
25+
"last",
26+
"previous",
27+
"next",
28+
"backward",
1829
]
1930
simple_modifiers = {
2031
key: value
@@ -47,8 +58,8 @@ def get_modifiers():
4758
"description": "Extend through start of line",
4859
},
4960
{
50-
"spokenForm": f"{complex_modifiers['extendThroughStartOf']} <M>",
51-
"description": "Extend through start of <M>",
61+
"spokenForm": f"{complex_modifiers['extendThroughStartOf']} <modifier>",
62+
"description": "Extend through start of <modifier>",
5263
},
5364
],
5465
},
@@ -61,8 +72,88 @@ def get_modifiers():
6172
"description": "Extend through end of line",
6273
},
6374
{
64-
"spokenForm": f"{complex_modifiers['extendThroughEndOf']} <M>",
65-
"description": "Extend through end of <M>",
75+
"spokenForm": f"{complex_modifiers['extendThroughEndOf']} <modifier>",
76+
"description": "Extend through end of <modifier>",
77+
},
78+
],
79+
},
80+
{
81+
"id": "containingScope",
82+
"type": "modifier",
83+
"variations": [
84+
{
85+
"spokenForm": f"<scope>",
86+
"description": "Containing instance of <scope>",
87+
},
88+
],
89+
},
90+
{
91+
"id": "every",
92+
"type": "modifier",
93+
"variations": [
94+
{
95+
"spokenForm": f"{complex_modifiers['every']} <scope>",
96+
"description": "Every instance of <scope>",
97+
},
98+
],
99+
},
100+
{
101+
"id": "relativeScope",
102+
"type": "modifier",
103+
"variations": [
104+
{
105+
"spokenForm": f"{complex_modifiers['previous']} <scope>",
106+
"description": "Previous instance of <scope>",
107+
},
108+
{
109+
"spokenForm": f"{complex_modifiers['next']} <scope>",
110+
"description": "Next instance of <scope>",
111+
},
112+
{
113+
"spokenForm": f"<ordinal> {complex_modifiers['previous']} <scope>",
114+
"description": "<ordinal> instance of <scope> before target",
115+
},
116+
{
117+
"spokenForm": f"<ordinal> {complex_modifiers['next']} <scope>",
118+
"description": "<ordinal> instance of <scope> after target",
119+
},
120+
{
121+
"spokenForm": f"{complex_modifiers['previous']} <number> <scope>s",
122+
"description": "previous <number> instances of <scope>",
123+
},
124+
{
125+
"spokenForm": f"<number> <scope>s {complex_modifiers['backward']}",
126+
"description": "<number> instances of <scope> including target, going backwards",
127+
},
128+
{
129+
"spokenForm": f"<number> <scope>s",
130+
"description": "<number> instances of <scope> including target, going forwards",
131+
},
132+
{
133+
"spokenForm": f"{complex_modifiers['next']} <number> <scope>s",
134+
"description": "next <number> instances of <scope>",
135+
},
136+
],
137+
},
138+
{
139+
"id": "ordinalScope",
140+
"type": "modifier",
141+
"variations": [
142+
{
143+
"spokenForm": f"<ordinal> <scope>",
144+
"description": "<ordinal> instance of <scope> in iteration scope",
145+
},
146+
{
147+
"spokenForm": f"<ordinal> {complex_modifiers['last']} <scope>",
148+
"description": "<ordinal>-to-last instance of <scope> in iteration scope",
149+
},
150+
{
151+
"spokenForm": f"{complex_modifiers['first']} <number> <scope>s",
152+
"description": "First <number> instances of <scope> in iteration scope",
153+
},
154+
{
155+
"spokenForm": f"{complex_modifiers['last']} <number> <scope>s",
156+
"description": "Last <number> instances of <scope> in iteration scope",
66157
},
67158
],
68159
},

src/csv_overrides.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from talon import Context, Module, actions, app, fs
88

99
from .conventions import get_cursorless_list_name
10+
from .vendor.inflection import pluralize
1011

1112
SPOKEN_FORM_HEADER = "Spoken form"
1213
CURSORLESS_IDENTIFIER_HEADER = "Cursorless identifier"
@@ -30,6 +31,7 @@ def init_csv_and_watch_changes(
3031
headers: list[str] = [SPOKEN_FORM_HEADER, CURSORLESS_IDENTIFIER_HEADER],
3132
ctx: Context = Context(),
3233
no_update_file: bool = False,
34+
pluralize_lists: Optional[list[str]] = [],
3335
):
3436
"""
3537
Initialize a cursorless settings csv, creating it if necessary, and watch
@@ -56,6 +58,7 @@ def init_csv_and_watch_changes(
5658
unknown values in this list
5759
no_update_file Optional[bool]: Set this to `TRUE` to indicate that we should
5860
not update the csv. This is used generally in case there was an issue coming up with the default set of values so we don't want to persist those to disk
61+
pluralize_lists: Create plural version of given lists
5962
"""
6063
if extra_ignored_values is None:
6164
extra_ignored_values = []
@@ -80,6 +83,7 @@ def on_watch(path, flags):
8083
extra_ignored_values,
8184
allow_unknown_values,
8285
default_list_name,
86+
pluralize_lists,
8387
ctx,
8488
)
8589

@@ -100,6 +104,7 @@ def on_watch(path, flags):
100104
extra_ignored_values,
101105
allow_unknown_values,
102106
default_list_name,
107+
pluralize_lists,
103108
ctx,
104109
)
105110
else:
@@ -111,6 +116,7 @@ def on_watch(path, flags):
111116
extra_ignored_values,
112117
allow_unknown_values,
113118
default_list_name,
119+
pluralize_lists,
114120
ctx,
115121
)
116122

@@ -130,6 +136,7 @@ def update_dicts(
130136
extra_ignored_values: list[str],
131137
allow_unknown_values: bool,
132138
default_list_name: Optional[str],
139+
pluralize_lists: Optional[list[str]],
133140
ctx: Context,
134141
):
135142
# Create map with all default values
@@ -173,7 +180,11 @@ def update_dicts(
173180

174181
# Assign result to talon context list
175182
for list_name, dict in results.items():
176-
ctx.lists[get_cursorless_list_name(list_name)] = dict
183+
list_singular_name = get_cursorless_list_name(list_name)
184+
ctx.lists[list_singular_name] = dict
185+
if list_name in pluralize_lists:
186+
list_plural_name = f"{list_singular_name}_plural"
187+
ctx.lists[list_plural_name] = {pluralize(k): v for k, v in dict.items()}
177188

178189

179190
def update_file(

src/modifiers/modifiers.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
from ..csv_overrides import init_csv_and_watch_changes
44
from .head_tail import head_tail_modifiers
55
from .interior import interior_modifiers
6+
from .ordinal_scope import first_modifiers, last_modifiers
67
from .range_type import range_types
8+
from .relative_scope import backward_modifiers, previous_next_modifiers
9+
from .simple_scope_modifier import simple_scope_modifiers
710

811
mod = Module()
912

@@ -40,9 +43,9 @@ def cursorless_simple_modifier(m) -> dict[str, str]:
4043
# rather than just "inside".
4144
head_tail_swallowed_modifiers = [
4245
"<user.cursorless_simple_modifier>", # bounds, just, leading, trailing
43-
"<user.cursorless_containing_scope>", # funk, state, class
46+
"<user.cursorless_simple_scope_modifier>", # funk, state, class, every funk
4447
"<user.cursorless_ordinal_scope>", # first past second word
45-
"<user.cursorless_relative_scope>", # next funk
48+
"<user.cursorless_relative_scope>", # next funk, 3 funks
4649
"<user.cursorless_surrounding_pair>", # matching/pair [curly, round]
4750
]
4851

@@ -73,6 +76,11 @@ def on_ready():
7376
"interior_modifier": interior_modifiers,
7477
"head_tail_modifier": head_tail_modifiers,
7578
"range_type": range_types,
79+
"simple_scope_modifier": simple_scope_modifiers,
80+
"first_modifier": first_modifiers,
81+
"last_modifier": last_modifiers,
82+
"previous_next_modifier": previous_next_modifiers,
83+
"backward_modifier": backward_modifiers,
7684
},
7785
)
7886

0 commit comments

Comments
 (0)