Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/platform/parser/node/parserServiceImpl.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 { WorkerWithRpcProxy } from '../../../util/node/worker';
7
import { Lazy } from '../../../util/vs/base/common/lazy';
8
import * as path from '../../../util/vs/base/common/path';
9
import { TreeSitterOffsetRange, TreeSitterPointRange } from './nodes';
10
import * as parser from './parserImpl';
11
import { IParserService, ParserWorkerTimeoutError, TreeSitterAST } from './parserService';
12
import { WASMLanguage, getWasmLanguage } from './treeSitterLanguages';
13
14
const workerPath = path.join(__dirname, 'worker2.js');
15
type ParserType = Omit<typeof parser, '_getNodeMatchingSelection'>;
16
17
export class ParserServiceImpl implements IParserService {
18
19
declare readonly _serviceBrand: undefined;
20
21
private _parser: WorkerOrLocal<ParserType>;
22
23
constructor(
24
useWorker: boolean
25
) {
26
this._parser = new WorkerOrLocal<ParserType>(parser, workerPath, useWorker);
27
}
28
29
dispose(): void {
30
this._parser.dispose();
31
}
32
33
getTreeSitterAST(textDocument: { readonly languageId: string; getText(): string }): TreeSitterAST | undefined {
34
const wasmLanguage = getWasmLanguage(textDocument.languageId);
35
if (!wasmLanguage) {
36
return undefined;
37
}
38
return this.getTreeSitterASTForWASMLanguage(wasmLanguage, textDocument.getText());
39
}
40
41
getTreeSitterASTForWASMLanguage(wasmLanguage: WASMLanguage, source: string): TreeSitterAST {
42
const parserProxy = this._parser.proxy;
43
return {
44
getFunctionBodies: () => parserProxy._getFunctionBodies(wasmLanguage, source),
45
getCoarseParentScope: (range: TreeSitterPointRange) => parserProxy._getCoarseParentScope(wasmLanguage, source, range),
46
getFixSelectionOfInterest: (range: TreeSitterPointRange, maxNumberOfLines: number) => parserProxy._getFixSelectionOfInterest(wasmLanguage, source, range, maxNumberOfLines),
47
getCallExpressions: (selection: TreeSitterOffsetRange) => parserProxy._getCallExpressions(wasmLanguage, source, selection),
48
getFunctionDefinitions: () => parserProxy._getFunctionDefinitions(wasmLanguage, source),
49
getClassReferences: (selection: TreeSitterOffsetRange) => parserProxy._getClassReferences(wasmLanguage, source, selection),
50
getClassDeclarations: () => parserProxy._getClassDeclarations(wasmLanguage, source),
51
getTypeDeclarations: () => parserProxy._getTypeDeclarations(wasmLanguage, source),
52
getTypeReferences: (selection: TreeSitterOffsetRange) => parserProxy._getTypeReferences(wasmLanguage, source, selection),
53
getSymbols: (selection: TreeSitterOffsetRange) => parserProxy._getSymbols(wasmLanguage, source, selection),
54
getDocumentableNodeIfOnIdentifier: (range: TreeSitterOffsetRange) => parserProxy._getDocumentableNodeIfOnIdentifier(wasmLanguage, source, range),
55
getTestableNode: (range: TreeSitterOffsetRange) => parserProxy._getTestableNode(wasmLanguage, source, range),
56
getTestableNodes: () => parserProxy._getTestableNodes(wasmLanguage, source),
57
getNodeToExplain: (range: TreeSitterOffsetRange) => parserProxy._getNodeToExplain(wasmLanguage, source, range),
58
getNodeToDocument: (range: TreeSitterOffsetRange) => parserProxy._getNodeToDocument(wasmLanguage, source, range),
59
getFineScopes: (selection: TreeSitterOffsetRange) => parserProxy._getFineScopes(wasmLanguage, source, selection),
60
getStructure: () => parserProxy._getStructure(wasmLanguage, source),
61
findLastTest: () => parserProxy._findLastTest(wasmLanguage, source),
62
getParseErrorCount: () => parserProxy._getParseErrorCount(wasmLanguage, source),
63
};
64
}
65
66
getSemanticChunkTree(wasmLanguage: WASMLanguage, source: string) {
67
return this._parser.proxy._getSemanticChunkTree(wasmLanguage, source);
68
}
69
70
getSemanticChunkNames(language: WASMLanguage, source: string) {
71
return this._parser.proxy._getSemanticChunkNames(language, source);
72
}
73
}
74
75
type Proxied<ProxyType> = {
76
[K in keyof ProxyType]: ProxyType[K] extends ((...args: infer Args) => infer R) ? (...args: Args) => Promise<Awaited<R>> : never;
77
};
78
79
const _workerCallTimeout = 3_000;
80
81
class WorkerOrLocal<T extends object> {
82
83
private readonly _local: T;
84
85
public get proxy(): Proxied<T> {
86
if (this._useWorker) {
87
return this._workerProxy;
88
}
89
return <any>this._local;
90
}
91
92
private _worker: Lazy<WorkerWithRpcProxy<T>>;
93
private readonly _workerProxy: Proxied<T>;
94
95
private _restart(): void {
96
if (this._worker.hasValue) {
97
this._worker.value.terminate();
98
}
99
this._worker = new Lazy(() => new WorkerWithRpcProxy<T>(this._workerPath, { name: 'Parser worker' }));
100
}
101
102
constructor(
103
local: T,
104
private readonly _workerPath: string,
105
private readonly _useWorker: boolean,
106
) {
107
this._local = new Proxy(local, {
108
get: (target, prop, receiver) => {
109
const originalMethod = (target as any)[prop];
110
if (typeof originalMethod !== 'function') {
111
return originalMethod;
112
}
113
114
return async (...args: any[]) => {
115
const result = await originalMethod.apply(target, viaJSON(args));
116
return viaJSON(result);
117
};
118
},
119
});
120
this._worker = new Lazy(() => new WorkerWithRpcProxy<T>(this._workerPath, { name: 'Parser worker' }));
121
this._workerProxy = this._createTimeoutProxy();
122
}
123
124
private _createTimeoutProxy(): Proxied<T> {
125
const self = this;
126
return new Proxy({} as Proxied<T>, {
127
get(_target, prop) {
128
return async (...args: unknown[]) => {
129
const timedOut = Symbol();
130
const workerProxy = self._worker.value.proxy;
131
const call = (workerProxy as any)[prop](...args);
132
let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
133
const timeoutPromise = new Promise<typeof timedOut>(resolve => {
134
timeoutHandle = setTimeout(() => resolve(timedOut), _workerCallTimeout);
135
});
136
try {
137
const result = await Promise.race([call, timeoutPromise]);
138
if (result === timedOut) {
139
self._restart();
140
throw new ParserWorkerTimeoutError();
141
}
142
return result;
143
} finally {
144
clearTimeout(timeoutHandle);
145
}
146
};
147
},
148
});
149
}
150
151
dispose(): void {
152
if (this._worker.hasValue) {
153
this._worker.value.terminate();
154
}
155
}
156
}
157
158
function viaJSON<T>(obj: T): T {
159
if (typeof obj === 'undefined') {
160
return obj;
161
}
162
return JSON.parse(JSON.stringify(obj));
163
}
164
165