Skip to content

Commit cc6092d

Browse files
Added tree
1 parent ff8d1d4 commit cc6092d

File tree

5 files changed

+79
-24
lines changed

5 files changed

+79
-24
lines changed

packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/CollectionItemScopeHandler/CollectionItemTextualScopeHandler.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ import type {
1515
} from "../scopeHandler.types";
1616
import type { ScopeHandlerFactory } from "../ScopeHandlerFactory";
1717
import { collectionItemIterationScopeHandler } from "./collectionItemIterationScopeHandler";
18+
import { createRangeTree } from "./createRangeTree";
1819
import { createTargetScope } from "./createTargetScope";
1920
import { getInteriorRanges } from "./getInteriorRanges";
2021
import { getSeparatorOccurrences } from "./getSeparatorOccurrences";
22+
import { RangeIterator } from "./RangeIterator";
2123

2224
export class CollectionItemTextualScopeHandler extends BaseScopeHandler {
2325
public scopeType: ScopeType = { type: "collectionItem" };
@@ -48,28 +50,33 @@ export class CollectionItemTextualScopeHandler extends BaseScopeHandler {
4850
editor,
4951
"collectionBoundary",
5052
);
53+
const interiorRangeIterator = new RangeIterator(
54+
createRangeTree(interiorRanges),
55+
);
5156
const stringRanges = getInteriorRanges(
5257
this.scopeHandlerFactory,
5358
this.languageId,
5459
editor,
5560
"string",
5661
);
62+
const stringRangeIterator = new RangeIterator(stringRanges);
5763
const scopes: TargetScope[] = [];
5864
const usedInteriors = new Set<Range>();
5965
const iterationStatesStack: IterationState[] = [];
6066

6167
for (const separator of separatorRanges) {
6268
// Separators in a string are not considered
63-
if (stringRanges.contains(separator)) {
69+
if (stringRangeIterator.contains(separator)) {
6470
continue;
6571
}
6672

6773
const currentIterationState =
6874
iterationStatesStack[iterationStatesStack.length - 1];
6975

7076
// Get range for smallest containing interior
71-
const containingInteriorRange =
72-
interiorRanges.getsSmallestContaining(separator);
77+
const containingInteriorRange = interiorRangeIterator
78+
.getContaining(separator)
79+
?.getSmallLestContaining(separator);
7380

7481
// The contain range is either the interior or the line containing the separator
7582
const containingIterationRange =
@@ -122,7 +129,7 @@ export class CollectionItemTextualScopeHandler extends BaseScopeHandler {
122129
}
123130

124131
// Add interior ranges without a delimiter in them. eg: `[foo]`
125-
for (const interior of interiorRanges.ranges) {
132+
for (const interior of interiorRanges) {
126133
if (!usedInteriors.has(interior)) {
127134
const range = shrinkRangeToFitContent(editor, interior);
128135
if (!range.isEmpty) {

packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/CollectionItemScopeHandler/RangeIterator.ts

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,32 @@
11
import type { Range } from "@cursorless/common";
22

3-
export class RangeIterator {
3+
export class RangeIterator<T extends Range> {
44
private index = 0;
55

6-
constructor(public ranges: Range[]) {}
6+
constructor(public ranges: T[]) {}
77

88
contains(separator: Range): boolean {
9+
return this.advance(separator);
10+
}
11+
12+
getContaining(separator: Range): T | undefined {
13+
if (!this.advance(separator)) {
14+
return undefined;
15+
}
16+
17+
return this.ranges[this.index];
18+
}
19+
20+
private advance(separator: Range): boolean {
921
while (this.index < this.ranges.length) {
1022
const range = this.ranges[this.index];
1123

1224
if (range.contains(separator)) {
1325
return true;
1426
}
1527

16-
// Separator is after the range. Since the ranges are sorted, we can stop here.
17-
if (separator.start.isAfter(range.end)) {
28+
// Separator is before the range. Since the ranges are sorted, we can stop here.
29+
if (separator.end.isBefore(range.start)) {
1830
return false;
1931
}
2032

@@ -23,15 +35,4 @@ export class RangeIterator {
2335

2436
return false;
2537
}
26-
27-
getsSmallestContaining(separator: Range): Range | undefined {
28-
// TODO: fixed performance on large files
29-
return this.ranges
30-
.filter((range) => range.contains(separator))
31-
.sort((a, b) => (a.contains(b) ? 1 : b.contains(a) ? -1 : 0))[0];
32-
}
3338
}
34-
35-
// const containingInteriorRange: Range | undefined = interiorRanges
36-
// .filter((range) => range.contains(separator))
37-
// .sort((a, b) => (a.contains(b) ? 1 : b.contains(a) ? -1 : 0))[0];
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Range } from "@cursorless/common";
2+
3+
export class RangeNode extends Range {
4+
children: RangeNode[] = [];
5+
6+
constructor(private range: Range) {
7+
super(range.start, range.end);
8+
}
9+
10+
getSmallLestContaining(separator: Range): Range {
11+
let result = this.range;
12+
13+
for (const child of this.children) {
14+
if (child.contains(separator)) {
15+
result = child.getSmallLestContaining(separator);
16+
break;
17+
}
18+
}
19+
20+
return result;
21+
}
22+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import type { Range } from "@cursorless/common";
2+
import { RangeNode } from "./RangeNode";
3+
4+
export function createRangeTree(ranges: Range[]): RangeNode[] {
5+
const results: RangeNode[] = [];
6+
7+
const parents: RangeNode[] = [];
8+
9+
for (const range of ranges) {
10+
const node = new RangeNode(range);
11+
12+
while (parents.length > 0 && !parents[parents.length - 1].contains(range)) {
13+
parents.pop();
14+
}
15+
16+
const parent = parents[parents.length - 1];
17+
18+
if (parent != null) {
19+
parent.children.push(node);
20+
} else {
21+
parents.push(node);
22+
results.push(node);
23+
}
24+
}
25+
26+
return results;
27+
}
Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import {
2+
type Range,
23
type SurroundingPairName,
34
type TextEditor,
45
Position,
56
} from "@cursorless/common";
67
import type { ScopeHandlerFactory } from "../ScopeHandlerFactory";
7-
import { RangeIterator } from "./RangeIterator";
88

99
export function getInteriorRanges(
1010
scopeHandlerFactory: ScopeHandlerFactory,
1111
languageId: string,
1212
editor: TextEditor,
1313
delimiter: SurroundingPairName,
14-
): RangeIterator {
14+
): Range[] {
1515
const scopeHandler = scopeHandlerFactory.create(
1616
{
1717
type: "surroundingPairInterior",
@@ -20,14 +20,12 @@ export function getInteriorRanges(
2020
languageId,
2121
);
2222

23-
const ranges = Array.from(
23+
return Array.from(
2424
scopeHandler.generateScopes(editor, new Position(0, 0), "forward", {
2525
containment: undefined,
2626
skipAncestorScopes: false,
2727
includeDescendantScopes: true,
2828
}),
2929
(scope) => scope.domain,
3030
);
31-
32-
return new RangeIterator(ranges);
3331
}

0 commit comments

Comments
 (0)