1
1
import {
2
2
Direction ,
3
3
Modifier ,
4
+ OrdinalScopeModifier ,
4
5
Range ,
5
6
RelativeScopeModifier ,
6
7
ScopeType ,
@@ -13,8 +14,8 @@ import { generateMatchesInRange } from "../../util/getMatchesInRange";
13
14
import { ModifierStageFactory } from "../ModifierStageFactory" ;
14
15
import type { ModifierStage } from "../PipelineStages.types" ;
15
16
import { PlainTarget } from "../targets" ;
16
- import { OutOfRangeError } from "./targetSequenceUtils" ;
17
17
import { ContainingTokenIfUntypedEmptyStage } from "./ConditionalModifierStages" ;
18
+ import { OutOfRangeError } from "./targetSequenceUtils" ;
18
19
19
20
export default class InstanceStage implements ModifierStage {
20
21
constructor (
@@ -23,15 +24,20 @@ export default class InstanceStage implements ModifierStage {
23
24
) { }
24
25
25
26
run ( inputTarget : Target ) : Target [ ] {
27
+ // If the target is untyped and empty, we want to target the containing
28
+ // token. This handles the case where they just say "instance" with an empty
29
+ // selection, eg "take every instance".
26
30
const target = new ContainingTokenIfUntypedEmptyStage (
27
31
this . modifierStageFactory ,
28
32
) . run ( inputTarget ) [ 0 ] ;
29
33
30
34
switch ( this . modifier . type ) {
31
- case "relativeScope" :
32
- return this . handleRelativeScope ( target , this . modifier ) ;
33
35
case "everyScope" :
34
36
return this . handleEveryScope ( target ) ;
37
+ case "ordinalScope" :
38
+ return this . handleOrdinalScope ( target , this . modifier ) ;
39
+ case "relativeScope" :
40
+ return this . handleRelativeScope ( target , this . modifier ) ;
35
41
default :
36
42
throw Error ( `${ this . modifier . type } instance scope not supported` ) ;
37
43
}
@@ -41,7 +47,30 @@ export default class InstanceStage implements ModifierStage {
41
47
const { editor } = target ;
42
48
43
49
return Array . from (
44
- this . getTargetIterable ( target , editor , editor . document . range , "forward" ) ,
50
+ this . getTargetIterable (
51
+ target ,
52
+ editor ,
53
+ this . getEveryRange ( editor ) ,
54
+ "forward" ,
55
+ ) ,
56
+ ) ;
57
+ }
58
+
59
+ private handleOrdinalScope (
60
+ target : Target ,
61
+ { start, length } : OrdinalScopeModifier ,
62
+ ) : Target [ ] {
63
+ const { editor } = target ;
64
+
65
+ return takeFromOffset (
66
+ this . getTargetIterable (
67
+ target ,
68
+ editor ,
69
+ this . getEveryRange ( editor ) ,
70
+ start >= 0 ? "forward" : "backward" ,
71
+ ) ,
72
+ start >= 0 ? start : - ( length + start ) ,
73
+ length ,
45
74
) ;
46
75
}
47
76
@@ -51,26 +80,20 @@ export default class InstanceStage implements ModifierStage {
51
80
) : Target [ ] {
52
81
const { editor } = target ;
53
82
54
- const iterable = this . getTargetIterable (
55
- target ,
56
- editor ,
83
+ const iterationRange =
57
84
direction === "forward"
58
85
? new Range ( target . contentRange . start , editor . document . range . end )
59
- : new Range ( editor . document . range . start , target . contentRange . end ) ,
60
- direction ,
61
- ) ;
86
+ : new Range ( editor . document . range . start , target . contentRange . end ) ;
62
87
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
- }
88
+ return takeFromOffset (
89
+ this . getTargetIterable ( target , editor , iterationRange , direction ) ,
90
+ offset ,
91
+ length ,
92
+ ) ;
93
+ }
72
94
73
- return targets ;
95
+ private getEveryRange ( editor : TextEditor ) : Range {
96
+ return editor . document . range ;
74
97
}
75
98
76
99
private getTargetIterable (
@@ -139,3 +162,31 @@ function getFilterScopeType(target: Target): ScopeType | null {
139
162
140
163
return null ;
141
164
}
165
+
166
+ /**
167
+ * Take `length` items from `iterable` starting at `offset`, throwing an error
168
+ * if there are not enough items.
169
+ *
170
+ * @param iterable The iterable to take from
171
+ * @param offset How many items to skip
172
+ * @param count How many items to take
173
+ * @returns An array of length `length` containing the items from `iterable`
174
+ * starting at `offset`
175
+ */
176
+ function takeFromOffset < T > (
177
+ iterable : Iterable < T > ,
178
+ offset : number ,
179
+ count : number ,
180
+ ) : T [ ] {
181
+ // Skip the first `offset` targets
182
+ Array . from ( itake ( offset , iterable ) ) ;
183
+
184
+ // Take the next `length` targets
185
+ const items = Array . from ( itake ( count , iterable ) ) ;
186
+
187
+ if ( items . length < count ) {
188
+ throw new OutOfRangeError ( ) ;
189
+ }
190
+
191
+ return items ;
192
+ }
0 commit comments