Skip to content

Commit 86266ce

Browse files
Added grand scope (#2130)
`change grand statement` ## Checklist - [x] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [x] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [x] I have not broken the cheatsheet
1 parent 1740cd3 commit 86266ce

File tree

12 files changed

+145
-5
lines changed

12 files changed

+145
-5
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
tags: [enhancement]
3+
pullRequest: 2130
4+
---
5+
6+
- Added `grand` modifier. This modifier will take the containing grandparent scope. eg `"take grand statement air"`

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def get_modifiers():
2222
"extendThroughStartOf",
2323
"extendThroughEndOf",
2424
"every",
25+
"ancestor",
2526
"first",
2627
"last",
2728
"previous",
@@ -101,6 +102,16 @@ def get_modifiers():
101102
},
102103
],
103104
},
105+
{
106+
"id": "ancestor",
107+
"type": "modifier",
108+
"variations": [
109+
{
110+
"spokenForm": f"{complex_modifiers['ancestor']} <scope>",
111+
"description": "Grandparent containing instance of <scope>",
112+
},
113+
],
114+
},
104115
{
105116
"id": "relativeScope",
106117
"type": "modifier",

cursorless-talon/src/modifiers/simple_scope_modifier.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,23 @@
1515
)
1616
def cursorless_simple_scope_modifier(m) -> dict[str, Any]:
1717
"""Containing scope, every scope, etc"""
18+
if hasattr(m, "cursorless_simple_scope_modifier"):
19+
modifier = m.cursorless_simple_scope_modifier
20+
21+
if modifier == "every":
22+
return {
23+
"type": "everyScope",
24+
"scopeType": m.cursorless_scope_type,
25+
}
26+
27+
if modifier == "ancestor":
28+
return {
29+
"type": "containingScope",
30+
"scopeType": m.cursorless_scope_type,
31+
"ancestorIndex": 1,
32+
}
33+
1834
return {
19-
"type": "everyScope" if m[0] == "every" else "containingScope",
35+
"type": "containingScope",
2036
"scopeType": m.cursorless_scope_type,
2137
}

cursorless-talon/src/spoken_forms.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
"its": "inferPreviousMark",
8282
"visible": "visible"
8383
},
84-
"simple_scope_modifier": { "every": "every" },
84+
"simple_scope_modifier": { "every": "every", "grand": "ancestor" },
8585
"interior_modifier": {
8686
"inside": "interiorOnly"
8787
},

docs/user/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,15 @@ The command `"every"` can be used to select a syntactic element and all of its m
215215

216216
For example, the command `take every key [blue] air` will select every key in the map/object/dict including the token with a blue hat over the letter 'a'.
217217

218+
##### `"grand"`
219+
220+
The command `"grand"` can be used to select the grand parent of the containing syntactic element.
221+
222+
- `"take grand statement air"`
223+
- `"take grand funk air"`
224+
225+
For example, the command `take grand statement [blue] air` will select that parent statement of the statement containing the token with a blue hat over the letter 'a'.
226+
218227
##### Sub-token modifiers
219228

220229
###### `"sub"`

