Skip to content

Commit d8d44ed

Browse files
Added tree
1 parent bc417d8 commit d8d44ed

File tree

4 files changed

+99
-8
lines changed

4 files changed

+99
-8
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
languageId: typescript
2+
command:
3+
version: 7
4+
spokenForm: change pair
5+
action:
6+
name: clearAndSetSelection
7+
target:
8+
type: primitive
9+
modifiers:
10+
- type: containingScope
11+
scopeType: {type: surroundingPair, delimiter: any}
12+
usePrePhraseSnapshot: true
13+
initialState:
14+
documentContents: "`[1, ${\"]\"}, 3]`"
15+
selections:
16+
- anchor: {line: 0, character: 1}
17+
active: {line: 0, character: 1}
18+
marks: {}
19+
finalState:
20+
documentContents: "``"
21+
selections:
22+
- anchor: {line: 0, character: 1}
23+
active: {line: 0, character: 1}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { Range } from "@cursorless/common";
2+
3+
export class RangeNode<T extends { range: Range }> {
4+
children: RangeNode<T>[] = [];
5+
6+
constructor(private item: T) {}
7+
8+
get range(): Range {
9+
return this.item.range;
10+
}
11+
12+
getSmallLestContaining(separator: Range): T {
13+
for (const child of this.children) {
14+
if (child.item.range.contains(separator)) {
15+
return child.getSmallLestContaining(separator);
16+
}
17+
}
18+
19+
return this.item;
20+
}
21+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import type { Range } from "@cursorless/common";
2+
import { RangeNode } from "./RangeNode";
3+
4+
/**
5+
* Creates a tree of ranges from a list of ranges. This improves containing lookup time.
6+
* @param items The ranges to create a tree from.
7+
* @returns The root nodes of the tree.
8+
*/
9+
export function createRangeTree<T extends { range: Range }>(
10+
items: T[],
11+
): RangeNode<T>[] {
12+
const results: RangeNode<T>[] = [];
13+
const parents: RangeNode<T>[] = [];
14+
15+
for (const item of items) {
16+
const node = new RangeNode(item);
17+
18+
while (
19+
parents.length > 0 &&
20+
!parents[parents.length - 1].range.contains(item.range)
21+
) {
22+
parents.pop();
23+
}
24+
25+
const parent = parents[parents.length - 1];
26+
27+
if (parent != null) {
28+
parent.children.push(node);
29+
} else {
30+
results.push(node);
31+
}
32+
33+
parents.push(node);
34+
}
35+
36+
return results;
37+
}

packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/getDelimiterOccurrences.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { matchAllIterator, Range, type TextDocument } from "@cursorless/common";
22
import type { LanguageDefinition } from "../../../../languages/LanguageDefinition";
33
import type { QueryCapture } from "../../../../languages/TreeSitterQuery/QueryCapture";
4+
import { createRangeTree } from "./createRangeTree";
45
import { getDelimiterRegex } from "./getDelimiterRegex";
56
import { RangeIterator } from "./RangeIterator";
67
import type { DelimiterOccurrence, IndividualDelimiter } from "./types";
@@ -22,11 +23,17 @@ export function getDelimiterOccurrences(
2223
return [];
2324
}
2425

25-
const disqualifyDelimiters = createRangeIterator(
26-
languageDefinition?.getCaptures(document, "disqualifyDelimiter"),
26+
const disqualifyDelimiters = new RangeIterator(
27+
getSortedCaptures(
28+
languageDefinition?.getCaptures(document, "disqualifyDelimiter"),
29+
),
2730
);
28-
const textFragments = createRangeIterator(
29-
languageDefinition?.getCaptures(document, "textFragment"),
31+
const textFragments = new RangeIterator(
32+
createRangeTree(
33+
getSortedCaptures(
34+
languageDefinition?.getCaptures(document, "textFragment"),
35+
),
36+
),
3037
);
3138

3239
const delimiterTextToDelimiterInfoMap = Object.fromEntries(
@@ -54,9 +61,12 @@ export function getDelimiterOccurrences(
5461
const isDisqualified = delimiter != null && !delimiter.hasError();
5562

5663
if (!isDisqualified) {
64+
const textFragmentRange = textFragments
65+
.getContaining(range)
66+
?.getSmallLestContaining(range).range;
5767
results.push({
5868
delimiterInfo: delimiterTextToDelimiterInfoMap[text],
59-
textFragmentRange: textFragments.getContaining(range)?.range,
69+
textFragmentRange,
6070
range,
6171
});
6272
}
@@ -65,10 +75,10 @@ export function getDelimiterOccurrences(
6575
return results;
6676
}
6777

68-
function createRangeIterator(
78+
function getSortedCaptures(
6979
captures: QueryCapture[] | undefined,
70-
): RangeIterator<QueryCapture> {
80+
): QueryCapture[] {
7181
const items = captures ?? [];
7282
items.sort((a, b) => a.range.start.compareTo(b.range.start));
73-
return new RangeIterator(items);
83+
return items;
7484
}

0 commit comments

Comments
 (0)