Skip to content

Commit 440b6b2

Browse files
1 parent 7a1db49 commit 440b6b2

File tree

10 files changed

+289
-22
lines changed

10 files changed

+289
-22
lines changed

packages/cursorless-engine/src/processTargets/modifiers/ItemStage/getIterationScope.ts

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Range, TextEditor } from "@cursorless/common";
1+
import { Range, TextEditor, TextLine } from "@cursorless/common";
22
import { LanguageDefinitions } from "../../../languages/LanguageDefinitions";
33
import { Target } from "../../../typings/target.types";
44
import { PlainTarget, SurroundingPairTarget } from "../../targets";
@@ -18,27 +18,18 @@ export function getIterationScope(
1818
): { range: Range; boundary?: [Range, Range] } {
1919
let surroundingTarget = getSurroundingPair(languageDefinitions, target);
2020

21-
// Iteration is necessary in case of nested strings
21+
// Iteration is necessary in case of in valid surrounding targets (nested strings, content range adjacent to delimiter)
2222
while (surroundingTarget != null) {
23-
const surroundingStringTarget = getStringSurroundingPair(
24-
languageDefinitions,
25-
surroundingTarget,
26-
);
27-
28-
// We don't look for items inside strings.
2923
if (
30-
// Not in a string
31-
surroundingStringTarget == null ||
32-
// In a non-string surrounding pair that is inside a surrounding string. This is fine.
33-
surroundingStringTarget.contentRange.start.isBefore(
34-
surroundingTarget.contentRange.start,
24+
useInteriorOfSurroundingTarget(
25+
languageDefinitions,
26+
target,
27+
surroundingTarget,
3528
)
3629
) {
3730
return {
3831
range: surroundingTarget.getInteriorStrict()[0].contentRange,
39-
boundary: surroundingTarget
40-
.getBoundaryStrict()
41-
.map((t) => t.contentRange) as [Range, Range],
32+
boundary: getBoundary(surroundingTarget),
4233
};
4334
}
4435

@@ -55,6 +46,84 @@ export function getIterationScope(
5546
};
5647
}
5748

49+
function useInteriorOfSurroundingTarget(
50+
languageDefinitions: LanguageDefinitions,
51+
target: Target,
52+
surroundingTarget: SurroundingPairTarget,
53+
): boolean {
54+
const { contentRange } = target;
55+
56+
if (contentRange.isEmpty) {
57+
const [left, right] = getBoundary(surroundingTarget);
58+
const pos = contentRange.start;
59+
// Content range is outside adjacent to pair
60+
if (pos.isEqual(left.start) || pos.isEqual(right.end)) {
61+
return false;
62+
}
63+
const line = target.editor.document.lineAt(pos);
64+
// Content range is just inside of opening/left delimiter
65+
if (
66+
pos.isEqual(left.end) &&
67+
characterIsWhitespaceOrMissing(line, pos.character)
68+
) {
69+
return false;
70+
}
71+
// Content range is just inside of closing/right delimiter
72+
if (
73+
pos.isEqual(right.start) &&
74+
characterIsWhitespaceOrMissing(line, pos.character - 1)
75+
) {
76+
return false;
77+
}
78+
} else {
79+
// Content range is equal to surrounding range
80+
if (contentRange.isRangeEqual(surroundingTarget.contentRange)) {
81+
return false;
82+
}
83+
84+
// Content range is equal to one of the boundaries of the surrounding range
85+
const [left, right] = getBoundary(surroundingTarget);
86+
if (contentRange.isRangeEqual(left) || contentRange.isRangeEqual(right)) {
87+
return false;
88+
}
89+
}
90+
91+
// We don't look for items inside strings.
92+
// A non-string surrounding pair that is inside a surrounding string is fine.
93+
const surroundingStringTarget = getStringSurroundingPair(
94+
languageDefinitions,
95+
surroundingTarget,
96+
);
97+
if (
98+
surroundingStringTarget != null &&
99+
surroundingTarget.contentRange.start.isBeforeOrEqual(
100+
surroundingStringTarget.contentRange.start,
101+
)
102+
) {
103+
return false;
104+
}
105+
106+
return true;
107+
}
108+
109+
function getBoundary(surroundingTarget: SurroundingPairTarget): [Range, Range] {
110+
return surroundingTarget.getBoundaryStrict().map((t) => t.contentRange) as [
111+
Range,
112+
Range,
113+
];
114+
}
115+
116+
function characterIsWhitespaceOrMissing(
117+
line: TextLine,
118+
index: number,
119+
): boolean {
120+
return (
121+
index < line.range.start.character ||
122+
index >= line.range.end.character ||
123+
line.text[index].trim() === ""
124+
);
125+
}
126+
58127
function getParentSurroundingPair(
59128
languageDefinitions: LanguageDefinitions,
60129
editor: TextEditor,

packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/itemTextual/chuckEveryItem.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ initialState:
1616
"testing": "whatever",
1717
}
1818
selections:
19-
- anchor: {line: 0, character: 1}
20-
active: {line: 0, character: 1}
19+
- anchor: {line: 1, character: 1}
20+
active: {line: 1, character: 1}
2121
marks: {}
2222
finalState:
2323
documentContents: |-
2424
{
2525
2626
}
2727
selections:
28-
- anchor: {line: 0, character: 1}
29-
active: {line: 0, character: 1}
28+
- anchor: {line: 1, character: 1}
29+
active: {line: 1, character: 1}
3030
fullTargets: [{type: primitive, mark: {type: cursor}, modifiers: [{type: everyScope, scopeType: {type: collectionItem}}]}]
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
languageId: plaintext
2+
command:
3+
version: 5
4+
spokenForm: clear item
5+
action: {name: clearAndSetSelection}
6+
targets:
7+
- type: primitive
8+
modifiers:
9+
- type: containingScope
10+
scopeType: {type: collectionItem}
11+
usePrePhraseSnapshot: true
12+
initialState:
13+
documentContents: "[1, [2, 3]];"
14+
selections:
15+
- anchor: {line: 0, character: 4}
16+
active: {line: 0, character: 10}
17+
marks: {}
18+
finalState:
19+
documentContents: "[1, ];"
20+
selections:
21+
- anchor: {line: 0, character: 4}
22+
active: {line: 0, character: 4}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
languageId: plaintext
2+
command:
3+
version: 5
4+
spokenForm: clear item
5+
action: {name: clearAndSetSelection}
6+
targets:
7+
- type: primitive
8+
modifiers:
9+
- type: containingScope
10+
scopeType: {type: collectionItem}
11+
usePrePhraseSnapshot: true
12+
initialState:
13+
documentContents: "[1, [2, 3]];"
14+
selections:
15+
- anchor: {line: 0, character: 4}
16+
active: {line: 0, character: 5}
17+
marks: {}
18+
finalState:
19+
documentContents: "[1, ];"
20+
selections:
21+
- anchor: {line: 0, character: 4}
22+
active: {line: 0, character: 4}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
languageId: plaintext
2+
command:
3+
version: 5
4+
spokenForm: clear item
5+
action: {name: clearAndSetSelection}
6+
targets:
7+
- type: primitive
8+
modifiers:
9+
- type: containingScope
10+
scopeType: {type: collectionItem}
11+
usePrePhraseSnapshot: true
12+
initialState:
13+
documentContents: "[1, [2, 3]];"
14+
selections:
15+
- anchor: {line: 0, character: 10}
16+
active: {line: 0, character: 9}
17+
marks: {}
18+
finalState:
19+
documentContents: "[1, ];"
20+
selections:
21+
- anchor: {line: 0, character: 4}
22+
active: {line: 0, character: 4}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
languageId: plaintext
2+
command:
3+
version: 5
4+
spokenForm: clear item
5+
action: {name: clearAndSetSelection}
6+
targets:
7+
- type: primitive
8+
modifiers:
9+
- type: containingScope
10+
scopeType: {type: collectionItem}
11+
usePrePhraseSnapshot: true
12+
initialState:
13+
documentContents: |-
14+
[
15+
[
16+
1,
17+
2
18+
],
19+
3
20+
];
21+
selections:
22+
- anchor: {line: 1, character: 4}
23+
active: {line: 1, character: 4}
24+
marks: {}
25+
finalState:
26+
documentContents: |-
27+
[
28+
,
29+
3
30+
];
31+
selections:
32+
- anchor: {line: 1, character: 4}
33+
active: {line: 1, character: 4}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
languageId: plaintext
2+
command:
3+
version: 5
4+
spokenForm: clear item
5+
action: {name: clearAndSetSelection}
6+
targets:
7+
- type: primitive
8+
modifiers:
9+
- type: containingScope
10+
scopeType: {type: collectionItem}
11+
usePrePhraseSnapshot: true
12+
initialState:
13+
documentContents: |-
14+
[
15+
[
16+
1,
17+
2
18+
],
19+
3
20+
];
21+
selections:
22+
- anchor: {line: 1, character: 5}
23+
active: {line: 1, character: 5}
24+
marks: {}
25+
finalState:
26+
documentContents: |-
27+
[
28+
,
29+
3
30+
];
31+
selections:
32+
- anchor: {line: 1, character: 4}
33+
active: {line: 1, character: 4}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
languageId: plaintext
2+
command:
3+
version: 5
4+
spokenForm: clear item
5+
action: {name: clearAndSetSelection}
6+
targets:
7+
- type: primitive
8+
modifiers:
9+
- type: containingScope
10+
scopeType: {type: collectionItem}
11+
usePrePhraseSnapshot: true
12+
initialState:
13+
documentContents: |-
14+
[
15+
[
16+
1,
17+
2
18+
],
19+
3
20+
];
21+
selections:
22+
- anchor: {line: 4, character: 4}
23+
active: {line: 4, character: 4}
24+
marks: {}
25+
finalState:
26+
documentContents: |-
27+
[
28+
,
29+
3
30+
];
31+
selections:
32+
- anchor: {line: 1, character: 4}
33+
active: {line: 1, character: 4}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
languageId: plaintext
2+
command:
3+
version: 5
4+
spokenForm: clear item
5+
action: {name: clearAndSetSelection}
6+
targets:
7+
- type: primitive
8+
modifiers:
9+
- type: containingScope
10+
scopeType: {type: collectionItem}
11+
usePrePhraseSnapshot: true
12+
initialState:
13+
documentContents: |-
14+
[
15+
[
16+
1,
17+
2
18+
],
19+
3
20+
];
21+
selections:
22+
- anchor: {line: 4, character: 5}
23+
active: {line: 4, character: 5}
24+
marks: {}
25+
finalState:
26+
documentContents: |-
27+
[
28+
,
29+
3
30+
];
31+
selections:
32+
- anchor: {line: 1, character: 4}
33+
active: {line: 1, character: 4}

packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/takeItem.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ finalState:
1919
2020
const value = { a: 1, b: 2, c: 3 };
2121
selections:
22-
- anchor: {line: 1, character: 16}
23-
active: {line: 1, character: 20}
22+
- anchor: {line: 1, character: 0}
23+
active: {line: 1, character: 35}
2424
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, modifier: {type: containingScope, scopeType: collectionItem}, insideOutsideType: inside}]

0 commit comments

Comments
 (0)