Skip to content

Commit b011135

Browse files
committed
Fix ordinal stages
1 parent 8206cf7 commit b011135

File tree

5 files changed

+165
-19
lines changed

5 files changed

+165
-19
lines changed

packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ export class ModifierStageFactoryImpl implements ModifierStageFactory {
8282

8383
return new EveryScopeStage(this, this.scopeHandlerFactory, modifier);
8484
case "ordinalScope":
85+
if (modifier.scopeType.type === "instance") {
86+
return new InstanceStage(this, modifier);
87+
}
88+
8589
return new OrdinalScopeStage(this, modifier);
8690
case "relativeScope":
8791
if (modifier.scopeType.type === "instance") {

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

Lines changed: 67 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
Direction,
33
Modifier,
4+
OrdinalScopeModifier,
45
Range,
56
RelativeScopeModifier,
67
ScopeType,
@@ -13,8 +14,8 @@ import { generateMatchesInRange } from "../../util/getMatchesInRange";
1314
import { ModifierStageFactory } from "../ModifierStageFactory";
1415
import type { ModifierStage } from "../PipelineStages.types";
1516
import { PlainTarget } from "../targets";
16-
import { OutOfRangeError } from "./targetSequenceUtils";
1717
import { ContainingTokenIfUntypedEmptyStage } from "./ConditionalModifierStages";
18+
import { OutOfRangeError } from "./targetSequenceUtils";
1819

1920
export default class InstanceStage implements ModifierStage {
2021
constructor(
@@ -32,6 +33,8 @@ export default class InstanceStage implements ModifierStage {
3233
return this.handleRelativeScope(target, this.modifier);
3334
case "everyScope":
3435
return this.handleEveryScope(target);
36+
case "ordinalScope":
37+
return this.handleOrdinalScope(target, this.modifier);
3538
default:
3639
throw Error(`${this.modifier.type} instance scope not supported`);
3740
}
@@ -41,7 +44,34 @@ export default class InstanceStage implements ModifierStage {
4144
const { editor } = target;
4245

4346
return Array.from(
44-
this.getTargetIterable(target, editor, editor.document.range, "forward"),
47+
this.getTargetIterable(
48+
target,
49+
editor,
50+
this.getEveryRange(editor),
51+
"forward",
52+
),
53+
);
54+
}
55+
56+
private getEveryRange(editor: TextEditor): Range {
57+
return editor.document.range;
58+
}
59+
60+
private handleOrdinalScope(
61+
target: Target,
62+
{ start, length }: OrdinalScopeModifier,
63+
): Target[] {
64+
const { editor } = target;
65+
66+
return takeFromOffset(
67+
this.getTargetIterable(
68+
target,
69+
editor,
70+
this.getEveryRange(editor),
71+
start >= 0 ? "forward" : "backward",
72+
),
73+
start >= 0 ? start : -(length + start),
74+
length,
4575
);
4676
}
4777

@@ -51,26 +81,16 @@ export default class InstanceStage implements ModifierStage {
5181
): Target[] {
5282
const { editor } = target;
5383

54-
const iterable = this.getTargetIterable(
55-
target,
56-
editor,
84+
const iterationRange =
5785
direction === "forward"
5886
? new Range(target.contentRange.start, editor.document.range.end)
59-
: new Range(editor.document.range.start, target.contentRange.end),
60-
direction,
61-
);
62-
63-
// Skip the first `offset` targets
64-
Array.from(itake(offset, iterable));
65-
66-
// Take the next `length` targets
67-
const targets = Array.from(itake(length, iterable));
68-
69-
if (targets.length < length) {
70-
throw new OutOfRangeError();
71-
}
87+
: new Range(editor.document.range.start, target.contentRange.end);
7288

73-
return targets;
89+
return takeFromOffset(
90+
this.getTargetIterable(target, editor, iterationRange, direction),
91+
offset,
92+
length,
93+
);
7494
}
7595

7696
private getTargetIterable(
@@ -139,3 +159,31 @@ function getFilterScopeType(target: Target): ScopeType | null {
139159

140160
return null;
141161
}
162+
163+
/**
164+
* Take `length` items from `iterable` starting at `offset`, throwing an error
165+
* if there are not enough items.
166+
*
167+
* @param iterable The iterable to take from
168+
* @param offset How many items to skip
169+
* @param count How many items to take
170+
* @returns An array of length `length` containing the items from `iterable`
171+
* starting at `offset`
172+
*/
173+
function takeFromOffset<T>(
174+
iterable: Iterable<T>,
175+
offset: number,
176+
count: number,
177+
): T[] {
178+
// Skip the first `offset` targets
179+
Array.from(itake(offset, iterable));
180+
181+
// Take the next `length` targets
182+
const items = Array.from(itake(count, iterable));
183+
184+
if (items.length < count) {
185+
throw new OutOfRangeError();
186+
}
187+
188+
return items;
189+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
languageId: plaintext
2+
command:
3+
version: 5
4+
spokenForm: clear second last instance air
5+
action: {name: clearAndSetSelection}
6+
targets:
7+
- type: primitive
8+
modifiers:
9+
- type: ordinalScope
10+
scopeType: {type: instance}
11+
start: -2
12+
length: 1
13+
mark: {type: decoratedSymbol, symbolColor: default, character: a}
14+
usePrePhraseSnapshot: true
15+
initialState:
16+
documentContents: |
17+
aaa bbb ccc aaa ddd aaa eee
18+
selections:
19+
- anchor: {line: 1, character: 0}
20+
active: {line: 1, character: 0}
21+
marks:
22+
default.a:
23+
start: {line: 0, character: 20}
24+
end: {line: 0, character: 23}
25+
finalState:
26+
documentContents: |
27+
aaa bbb ccc ddd aaa eee
28+
selections:
29+
- anchor: {line: 0, character: 12}
30+
active: {line: 0, character: 12}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
languageId: plaintext
2+
command:
3+
version: 5
4+
spokenForm: clear first two instances air
5+
action: {name: clearAndSetSelection}
6+
targets:
7+
- type: primitive
8+
modifiers:
9+
- type: ordinalScope
10+
scopeType: {type: instance}
11+
start: 0
12+
length: 2
13+
mark: {type: decoratedSymbol, symbolColor: default, character: a}
14+
usePrePhraseSnapshot: true
15+
initialState:
16+
documentContents: |
17+
aaa bbb ccc aaa ddd aaa eee
18+
selections:
19+
- anchor: {line: 1, character: 0}
20+
active: {line: 1, character: 0}
21+
marks:
22+
default.a:
23+
start: {line: 0, character: 0}
24+
end: {line: 0, character: 3}
25+
finalState:
26+
documentContents: |2
27+
bbb ccc ddd aaa eee
28+
selections:
29+
- anchor: {line: 0, character: 0}
30+
active: {line: 0, character: 0}
31+
- anchor: {line: 0, character: 9}
32+
active: {line: 0, character: 9}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
languageId: plaintext
2+
command:
3+
version: 5
4+
spokenForm: clear last two instances air
5+
action: {name: clearAndSetSelection}
6+
targets:
7+
- type: primitive
8+
modifiers:
9+
- type: ordinalScope
10+
scopeType: {type: instance}
11+
start: -2
12+
length: 2
13+
mark: {type: decoratedSymbol, symbolColor: default, character: a}
14+
usePrePhraseSnapshot: true
15+
initialState:
16+
documentContents: |
17+
aaa bbb ccc aaa ddd aaa eee
18+
selections:
19+
- anchor: {line: 1, character: 0}
20+
active: {line: 1, character: 0}
21+
marks:
22+
default.a:
23+
start: {line: 0, character: 0}
24+
end: {line: 0, character: 3}
25+
finalState:
26+
documentContents: |
27+
aaa bbb ccc ddd eee
28+
selections:
29+
- anchor: {line: 0, character: 12}
30+
active: {line: 0, character: 12}
31+
- anchor: {line: 0, character: 17}
32+
active: {line: 0, character: 17}

0 commit comments

Comments
 (0)