packages/cursorless-engine/src/generateSpokenForm/primitiveTargetToSpokenForm.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,18 @@ export class PrimitiveTargetSpokenFormGenerator {
4949
throw new NoSpokenFormError(`Modifier '${modifier.type}'`);
5050

5151
case "containingScope":
52-
return [this.handleScopeType(modifier.scopeType)];
52+
if (modifier.ancestorIndex == null || modifier.ancestorIndex === 0) {
53+
return this.handleScopeType(modifier.scopeType);
54+
}
55+
if (modifier.ancestorIndex === 1) {
56+
return [
57+
this.spokenFormMap.modifierExtra.ancestor,
58+
this.handleScopeType(modifier.scopeType),
59+
];
60+
}
61+
throw new NoSpokenFormError(
62+
`Modifier '${modifier.type}' with ancestor index ${modifier.ancestorIndex}`,
63+
);
5364

5465
case "everyScope":
5566
return [

packages/cursorless-engine/src/processTargets/modifiers/getContainingScopeTarget.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ export function getContainingScopeTarget(
3838
editor,
3939
scope.domain.end,
4040
"forward",
41-
ancestorIndex - 1,
41+
ancestorIndex,
42+
true,
4243
);
4344
}
4445

@@ -86,10 +87,12 @@ function expandFromPosition(
8687
position: Position,
8788
direction: Direction,
8889
ancestorIndex: number,
90+
allowAdjacentScopes: boolean = false,
8991
): TargetScope | undefined {
9092
let nextAncestorIndex = 0;
9193
for (const scope of scopeHandler.generateScopes(editor, position, direction, {
9294
containment: "required",
95+
allowAdjacentScopes,
9396
})) {
9497
if (nextAncestorIndex === ancestorIndex) {
9598
return scope;

packages/cursorless-engine/src/spokenForms/SpokenFormType.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ type ModifierExtra =
6060
| "previous"
6161
| "next"
6262
| "forward"
63-
| "backward";
63+
| "backward"
64+
| "ancestor";
6465

6566
export type SpokenFormType = keyof SpokenFormMapKeyTypes;

packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ export const defaultSpokenFormMapCore: DefaultSpokenFormMapDefinition = {
133133
next: "next",
134134
forward: "forward",
135135
backward: "backward",
136+
ancestor: "grand",
136137
},
137138

138139
customRegex: {},
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
languageId: javascript
2+
command:
3+
version: 6
4+
spokenForm: change grand state
5+
action:
6+
name: clearAndSetSelection
7+
target:
8+
type: primitive
9+
modifiers:
10+
- type: containingScope
11+
scopeType: {type: statement}
12+
ancestorIndex: 1
13+
usePrePhraseSnapshot: true
14+
initialState:
15+
documentContents: |-
16+
function myFunk() {
17+
const value = 2
18+
}
19+
selections:
20+
- anchor: {line: 1, character: 4}
21+
active: {line: 1, character: 4}
22+
marks: {}
23+
finalState:
24+
documentContents: ""
25+
selections:
26+
- anchor: {line: 0, character: 0}
27+
active: {line: 0, character: 0}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
languageId: python
2+
command:
3+
version: 6
4+
spokenForm: change grand state
5+
action:
6+
name: clearAndSetSelection
7+
target:
8+
type: primitive
9+
modifiers:
10+
- type: containingScope
11+
scopeType: {type: statement}
12+
ancestorIndex: 1
13+
usePrePhraseSnapshot: true
14+
initialState:
15+
documentContents: |-
16+
def my_funk():
17+
value = 2
18+
selections:
19+
- anchor: {line: 1, character: 4}
20+
active: {line: 1, character: 4}
21+
marks: {}
22+
finalState:
23+
documentContents: ""
24+
selections:
25+
- anchor: {line: 0, character: 0}
26+
active: {line: 0, character: 0}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
languageId: javascript
2+
command:
3+
version: 6
4+
spokenForm: change grand state
5+
action:
6+
name: clearAndSetSelection
7+
target:
8+
type: primitive
9+
modifiers:
10+
- type: containingScope
11+
scopeType: {type: statement}
12+
ancestorIndex: 1
13+
usePrePhraseSnapshot: true
14+
initialState:
15+
documentContents: |-
16+
class MyClass {
17+
myFunk() {
18+
19+
}
20+
}
21+
selections:
22+
- anchor: {line: 2, character: 8}
23+
active: {line: 2, character: 8}
24+
marks: {}
25+
finalState:
26+
documentContents: ""
27+
selections:
28+
- anchor: {line: 0, character: 0}
29+
active: {line: 0, character: 0}

0 commit comments

Comments
 (0)