@codingame/monaco-vscode-treesitter-service-override 15.0.2 → 16.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +1 -1
- package/package.json +9 -6
- package/vscode/src/vs/editor/common/languages/highlights/typescript.scm +488 -0
- package/vscode/src/vs/editor/common/model/tokenStore.d.ts +0 -1
- package/vscode/src/vs/editor/common/model/tokenStore.js +14 -8
- package/vscode/src/vs/editor/common/model/treeSitterTokenStoreService.d.ts +3 -0
- package/vscode/src/vs/editor/common/model/treeSitterTokenStoreService.js +33 -27
- package/vscode/src/vs/editor/common/services/treeSitter/cursorUtils.d.ts +6 -0
- package/vscode/src/vs/editor/common/services/treeSitter/cursorUtils.js +76 -0
- package/vscode/src/vs/editor/common/services/treeSitter/textModelTreeSitter.d.ts +88 -0
- package/vscode/src/vs/editor/common/services/treeSitter/textModelTreeSitter.js +675 -0
- package/vscode/src/vs/editor/common/services/treeSitter/treeSitterLanguages.d.ts +30 -0
- package/vscode/src/vs/editor/common/services/treeSitter/treeSitterLanguages.js +102 -0
- package/vscode/src/vs/editor/common/services/treeSitter/treeSitterParserService.d.ts +8 -80
- package/vscode/src/vs/editor/common/services/treeSitter/treeSitterParserService.js +20 -522
- package/vscode/src/vs/editor/common/services/treeSitterParserService.d.ts +16 -3
- package/vscode/src/vs/editor/common/services/treeSitterParserService.js +1 -1
- package/vscode/src/vs/workbench/services/treeSitter/browser/treeSitterCodeEditors.d.ts +7 -2
- package/vscode/src/vs/workbench/services/treeSitter/browser/treeSitterCodeEditors.js +43 -22
- package/vscode/src/vs/workbench/services/treeSitter/browser/treeSitterTokenizationFeature.d.ts +14 -14
- package/vscode/src/vs/workbench/services/treeSitter/browser/treeSitterTokenizationFeature.js +256 -169
- package/assets/typescript.scm +0 -388
|
@@ -0,0 +1,675 @@
|
|
|
1
|
+
|
|
2
|
+
import { __decorate, __param } from '@codingame/monaco-vscode-api/external/tslib/tslib.es6';
|
|
3
|
+
import { ITreeSitterImporter } from '@codingame/monaco-vscode-api/vscode/vs/editor/common/services/treeSitterParserService.service';
|
|
4
|
+
import { Disposable, DisposableStore, dispose } from '@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle';
|
|
5
|
+
import { ITelemetryService } from '@codingame/monaco-vscode-api/vscode/vs/platform/telemetry/common/telemetry.service';
|
|
6
|
+
import { ILogService } from '@codingame/monaco-vscode-api/vscode/vs/platform/log/common/log.service';
|
|
7
|
+
import { setTimeout0 } from '@codingame/monaco-vscode-api/vscode/vs/base/common/platform';
|
|
8
|
+
import { Emitter, Event } from '@codingame/monaco-vscode-api/vscode/vs/base/common/event';
|
|
9
|
+
import { cancelOnDispose } from '@codingame/monaco-vscode-api/vscode/vs/base/common/cancellation';
|
|
10
|
+
import { Range } from '@codingame/monaco-vscode-api/vscode/vs/editor/common/core/range';
|
|
11
|
+
import { LimitedQueue } from '@codingame/monaco-vscode-api/vscode/vs/base/common/async';
|
|
12
|
+
import { TextLength } from '@codingame/monaco-vscode-api/vscode/vs/editor/common/core/textLength';
|
|
13
|
+
import { FileAccess } from '@codingame/monaco-vscode-api/vscode/vs/base/common/network';
|
|
14
|
+
import { IFileService } from '@codingame/monaco-vscode-api/vscode/vs/platform/files/common/files.service';
|
|
15
|
+
import { isCancellationError, CancellationError } from '@codingame/monaco-vscode-api/vscode/vs/base/common/errors';
|
|
16
|
+
import { gotoParent, getClosestPreviousNodes, nextSiblingOrParentSibling, gotoNthChild } from './cursorUtils.js';
|
|
17
|
+
|
|
18
|
+
var TelemetryParseType;
|
|
19
|
+
(function (TelemetryParseType) {
|
|
20
|
+
TelemetryParseType["Full"] = "fullParse";
|
|
21
|
+
TelemetryParseType["Incremental"] = "incrementalParse";
|
|
22
|
+
})(TelemetryParseType || (TelemetryParseType = {}));
|
|
23
|
+
let TextModelTreeSitter = class TextModelTreeSitter extends Disposable {
|
|
24
|
+
get parseResult() { return this._rootTreeSitterTree; }
|
|
25
|
+
constructor(textModel, _treeSitterLanguages, parseImmediately = true, _treeSitterImporter, _logService, _telemetryService, _fileService) {
|
|
26
|
+
super();
|
|
27
|
+
this.textModel = textModel;
|
|
28
|
+
this._treeSitterLanguages = _treeSitterLanguages;
|
|
29
|
+
this._treeSitterImporter = _treeSitterImporter;
|
|
30
|
+
this._logService = _logService;
|
|
31
|
+
this._telemetryService = _telemetryService;
|
|
32
|
+
this._fileService = _fileService;
|
|
33
|
+
this._onDidChangeParseResult = this._register(( new Emitter()));
|
|
34
|
+
this.onDidChangeParseResult = this._onDidChangeParseResult.event;
|
|
35
|
+
this._injectionTreeSitterTrees = ( new Map());
|
|
36
|
+
this._versionId = 0;
|
|
37
|
+
this._parseSessionDisposables = this._register(( new DisposableStore()));
|
|
38
|
+
if (parseImmediately) {
|
|
39
|
+
this._register(Event.runAndSubscribe(this.textModel.onDidChangeLanguage, (e => this._onDidChangeLanguage(e ? e.newLanguage : this.textModel.getLanguageId()))));
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
this._register(this.textModel.onDidChangeLanguage(e => this._onDidChangeLanguage(e ? e.newLanguage : this.textModel.getLanguageId())));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async _onDidChangeLanguage(languageId) {
|
|
46
|
+
this.parse(languageId);
|
|
47
|
+
}
|
|
48
|
+
async parse(languageId = this.textModel.getLanguageId()) {
|
|
49
|
+
this._parseSessionDisposables.clear();
|
|
50
|
+
this._rootTreeSitterTree = undefined;
|
|
51
|
+
const token = cancelOnDispose(this._parseSessionDisposables);
|
|
52
|
+
let language;
|
|
53
|
+
try {
|
|
54
|
+
language = await this._getLanguage(languageId, token);
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
if (isCancellationError(e)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
throw e;
|
|
61
|
+
}
|
|
62
|
+
const Parser = await this._treeSitterImporter.getParserClass();
|
|
63
|
+
if (token.isCancellationRequested) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const treeSitterTree = this._parseSessionDisposables.add(( new TreeSitterParseResult(( new Parser()), languageId, language, this._logService, this._telemetryService)));
|
|
67
|
+
this._rootTreeSitterTree = treeSitterTree;
|
|
68
|
+
this._parseSessionDisposables.add(treeSitterTree.onDidUpdate(e => this._handleTreeUpdate(e)));
|
|
69
|
+
this._parseSessionDisposables.add(this.textModel.onDidChangeContent(e => this._onDidChangeContent(treeSitterTree, [e])));
|
|
70
|
+
this._onDidChangeContent(treeSitterTree, undefined);
|
|
71
|
+
if (token.isCancellationRequested) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
return this._rootTreeSitterTree;
|
|
75
|
+
}
|
|
76
|
+
_getLanguage(languageId, token) {
|
|
77
|
+
const language = this._treeSitterLanguages.getOrInitLanguage(languageId);
|
|
78
|
+
if (language) {
|
|
79
|
+
return Promise.resolve(language);
|
|
80
|
+
}
|
|
81
|
+
const disposables = [];
|
|
82
|
+
return ( new Promise((resolve, reject) => {
|
|
83
|
+
disposables.push(this._treeSitterLanguages.onDidAddLanguage(e => {
|
|
84
|
+
if (e.id === languageId) {
|
|
85
|
+
dispose(disposables);
|
|
86
|
+
resolve(e.language);
|
|
87
|
+
}
|
|
88
|
+
}));
|
|
89
|
+
token.onCancellationRequested(() => {
|
|
90
|
+
dispose(disposables);
|
|
91
|
+
reject(( new CancellationError()));
|
|
92
|
+
}, undefined, disposables);
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
async _handleTreeUpdate(e, parentTreeResult, parentLanguage) {
|
|
96
|
+
if (e.ranges && (e.versionId >= this._versionId)) {
|
|
97
|
+
this._versionId = e.versionId;
|
|
98
|
+
const tree = parentTreeResult ?? this._rootTreeSitterTree;
|
|
99
|
+
let injections;
|
|
100
|
+
if (tree.tree) {
|
|
101
|
+
injections = await this._collectInjections(tree.tree);
|
|
102
|
+
if (injections) {
|
|
103
|
+
this._processInjections(injections, tree, parentLanguage ?? this.textModel.getLanguageId(), e.includedModelChanges);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
this._onDidChangeParseResult.fire({ ranges: e.ranges, versionId: e.versionId, tree: this, languageId: this.textModel.getLanguageId(), hasInjections: !!injections && injections.size > 0 });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async _ensureInjectionQueries() {
|
|
110
|
+
if (!this._queries) {
|
|
111
|
+
const injectionsQueriesLocation = `vs/editor/common/languages/injections/${this.textModel.getLanguageId()}.scm`;
|
|
112
|
+
const uri = ( FileAccess.asFileUri(injectionsQueriesLocation));
|
|
113
|
+
if (!(await this._fileService.exists(uri))) {
|
|
114
|
+
this._queries = '';
|
|
115
|
+
}
|
|
116
|
+
else if (this._fileService.hasProvider(uri)) {
|
|
117
|
+
const query = await this._fileService.readFile(uri);
|
|
118
|
+
this._queries = ( query.value.toString());
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
this._queries = '';
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return this._queries;
|
|
125
|
+
}
|
|
126
|
+
async _getQuery() {
|
|
127
|
+
if (!this._query) {
|
|
128
|
+
const language = await this._treeSitterLanguages.getLanguage(this.textModel.getLanguageId());
|
|
129
|
+
if (!language) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const queries = await this._ensureInjectionQueries();
|
|
133
|
+
if (queries === '') {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const Query = await this._treeSitterImporter.getQueryClass();
|
|
137
|
+
this._query = ( new Query(language, queries));
|
|
138
|
+
}
|
|
139
|
+
return this._query;
|
|
140
|
+
}
|
|
141
|
+
async _collectInjections(tree) {
|
|
142
|
+
const query = await this._getQuery();
|
|
143
|
+
if (!query) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (!tree?.rootNode) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const cursor = tree.walk();
|
|
150
|
+
const injections = ( new Map());
|
|
151
|
+
let hasNext = true;
|
|
152
|
+
while (hasNext) {
|
|
153
|
+
hasNext = await this._processNode(cursor, query, injections);
|
|
154
|
+
await ( new Promise(resolve => setTimeout0(resolve)));
|
|
155
|
+
}
|
|
156
|
+
return this._mergeAdjacentRanges(injections);
|
|
157
|
+
}
|
|
158
|
+
_processNode(cursor, query, injections) {
|
|
159
|
+
const node = cursor.currentNode;
|
|
160
|
+
const nodeLineCount = node.endPosition.row - node.startPosition.row;
|
|
161
|
+
if (nodeLineCount <= 1000) {
|
|
162
|
+
this._processCaptures(query, node, injections);
|
|
163
|
+
return cursor.gotoNextSibling() || this.gotoNextSiblingOfAncestor(cursor);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
return cursor.gotoFirstChild() || cursor.gotoNextSibling() || this.gotoNextSiblingOfAncestor(cursor);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
_processCaptures(query, node, injections) {
|
|
170
|
+
const captures = query.captures(node);
|
|
171
|
+
for (const capture of captures) {
|
|
172
|
+
const injectionLanguage = capture.setProperties?.['injection.language'];
|
|
173
|
+
if (injectionLanguage) {
|
|
174
|
+
const range = this._createRangeFromNode(capture.node);
|
|
175
|
+
if (!( injections.has(injectionLanguage))) {
|
|
176
|
+
injections.set(injectionLanguage, []);
|
|
177
|
+
}
|
|
178
|
+
injections.get(injectionLanguage)?.push(range);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
_createRangeFromNode(node) {
|
|
183
|
+
return {
|
|
184
|
+
startIndex: node.startIndex,
|
|
185
|
+
endIndex: node.endIndex,
|
|
186
|
+
startPosition: { row: node.startPosition.row, column: node.startPosition.column },
|
|
187
|
+
endPosition: { row: node.endPosition.row, column: node.endPosition.column }
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
_mergeAdjacentRanges(injections) {
|
|
191
|
+
for (const [languageId, ranges] of injections) {
|
|
192
|
+
if (ranges.length <= 1) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
const mergedRanges = [];
|
|
196
|
+
let current = ranges[0];
|
|
197
|
+
for (let i = 1; i < ranges.length; i++) {
|
|
198
|
+
const next = ranges[i];
|
|
199
|
+
if (next.startIndex <= current.endIndex) {
|
|
200
|
+
current = this._mergeRanges(current, next);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
mergedRanges.push(current);
|
|
204
|
+
current = next;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
mergedRanges.push(current);
|
|
208
|
+
injections.set(languageId, mergedRanges);
|
|
209
|
+
}
|
|
210
|
+
return injections;
|
|
211
|
+
}
|
|
212
|
+
_mergeRanges(current, next) {
|
|
213
|
+
return {
|
|
214
|
+
startIndex: current.startIndex,
|
|
215
|
+
endIndex: Math.max(current.endIndex, next.endIndex),
|
|
216
|
+
startPosition: current.startPosition,
|
|
217
|
+
endPosition: next.endPosition.row > current.endPosition.row ?
|
|
218
|
+
next.endPosition :
|
|
219
|
+
current.endPosition
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
async _processInjections(injections, parentTree, parentLanguage, modelChanges) {
|
|
223
|
+
for (const [languageId, ranges] of injections) {
|
|
224
|
+
const language = await this._treeSitterLanguages.getLanguage(languageId);
|
|
225
|
+
if (!language) {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
const treeSitterTree = await this._getOrCreateInjectedTree(languageId, language, parentTree, parentLanguage);
|
|
229
|
+
if (treeSitterTree) {
|
|
230
|
+
this._onDidChangeContent(treeSitterTree, modelChanges, ranges);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
async _getOrCreateInjectedTree(languageId, language, parentTree, parentLanguage) {
|
|
235
|
+
let treeSitterTree = this._injectionTreeSitterTrees.get(languageId);
|
|
236
|
+
if (!treeSitterTree) {
|
|
237
|
+
const Parser = await this._treeSitterImporter.getParserClass();
|
|
238
|
+
treeSitterTree = ( new TreeSitterParseResult(( new Parser()), languageId, language, this._logService, this._telemetryService));
|
|
239
|
+
this._parseSessionDisposables.add(treeSitterTree.onDidUpdate(e => this._handleTreeUpdate(e, parentTree, parentLanguage)));
|
|
240
|
+
this._injectionTreeSitterTrees.set(languageId, treeSitterTree);
|
|
241
|
+
}
|
|
242
|
+
return treeSitterTree;
|
|
243
|
+
}
|
|
244
|
+
gotoNextSiblingOfAncestor(cursor) {
|
|
245
|
+
while (cursor.gotoParent()) {
|
|
246
|
+
if (cursor.gotoNextSibling()) {
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
getInjection(offset, parentLanguage) {
|
|
253
|
+
if (this._injectionTreeSitterTrees.size === 0) {
|
|
254
|
+
return undefined;
|
|
255
|
+
}
|
|
256
|
+
let hasFoundParentLanguage = parentLanguage === this.textModel.getLanguageId();
|
|
257
|
+
for (const [_, treeSitterTree] of this._injectionTreeSitterTrees) {
|
|
258
|
+
if (treeSitterTree.tree) {
|
|
259
|
+
if (hasFoundParentLanguage && treeSitterTree.ranges?.find(r => r.startIndex <= offset && r.endIndex >= offset)) {
|
|
260
|
+
return treeSitterTree;
|
|
261
|
+
}
|
|
262
|
+
if (!hasFoundParentLanguage && treeSitterTree.languageId === parentLanguage) {
|
|
263
|
+
hasFoundParentLanguage = true;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
_onDidChangeContent(treeSitterTree, change, ranges) {
|
|
270
|
+
treeSitterTree.onDidChangeContent(this.textModel, change, ranges);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
TextModelTreeSitter = ( __decorate([
|
|
274
|
+
( __param(3, ITreeSitterImporter)),
|
|
275
|
+
( __param(4, ILogService)),
|
|
276
|
+
( __param(5, ITelemetryService)),
|
|
277
|
+
( __param(6, IFileService))
|
|
278
|
+
], TextModelTreeSitter));
|
|
279
|
+
class TreeSitterParseResult {
|
|
280
|
+
get versionId() {
|
|
281
|
+
return this._versionId;
|
|
282
|
+
}
|
|
283
|
+
constructor(parser, languageId, language, _logService, _telemetryService) {
|
|
284
|
+
this.parser = parser;
|
|
285
|
+
this.languageId = languageId;
|
|
286
|
+
this.language = language;
|
|
287
|
+
this._logService = _logService;
|
|
288
|
+
this._telemetryService = _telemetryService;
|
|
289
|
+
this._onDidUpdate = ( new Emitter());
|
|
290
|
+
this.onDidUpdate = this._onDidUpdate.event;
|
|
291
|
+
this._versionId = 0;
|
|
292
|
+
this._editVersion = 0;
|
|
293
|
+
this._isDisposed = false;
|
|
294
|
+
this._onDidChangeContentQueue = ( new LimitedQueue());
|
|
295
|
+
this._lastYieldTime = 0;
|
|
296
|
+
this.parser.setLanguage(language);
|
|
297
|
+
}
|
|
298
|
+
dispose() {
|
|
299
|
+
this._isDisposed = true;
|
|
300
|
+
this._onDidUpdate.dispose();
|
|
301
|
+
this._tree?.delete();
|
|
302
|
+
this._lastFullyParsed?.delete();
|
|
303
|
+
this._lastFullyParsedWithEdits?.delete();
|
|
304
|
+
this.parser?.delete();
|
|
305
|
+
}
|
|
306
|
+
get tree() { return this._lastFullyParsed; }
|
|
307
|
+
get isDisposed() { return this._isDisposed; }
|
|
308
|
+
findChangedNodes(newTree, oldTree) {
|
|
309
|
+
const newCursor = newTree.walk();
|
|
310
|
+
const oldCursor = oldTree.walk();
|
|
311
|
+
const nodes = [];
|
|
312
|
+
let next = true;
|
|
313
|
+
do {
|
|
314
|
+
if (newCursor.currentNode.hasChanges) {
|
|
315
|
+
const newChildren = newCursor.currentNode.children;
|
|
316
|
+
const indexChangedChildren = [];
|
|
317
|
+
const changedChildren = newChildren.filter((c, index) => {
|
|
318
|
+
if (c?.hasChanges || (oldCursor.currentNode.children.length <= index)) {
|
|
319
|
+
indexChangedChildren.push(index);
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
return false;
|
|
323
|
+
});
|
|
324
|
+
if ((changedChildren.length === 0) || (newCursor.currentNode.hasError !== oldCursor.currentNode.hasError)) {
|
|
325
|
+
while (newCursor.currentNode.parent && next && !newCursor.currentNode.isNamed) {
|
|
326
|
+
next = gotoParent(newCursor, oldCursor);
|
|
327
|
+
}
|
|
328
|
+
const newNode = newCursor.currentNode;
|
|
329
|
+
const closestPreviousNode = getClosestPreviousNodes(newCursor, newTree) ?? newNode;
|
|
330
|
+
nodes.push({
|
|
331
|
+
startIndex: closestPreviousNode.startIndex,
|
|
332
|
+
endIndex: newNode.endIndex,
|
|
333
|
+
startPosition: closestPreviousNode.startPosition,
|
|
334
|
+
endPosition: newNode.endPosition
|
|
335
|
+
});
|
|
336
|
+
next = nextSiblingOrParentSibling(newCursor, oldCursor);
|
|
337
|
+
}
|
|
338
|
+
else if (changedChildren.length >= 1) {
|
|
339
|
+
next = gotoNthChild(newCursor, oldCursor, indexChangedChildren[0]);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
next = nextSiblingOrParentSibling(newCursor, oldCursor);
|
|
344
|
+
}
|
|
345
|
+
} while (next);
|
|
346
|
+
return nodes;
|
|
347
|
+
}
|
|
348
|
+
findTreeChanges(newTree, changedNodes, newRanges) {
|
|
349
|
+
let newRangeIndex = 0;
|
|
350
|
+
const mergedChanges = [];
|
|
351
|
+
for (let nodeIndex = 0; nodeIndex < changedNodes.length; nodeIndex++) {
|
|
352
|
+
const node = changedNodes[nodeIndex];
|
|
353
|
+
if (mergedChanges.length > 0) {
|
|
354
|
+
if ((node.startIndex >= mergedChanges[mergedChanges.length - 1].newRangeStartOffset) && (node.endIndex <= mergedChanges[mergedChanges.length - 1].newRangeEndOffset)) {
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
const cursor = newTree.walk();
|
|
359
|
+
const cursorContainersNode = () => cursor.startIndex < node.startIndex && cursor.endIndex > node.endIndex;
|
|
360
|
+
while (cursorContainersNode()) {
|
|
361
|
+
let child = cursor.gotoFirstChild();
|
|
362
|
+
let foundChild = false;
|
|
363
|
+
while (child) {
|
|
364
|
+
if (cursorContainersNode() && cursor.currentNode.isNamed) {
|
|
365
|
+
foundChild = true;
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
child = cursor.gotoNextSibling();
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (!foundChild) {
|
|
373
|
+
cursor.gotoParent();
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
if (cursor.currentNode.childCount === 0) {
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
let nodesInRange;
|
|
381
|
+
const foundNodeSize = cursor.endIndex - cursor.startIndex;
|
|
382
|
+
if (foundNodeSize > 5000) {
|
|
383
|
+
let child = cursor.gotoFirstChild();
|
|
384
|
+
nodesInRange = [];
|
|
385
|
+
while (child) {
|
|
386
|
+
if (cursor.endIndex > node.startIndex) {
|
|
387
|
+
nodesInRange.push(cursor.currentNode);
|
|
388
|
+
do {
|
|
389
|
+
child = cursor.gotoNextSibling();
|
|
390
|
+
} while (child && (cursor.endIndex < node.endIndex));
|
|
391
|
+
nodesInRange.push(cursor.currentNode);
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
child = cursor.gotoNextSibling();
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
nodesInRange = [cursor.currentNode];
|
|
399
|
+
}
|
|
400
|
+
while (cursor.currentNode.id !== nodesInRange[0].id) {
|
|
401
|
+
cursor.gotoPreviousSibling();
|
|
402
|
+
}
|
|
403
|
+
const previousNode = getClosestPreviousNodes(cursor, newTree);
|
|
404
|
+
const startPosition = previousNode ? previousNode.endPosition : nodesInRange[0].startPosition;
|
|
405
|
+
const startIndex = previousNode ? previousNode.endIndex : nodesInRange[0].startIndex;
|
|
406
|
+
const endPosition = nodesInRange[nodesInRange.length - 1].endPosition;
|
|
407
|
+
const endIndex = nodesInRange[nodesInRange.length - 1].endIndex;
|
|
408
|
+
const newChange = { newRange: ( new Range(
|
|
409
|
+
startPosition.row + 1,
|
|
410
|
+
startPosition.column + 1,
|
|
411
|
+
endPosition.row + 1,
|
|
412
|
+
endPosition.column + 1
|
|
413
|
+
)), newRangeStartOffset: startIndex, newRangeEndOffset: endIndex };
|
|
414
|
+
if ((newRangeIndex < newRanges.length) && rangesIntersect(newRanges[newRangeIndex], { startIndex, endIndex})) {
|
|
415
|
+
if (newRanges[newRangeIndex].startIndex < newChange.newRangeStartOffset) {
|
|
416
|
+
newChange.newRange = newChange.newRange.setStartPosition(newRanges[newRangeIndex].startPosition.row + 1, newRanges[newRangeIndex].startPosition.column + 1);
|
|
417
|
+
newChange.newRangeStartOffset = newRanges[newRangeIndex].startIndex;
|
|
418
|
+
}
|
|
419
|
+
if (newRanges[newRangeIndex].endIndex > newChange.newRangeEndOffset) {
|
|
420
|
+
newChange.newRange = newChange.newRange.setEndPosition(newRanges[newRangeIndex].endPosition.row + 1, newRanges[newRangeIndex].endPosition.column + 1);
|
|
421
|
+
newChange.newRangeEndOffset = newRanges[newRangeIndex].endIndex;
|
|
422
|
+
}
|
|
423
|
+
newRangeIndex++;
|
|
424
|
+
}
|
|
425
|
+
else if (newRangeIndex < newRanges.length && newRanges[newRangeIndex].endIndex < newChange.newRangeStartOffset) {
|
|
426
|
+
mergedChanges.push({
|
|
427
|
+
newRange: ( new Range(
|
|
428
|
+
newRanges[newRangeIndex].startPosition.row + 1,
|
|
429
|
+
newRanges[newRangeIndex].startPosition.column + 1,
|
|
430
|
+
newRanges[newRangeIndex].endPosition.row + 1,
|
|
431
|
+
newRanges[newRangeIndex].endPosition.column + 1
|
|
432
|
+
)),
|
|
433
|
+
newRangeStartOffset: newRanges[newRangeIndex].startIndex,
|
|
434
|
+
newRangeEndOffset: newRanges[newRangeIndex].endIndex
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
if ((mergedChanges.length > 0) && (mergedChanges[mergedChanges.length - 1].newRangeEndOffset >= newChange.newRangeStartOffset)) {
|
|
438
|
+
mergedChanges[mergedChanges.length - 1].newRange = Range.fromPositions(mergedChanges[mergedChanges.length - 1].newRange.getStartPosition(), newChange.newRange.getEndPosition());
|
|
439
|
+
mergedChanges[mergedChanges.length - 1].newRangeEndOffset = newChange.newRangeEndOffset;
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
mergedChanges.push(newChange);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return this._constrainRanges(mergedChanges);
|
|
446
|
+
}
|
|
447
|
+
_constrainRanges(changes) {
|
|
448
|
+
if (!this.ranges) {
|
|
449
|
+
return changes;
|
|
450
|
+
}
|
|
451
|
+
const constrainedChanges = [];
|
|
452
|
+
let changesIndex = 0;
|
|
453
|
+
let rangesIndex = 0;
|
|
454
|
+
while (changesIndex < changes.length && rangesIndex < this.ranges.length) {
|
|
455
|
+
const change = changes[changesIndex];
|
|
456
|
+
const range = this.ranges[rangesIndex];
|
|
457
|
+
if (change.newRangeEndOffset < range.startIndex) {
|
|
458
|
+
changesIndex++;
|
|
459
|
+
}
|
|
460
|
+
else if (change.newRangeStartOffset > range.endIndex) {
|
|
461
|
+
rangesIndex++;
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
const newRangeStartOffset = Math.max(change.newRangeStartOffset, range.startIndex);
|
|
465
|
+
const newRangeEndOffset = Math.min(change.newRangeEndOffset, range.endIndex);
|
|
466
|
+
const newRange = change.newRange.intersectRanges(( new Range(
|
|
467
|
+
range.startPosition.row + 1,
|
|
468
|
+
range.startPosition.column + 1,
|
|
469
|
+
range.endPosition.row + 1,
|
|
470
|
+
range.endPosition.column + 1
|
|
471
|
+
)));
|
|
472
|
+
constrainedChanges.push({
|
|
473
|
+
newRange,
|
|
474
|
+
newRangeEndOffset,
|
|
475
|
+
newRangeStartOffset
|
|
476
|
+
});
|
|
477
|
+
if (newRangeEndOffset < change.newRangeEndOffset) {
|
|
478
|
+
change.newRange = Range.fromPositions(newRange.getEndPosition(), change.newRange.getEndPosition());
|
|
479
|
+
change.newRangeStartOffset = newRangeEndOffset + 1;
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
changesIndex++;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return constrainedChanges;
|
|
487
|
+
}
|
|
488
|
+
onDidChangeContent(model, changes, ranges) {
|
|
489
|
+
const version = model.getVersionId();
|
|
490
|
+
if (version === this._editVersion) {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
let newRanges = [];
|
|
494
|
+
if (ranges) {
|
|
495
|
+
newRanges = this._setRanges(ranges);
|
|
496
|
+
}
|
|
497
|
+
if (changes && changes.length > 0) {
|
|
498
|
+
if (this._unfiredChanges) {
|
|
499
|
+
this._unfiredChanges.push(...changes);
|
|
500
|
+
}
|
|
501
|
+
else {
|
|
502
|
+
this._unfiredChanges = changes;
|
|
503
|
+
}
|
|
504
|
+
for (const change of changes) {
|
|
505
|
+
this._applyEdits(change.changes, version);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
this._applyEdits([], version);
|
|
510
|
+
}
|
|
511
|
+
this._onDidChangeContentQueue.queue(async () => {
|
|
512
|
+
if (this.isDisposed) {
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
const oldTree = this._lastFullyParsed;
|
|
516
|
+
let changedNodes;
|
|
517
|
+
if (this._lastFullyParsedWithEdits && this._lastFullyParsed) {
|
|
518
|
+
changedNodes = this.findChangedNodes(this._lastFullyParsedWithEdits, this._lastFullyParsed);
|
|
519
|
+
}
|
|
520
|
+
const completed = await this._parseAndUpdateTree(model, version);
|
|
521
|
+
if (completed) {
|
|
522
|
+
let ranges;
|
|
523
|
+
if (!changedNodes) {
|
|
524
|
+
if (this._ranges) {
|
|
525
|
+
ranges = ( this._ranges.map(r => ({ newRange: ( new Range(
|
|
526
|
+
r.startPosition.row + 1,
|
|
527
|
+
r.startPosition.column + 1,
|
|
528
|
+
r.endPosition.row + 1,
|
|
529
|
+
r.endPosition.column + 1
|
|
530
|
+
)), oldRangeLength: r.endIndex - r.startIndex, newRangeStartOffset: r.startIndex, newRangeEndOffset: r.endIndex })));
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
ranges = [{ newRange: model.getFullModelRange(), newRangeStartOffset: 0, newRangeEndOffset: model.getValueLength() }];
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
else if (oldTree && changedNodes) {
|
|
537
|
+
ranges = this.findTreeChanges(completed, changedNodes, newRanges);
|
|
538
|
+
}
|
|
539
|
+
const changes = this._unfiredChanges ?? [];
|
|
540
|
+
this._unfiredChanges = undefined;
|
|
541
|
+
this._onDidUpdate.fire({ language: this.languageId, ranges, versionId: version, tree: completed, includedModelChanges: changes });
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
_applyEdits(changes, version) {
|
|
546
|
+
for (const change of changes) {
|
|
547
|
+
const originalTextLength = TextLength.ofRange(Range.lift(change.range));
|
|
548
|
+
const newTextLength = TextLength.ofText(change.text);
|
|
549
|
+
const summedTextLengths = change.text.length === 0 ? newTextLength : originalTextLength.add(newTextLength);
|
|
550
|
+
const edit = {
|
|
551
|
+
startIndex: change.rangeOffset,
|
|
552
|
+
oldEndIndex: change.rangeOffset + change.rangeLength,
|
|
553
|
+
newEndIndex: change.rangeOffset + change.text.length,
|
|
554
|
+
startPosition: { row: change.range.startLineNumber - 1, column: change.range.startColumn - 1 },
|
|
555
|
+
oldEndPosition: { row: change.range.endLineNumber - 1, column: change.range.endColumn - 1 },
|
|
556
|
+
newEndPosition: { row: change.range.startLineNumber + summedTextLengths.lineCount - 1, column: summedTextLengths.lineCount ? summedTextLengths.columnCount : (change.range.endColumn + summedTextLengths.columnCount) }
|
|
557
|
+
};
|
|
558
|
+
this._tree?.edit(edit);
|
|
559
|
+
this._lastFullyParsedWithEdits?.edit(edit);
|
|
560
|
+
}
|
|
561
|
+
this._editVersion = version;
|
|
562
|
+
}
|
|
563
|
+
async _parseAndUpdateTree(model, version) {
|
|
564
|
+
const tree = await this._parse(model);
|
|
565
|
+
if (tree) {
|
|
566
|
+
this._tree?.delete();
|
|
567
|
+
this._tree = tree;
|
|
568
|
+
this._lastFullyParsed?.delete();
|
|
569
|
+
this._lastFullyParsed = tree.copy();
|
|
570
|
+
this._lastFullyParsedWithEdits?.delete();
|
|
571
|
+
this._lastFullyParsedWithEdits = tree.copy();
|
|
572
|
+
this._versionId = version;
|
|
573
|
+
return tree;
|
|
574
|
+
}
|
|
575
|
+
else if (!this._tree) {
|
|
576
|
+
this.parser.reset();
|
|
577
|
+
}
|
|
578
|
+
return undefined;
|
|
579
|
+
}
|
|
580
|
+
_parse(model) {
|
|
581
|
+
let parseType = TelemetryParseType.Full;
|
|
582
|
+
if (this.tree) {
|
|
583
|
+
parseType = TelemetryParseType.Incremental;
|
|
584
|
+
}
|
|
585
|
+
return this._parseAndYield(model, parseType);
|
|
586
|
+
}
|
|
587
|
+
async _parseAndYield(model, parseType) {
|
|
588
|
+
let time = 0;
|
|
589
|
+
let passes = 0;
|
|
590
|
+
const inProgressVersion = this._editVersion;
|
|
591
|
+
let newTree;
|
|
592
|
+
this._lastYieldTime = performance.now();
|
|
593
|
+
do {
|
|
594
|
+
const timer = performance.now();
|
|
595
|
+
try {
|
|
596
|
+
newTree = this.parser.parse((index, position) => this._parseCallback(model, index), this._tree, { progressCallback: this._parseProgressCallback.bind(this), includedRanges: this._ranges });
|
|
597
|
+
}
|
|
598
|
+
catch (e) {
|
|
599
|
+
}
|
|
600
|
+
finally {
|
|
601
|
+
time += performance.now() - timer;
|
|
602
|
+
passes++;
|
|
603
|
+
}
|
|
604
|
+
await ( new Promise(resolve => setTimeout0(resolve)));
|
|
605
|
+
} while (!model.isDisposed() && !this.isDisposed && !newTree && inProgressVersion === model.getVersionId());
|
|
606
|
+
this.sendParseTimeTelemetry(parseType, time, passes);
|
|
607
|
+
return (newTree && (inProgressVersion === model.getVersionId())) ? newTree : undefined;
|
|
608
|
+
}
|
|
609
|
+
_parseProgressCallback(state) {
|
|
610
|
+
const now = performance.now();
|
|
611
|
+
if (now - this._lastYieldTime > 50) {
|
|
612
|
+
this._lastYieldTime = now;
|
|
613
|
+
return true;
|
|
614
|
+
}
|
|
615
|
+
return false;
|
|
616
|
+
}
|
|
617
|
+
_parseCallback(textModel, index) {
|
|
618
|
+
try {
|
|
619
|
+
return textModel.getTextBuffer().getNearestChunk(index);
|
|
620
|
+
}
|
|
621
|
+
catch (e) {
|
|
622
|
+
this._logService.debug('Error getting chunk for tree-sitter parsing', e);
|
|
623
|
+
}
|
|
624
|
+
return undefined;
|
|
625
|
+
}
|
|
626
|
+
_setRanges(newRanges) {
|
|
627
|
+
const unKnownRanges = [];
|
|
628
|
+
if (this._ranges) {
|
|
629
|
+
for (const newRange of newRanges) {
|
|
630
|
+
let isFullyIncluded = false;
|
|
631
|
+
for (let i = 0; i < this._ranges.length; i++) {
|
|
632
|
+
const existingRange = this._ranges[i];
|
|
633
|
+
if (rangesEqual(existingRange, newRange) || rangesIntersect(existingRange, newRange)) {
|
|
634
|
+
isFullyIncluded = true;
|
|
635
|
+
break;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
if (!isFullyIncluded) {
|
|
639
|
+
unKnownRanges.push(newRange);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
else {
|
|
644
|
+
unKnownRanges.push(...newRanges);
|
|
645
|
+
}
|
|
646
|
+
this._ranges = newRanges;
|
|
647
|
+
return unKnownRanges;
|
|
648
|
+
}
|
|
649
|
+
get ranges() {
|
|
650
|
+
return this._ranges;
|
|
651
|
+
}
|
|
652
|
+
sendParseTimeTelemetry(parseType, time, passes) {
|
|
653
|
+
this._logService.debug(`Tree parsing (${parseType}) took ${time} ms and ${passes} passes.`);
|
|
654
|
+
if (parseType === TelemetryParseType.Full) {
|
|
655
|
+
this._telemetryService.publicLog2(`treeSitter.fullParse`, { languageId: this.languageId, time, passes });
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
this._telemetryService.publicLog2(`treeSitter.incrementalParse`, { languageId: this.languageId, time, passes });
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
function rangesEqual(a, b) {
|
|
663
|
+
return (a.startPosition.row === b.startPosition.row)
|
|
664
|
+
&& (a.startPosition.column === b.startPosition.column)
|
|
665
|
+
&& (a.endPosition.row === b.endPosition.row)
|
|
666
|
+
&& (a.endPosition.column === b.endPosition.column)
|
|
667
|
+
&& (a.startIndex === b.startIndex)
|
|
668
|
+
&& (a.endIndex === b.endIndex);
|
|
669
|
+
}
|
|
670
|
+
function rangesIntersect(a, b) {
|
|
671
|
+
return (a.startIndex <= b.startIndex && a.endIndex >= b.startIndex) ||
|
|
672
|
+
(b.startIndex <= a.startIndex && b.endIndex >= a.startIndex);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
export { TextModelTreeSitter, TreeSitterParseResult };
|