Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/platform/parser/node/testGenParsing.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 { QueryCapture } from 'web-tree-sitter';
7
import { uniqueFilter } from '../../../util/vs/base/common/arrays';
8
import { assertType } from '../../../util/vs/base/common/types';
9
import { Node, TreeSitterOffsetRange } from './nodes';
10
import { _parse } from './parserWithCaching';
11
import { runQueries } from './querying';
12
import { WASMLanguage } from './treeSitterLanguages';
13
import { testableNodeQueries, testInSuiteQueries } from './treeSitterQueries';
14
15
export type TestableNode = {
16
identifier: {
17
name: string;
18
range: TreeSitterOffsetRange;
19
};
20
node: Node;
21
};
22
23
24
export async function _getTestableNode(
25
language: WASMLanguage,
26
source: string,
27
range: TreeSitterOffsetRange
28
): Promise<TestableNode | null> {
29
const treeRef = await _parse(language, source);
30
31
try {
32
const queryCaptures = runQueries(
33
testableNodeQueries[language],
34
treeRef.tree.rootNode
35
).flatMap(({ captures }) => captures); // @ulugbekna: keep in mind: there's duplication of captures
36
37
const symbolKindToIdents = new Map<string, QueryCapture[]>();
38
39
for (const capture of queryCaptures) {
40
const [symbolKind, name] = capture.name.split('.');
41
if (name !== 'identifier') {
42
continue;
43
}
44
45
const idents = symbolKindToIdents.get(symbolKind) || [];
46
idents.push(capture);
47
symbolKindToIdents.set(symbolKind, idents);
48
}
49
50
let minimalTestableNode: TestableNode | null = null;
51
52
for (const capture of queryCaptures) {
53
const [symbolKind, name] = capture.name.split('.');
54
55
if (name !== undefined || // ensure we traverse only declarations (and child nodes such as `method.identifier` or `method.accessibility_modifier`)
56
!TreeSitterOffsetRange.doesContain(capture.node, range) // ensure this declaration contains our range of interest
57
) {
58
continue;
59
}
60
61
// ensure we pick range-wise minimal testable node
62
if (minimalTestableNode !== null &&
63
TreeSitterOffsetRange.len(minimalTestableNode.node) < TreeSitterOffsetRange.len(capture.node)
64
) {
65
continue;
66
}
67
68
const idents = symbolKindToIdents.get(symbolKind);
69
70
assertType(idents !== undefined, `must have seen identifier for symbol kind '${symbolKind}' (lang: ${language})`);
71
72
const nodeIdent = idents.find(ident => TreeSitterOffsetRange.doesContain(capture.node, ident.node));
73
74
assertType(nodeIdent !== undefined, `must have seen identifier for symbol '${symbolKind}' (lang: ${language})`);
75
76
minimalTestableNode = {
77
identifier: {
78
name: nodeIdent.node.text,
79
range: TreeSitterOffsetRange.ofSyntaxNode(nodeIdent.node),
80
},
81
node: Node.ofSyntaxNode(capture.node),
82
};
83
}
84
85
return minimalTestableNode;
86
87
} catch (e) {
88
console.error('getTestableNode: Unexpected error', e);
89
return null;
90
} finally {
91
treeRef.dispose();
92
}
93
}
94
95
export async function _getTestableNodes(
96
language: WASMLanguage,
97
source: string,
98
): Promise<TestableNode[] | null> {
99
const treeRef = await _parse(language, source);
100
101
try {
102
const queryCaptures = runQueries(
103
testableNodeQueries[language],
104
treeRef.tree.rootNode
105
)
106
.flatMap(({ captures }) => captures)
107
.filter(uniqueFilter((c: QueryCapture) => [c.node.startIndex, c.node.endIndex].toString()));
108
109
const symbolKindToIdents = new Map<string, QueryCapture[]>();
110
111
for (const capture of queryCaptures) {
112
const [symbolKind, name] = capture.name.split('.');
113
if (name !== 'identifier') {
114
continue;
115
}
116
117
const idents = symbolKindToIdents.get(symbolKind) || [];
118
idents.push(capture);
119
symbolKindToIdents.set(symbolKind, idents);
120
}
121
122
const testableNodes: TestableNode[] = [];
123
124
for (const capture of queryCaptures) {
125
if (capture.name.includes('.')) {
126
continue;
127
}
128
129
const symbolKind = capture.name;
130
131
const idents = symbolKindToIdents.get(symbolKind);
132
133
assertType(idents !== undefined, `must have seen identifier for symbol kind '${symbolKind}' (lang: ${language})`);
134
135
const nodeIdent = idents.find(ident => TreeSitterOffsetRange.doesContain(capture.node, ident.node));
136
137
assertType(nodeIdent !== undefined, `must have seen identifier for symbol '${symbolKind}' (lang: ${language})`);
138
139
testableNodes.push({
140
identifier: {
141
name: nodeIdent.node.text,
142
range: TreeSitterOffsetRange.ofSyntaxNode(nodeIdent.node),
143
},
144
node: Node.ofSyntaxNode(capture.node),
145
});
146
}
147
148
return testableNodes;
149
150
} catch (e) {
151
console.error('getTestableNodes: Unexpected error', e);
152
return null;
153
} finally {
154
treeRef.dispose();
155
}
156
}
157
158
export async function _findLastTest(lang: WASMLanguage, src: string): Promise<TreeSitterOffsetRange | null> {
159
160
const treeRef = await _parse(lang, src);
161
162
try {
163
const queryResults = runQueries(testInSuiteQueries[lang], treeRef.tree.rootNode);
164
165
const captures = queryResults
166
.flatMap(e => e.captures).sort((a, b) => a.node.endIndex - b.node.endIndex)
167
.filter(c => c.name === 'test');
168
169
if (captures.length === 0) {
170
return null;
171
}
172
173
const lastTest = captures[captures.length - 1].node;
174
175
return {
176
startIndex: lastTest.startIndex,
177
endIndex: lastTest.endIndex
178
};
179
} finally {
180
treeRef.dispose();
181
}
182
}
183
184