Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/platform/parser/node/nodes.ts
13401 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import type { Point, SyntaxNode } from 'web-tree-sitter';
7
import { BugIndicatingError } from '../../../util/vs/base/common/errors';
8
import { Range, Uri } from '../../../vscodeTypes';
9
10
export interface TreeSitterOffsetRange {
11
startIndex: number;
12
endIndex: number;
13
}
14
15
export interface TreeSitterExpressionInfo extends TreeSitterOffsetRange {
16
version?: number;
17
identifier: string;
18
text: string;
19
}
20
21
export interface TreeSitterExpressionLocationInfo extends TreeSitterOffsetRange {
22
text: string;
23
version?: number;
24
uri?: Uri;
25
range?: Range;
26
identifier?: string;
27
}
28
29
export interface TreeSitterPoint {
30
row: number;
31
column: number;
32
}
33
34
export interface TreeSitterPointRange {
35
startPosition: TreeSitterPoint;
36
endPosition: TreeSitterPoint;
37
}
38
39
/** Util functions to deal with `TreeSitterOffsetRange` type */
40
export const TreeSitterOffsetRange = {
41
/** check if `container` contains `containee` (non-strict, ie [0, 3] contains [0, 3] */
42
doesContain: (container: TreeSitterOffsetRange, containee: TreeSitterOffsetRange): boolean => container.startIndex <= containee.startIndex && containee.endIndex <= container.endIndex,
43
44
ofSyntaxNode: (n: SyntaxNode): TreeSitterOffsetRange => ({ startIndex: n.startIndex, endIndex: n.endIndex }),
45
46
/** sort by `node.startIndex`, break ties by `node.endIndex` (so that nodes with same start index are sorted in descending order) */
47
compare: (a: TreeSitterOffsetRange, b: TreeSitterOffsetRange): number => a.startIndex - b.startIndex || b.endIndex - a.endIndex,
48
isEqual: (a: TreeSitterOffsetRange, b: TreeSitterOffsetRange): boolean => TreeSitterOffsetRange.compare(a, b) === 0,
49
50
doIntersect: (a: TreeSitterOffsetRange, b: TreeSitterOffsetRange) => {
51
const start = Math.max(a.startIndex, b.startIndex);
52
const end = Math.min(a.endIndex, b.endIndex);
53
return start < end;
54
},
55
56
len: (n: TreeSitterOffsetRange) => n.endIndex - n.startIndex,
57
58
/** Given offset ranges [a0, a1] and [b0, b1], returns overlap size */
59
intersectionSize: (a: TreeSitterOffsetRange, b: TreeSitterOffsetRange): number => {
60
const start = Math.max(a.startIndex, b.startIndex);
61
const end = Math.min(a.endIndex, b.endIndex);
62
return Math.max(end - start, 0);
63
},
64
65
/** Check the given object extends TreeSitterOffsetRange */
66
isTreeSitterOffsetRange(obj: any): obj is TreeSitterOffsetRange {
67
return typeof obj.startIndex === 'number' && typeof obj.endIndex === 'number';
68
},
69
};
70
71
export const TreeSitterPoint = {
72
73
isEqual(n: TreeSitterPoint, other: TreeSitterPoint): boolean {
74
return n.row === other.row && n.column === other.column;
75
},
76
77
isBefore(n: TreeSitterPoint, other: TreeSitterPoint): boolean {
78
if (n.row < other.row || (n.row === other.row && n.column < other.column)) {
79
return true;
80
}
81
return false;
82
},
83
84
isAfter(n: TreeSitterPoint, other: TreeSitterPoint): boolean {
85
return TreeSitterPoint.isBefore(other, n);
86
},
87
88
isBeforeOrEqual(n: TreeSitterPoint, other: TreeSitterPoint): boolean {
89
const isBefore = TreeSitterPoint.isBefore(n, other);
90
const isEqual = TreeSitterPoint.isEqual(n, other);
91
if (isBefore || isEqual) {
92
return true;
93
}
94
return false;
95
},
96
97
equals(n: TreeSitterPoint, other: TreeSitterPoint): boolean {
98
return n.column === other.column && n.row === other.row;
99
},
100
101
isAfterOrEqual(n: TreeSitterPoint, other: TreeSitterPoint): boolean {
102
return TreeSitterPoint.isBeforeOrEqual(other, n);
103
},
104
105
ofPoint: (n: Point): TreeSitterPoint => ({
106
row: n.row,
107
column: n.column
108
}),
109
};
110
111
export const TreeSitterPointRange = {
112
113
/** check if `container` contains `containee` (non-strict) */
114
doesContain: (container: TreeSitterPointRange, containee: TreeSitterPointRange): boolean => {
115
return TreeSitterPoint.isBeforeOrEqual(container.startPosition, containee.startPosition) && TreeSitterPoint.isAfterOrEqual(container.endPosition, containee.endPosition);
116
},
117
118
equals: (a: TreeSitterPointRange, b: TreeSitterPointRange): boolean => {
119
return TreeSitterPoint.equals(a.startPosition, b.startPosition) && TreeSitterPoint.equals(a.endPosition, b.endPosition);
120
},
121
122
ofSyntaxNode: (n: SyntaxNode): TreeSitterPointRange => ({
123
startPosition: n.startPosition,
124
endPosition: n.endPosition
125
}),
126
};
127
128
export interface Node extends TreeSitterOffsetRange {
129
type: string;
130
}
131
132
export const Node = {
133
ofSyntaxNode: (n: SyntaxNode) => ({
134
type: n.type,
135
startIndex: n.startIndex,
136
endIndex: n.endIndex,
137
}),
138
};
139
140
export interface TreeSitterChunkHeaderInfo extends TreeSitterOffsetRange {
141
text: string;
142
range: TreeSitterPointRange;
143
}
144
145
export const TreeSitterChunkHeaderInfo = {
146
ofSyntaxNode: (n: SyntaxNode): TreeSitterChunkHeaderInfo => ({
147
range: TreeSitterPointRange.ofSyntaxNode(n),
148
startIndex: n.startIndex,
149
text: n.text,
150
endIndex: n.endIndex,
151
}),
152
};
153
154
/**
155
* Represents a node in the overlay tree.
156
*/
157
export class OverlayNode {
158
constructor(
159
public readonly startIndex: number,
160
public readonly endIndex: number,
161
/**
162
* @example `class_declaration`
163
*/
164
public kind: string, // TODO@ulugbekna: come up with more generic kinds so that these aren't per-language, then use enum?
165
public readonly children: OverlayNode[],
166
) {
167
if (startIndex > endIndex) {
168
throw new BugIndicatingError('startIndex must be less than endIndex');
169
}
170
let minStartIndex = startIndex;
171
for (const child of children) {
172
if (child.startIndex < minStartIndex) {
173
throw new BugIndicatingError('Invalid child startIndex');
174
}
175
if (child.endIndex > endIndex) {
176
throw new BugIndicatingError('Invalid child endIndex');
177
}
178
minStartIndex = Math.max(child.endIndex, minStartIndex);
179
}
180
}
181
182
toString() {
183
const printedNodes: string[] = [];
184
function toString(node: OverlayNode, indent = '') {
185
printedNodes.push(`${indent}${node.kind} [${node.startIndex}, ${node.endIndex}]`);
186
node.children.forEach(child => toString(child, indent + ' '));
187
}
188
toString(this);
189
return printedNodes.join('\n');
190
}
191
}
192
193