@jpoly1219/context-extractor 0.2.8 → 0.2.9
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/dist/src/app.d.ts +7 -3
- package/dist/src/app.js +304 -101
- package/dist/src/ast.d.ts +6 -0
- package/dist/src/ast.js +80 -0
- package/dist/src/codeql.js +17 -7
- package/dist/src/constants.js +17 -7
- package/dist/src/core.d.ts +0 -1
- package/dist/src/core.js +17 -7
- package/dist/src/main.d.ts +8 -2
- package/dist/src/main.js +51 -13
- package/dist/src/ocaml-driver.d.ts +55 -5
- package/dist/src/ocaml-driver.js +295 -190
- package/dist/src/ocaml-utils/_build/default/test_parser.bc.cjs +194658 -0
- package/dist/src/runner.js +118 -3
- package/dist/src/tree-sitter-files/queries/hole-queries/typescript.scm +36 -0
- package/dist/src/tree-sitter-files/queries/relevant-headers-queries/typescript-get-toplevel-headers.scm +22 -0
- package/dist/src/tree-sitter-files/queries/relevant-types-queries/typescript-extract-identifiers.scm +10 -0
- package/dist/src/tree-sitter-files/queries/relevant-types-queries/typescript-find-typedecl-given-typeidentifier.scm +11 -0
- package/dist/src/tree-sitter-files/wasms/tree-sitter-ocaml.wasm +0 -0
- package/dist/src/tree-sitter-files/wasms/tree-sitter-typescript.wasm +0 -0
- package/dist/src/tree-sitter-files/wasms/tree-sitter.wasm +0 -0
- package/dist/src/tree-sitter.d.ts +40 -0
- package/dist/src/tree-sitter.js +311 -0
- package/dist/src/types.d.ts +79 -9
- package/dist/src/types.js +1 -6
- package/dist/src/typescript-driver.d.ts +81 -13
- package/dist/src/typescript-driver.js +1123 -237
- package/dist/src/typescript-type-checker.d.ts +6 -2
- package/dist/src/typescript-type-checker.js +222 -10
- package/dist/src/utils.d.ts +11 -1
- package/dist/src/utils.js +87 -10
- package/dist/src/vscode-ide.d.ts +29 -0
- package/dist/src/vscode-ide.js +161 -0
- package/package.json +12 -6
|
@@ -15,156 +15,344 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
25
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
36
|
exports.TypeScriptDriver = void 0;
|
|
37
|
+
const ts = __importStar(require("typescript"));
|
|
27
38
|
const fs = __importStar(require("fs"));
|
|
28
39
|
const path = __importStar(require("path"));
|
|
29
40
|
const child_process_1 = require("child_process");
|
|
30
41
|
const typescript_type_checker_1 = require("./typescript-type-checker");
|
|
31
42
|
const utils_1 = require("./utils");
|
|
43
|
+
const ast_1 = require("./ast");
|
|
44
|
+
const tree_sitter_1 = require("./tree-sitter");
|
|
32
45
|
class TypeScriptDriver {
|
|
33
|
-
constructor() {
|
|
46
|
+
constructor(ide, sources, projectRoot) {
|
|
34
47
|
this.typeChecker = new typescript_type_checker_1.TypeScriptTypeChecker();
|
|
48
|
+
this.ide = ide;
|
|
49
|
+
if (ide == "vscode") {
|
|
50
|
+
Promise.resolve().then(() => __importStar(require("./vscode-ide"))).then(module => this.vscodeImport = module);
|
|
51
|
+
}
|
|
52
|
+
// Initialize TypeScript compiler API.
|
|
53
|
+
this.tsCompilerProgram = this.typeChecker.createTsCompilerProgram(sources, projectRoot);
|
|
54
|
+
this.tsCompilerTypeChecker = this.tsCompilerProgram.getTypeChecker();
|
|
35
55
|
}
|
|
36
56
|
async init(lspClient, sketchPath) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
'
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
'
|
|
44
|
-
'
|
|
45
|
-
|
|
46
|
-
|
|
57
|
+
if (lspClient) {
|
|
58
|
+
const capabilities = {
|
|
59
|
+
'textDocument': {
|
|
60
|
+
'codeAction': { 'dynamicRegistration': true },
|
|
61
|
+
'codeLens': { 'dynamicRegistration': true },
|
|
62
|
+
'colorProvider': { 'dynamicRegistration': true },
|
|
63
|
+
'completion': {
|
|
64
|
+
'completionItem': {
|
|
65
|
+
'commitCharactersSupport': true,
|
|
66
|
+
'documentationFormat': ['markdown', 'plaintext'],
|
|
67
|
+
'snippetSupport': true
|
|
68
|
+
},
|
|
69
|
+
'completionItemKind': {
|
|
70
|
+
'valueSet': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]
|
|
71
|
+
},
|
|
72
|
+
'contextSupport': true,
|
|
73
|
+
'dynamicRegistration': true
|
|
47
74
|
},
|
|
48
|
-
'
|
|
49
|
-
|
|
75
|
+
'definition': { 'dynamicRegistration': true },
|
|
76
|
+
'documentHighlight': { 'dynamicRegistration': true },
|
|
77
|
+
'documentLink': { 'dynamicRegistration': true },
|
|
78
|
+
'documentSymbol': {
|
|
79
|
+
'dynamicRegistration': true,
|
|
80
|
+
'symbolKind': {
|
|
81
|
+
'valueSet': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]
|
|
82
|
+
}
|
|
50
83
|
},
|
|
51
|
-
'
|
|
52
|
-
'
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
'dynamicRegistration': true,
|
|
59
|
-
'
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
84
|
+
'formatting': { 'dynamicRegistration': true },
|
|
85
|
+
'hover': {
|
|
86
|
+
'contentFormat': ['markdown', 'plaintext'],
|
|
87
|
+
'dynamicRegistration': true
|
|
88
|
+
},
|
|
89
|
+
'implementation': { 'dynamicRegistration': true },
|
|
90
|
+
// 'inlayhint': { 'dynamicRegistration': true },
|
|
91
|
+
'onTypeFormatting': { 'dynamicRegistration': true },
|
|
92
|
+
'publishDiagnostics': { 'relatedInformation': true },
|
|
93
|
+
'rangeFormatting': { 'dynamicRegistration': true },
|
|
94
|
+
'references': { 'dynamicRegistration': true },
|
|
95
|
+
'rename': { 'dynamicRegistration': true },
|
|
96
|
+
'signatureHelp': {
|
|
97
|
+
'dynamicRegistration': true,
|
|
98
|
+
'signatureInformation': { 'documentationFormat': ['markdown', 'plaintext'] }
|
|
99
|
+
},
|
|
100
|
+
'synchronization': {
|
|
101
|
+
'didSave': true,
|
|
102
|
+
'dynamicRegistration': true,
|
|
103
|
+
'willSave': true,
|
|
104
|
+
'willSaveWaitUntil': true
|
|
105
|
+
},
|
|
106
|
+
'typeDefinition': { 'dynamicRegistration': true, 'linkSupport': true },
|
|
107
|
+
// 'typeHierarchy': { 'dynamicRegistration': true }
|
|
67
108
|
},
|
|
68
|
-
'
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
109
|
+
'workspace': {
|
|
110
|
+
'applyEdit': true,
|
|
111
|
+
'configuration': true,
|
|
112
|
+
'didChangeConfiguration': { 'dynamicRegistration': true },
|
|
113
|
+
'didChangeWatchedFiles': { 'dynamicRegistration': true },
|
|
114
|
+
'executeCommand': { 'dynamicRegistration': true },
|
|
115
|
+
'symbol': {
|
|
116
|
+
'dynamicRegistration': true,
|
|
117
|
+
'symbolKind': {
|
|
118
|
+
'valueSet': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]
|
|
119
|
+
}
|
|
120
|
+
}, 'workspaceEdit': { 'documentChanges': true },
|
|
121
|
+
'workspaceFolders': true
|
|
78
122
|
},
|
|
79
|
-
'
|
|
80
|
-
'
|
|
81
|
-
'dynamicRegistration': true,
|
|
82
|
-
'willSave': true,
|
|
83
|
-
'willSaveWaitUntil': true
|
|
123
|
+
'general': {
|
|
124
|
+
'positionEncodings': ['utf-8']
|
|
84
125
|
},
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
'
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
126
|
+
};
|
|
127
|
+
const rootPath = path.dirname(sketchPath);
|
|
128
|
+
const rootUri = `file://${rootPath}`;
|
|
129
|
+
const workspaceFolders = [{ 'name': 'context-extractor', 'uri': rootUri }];
|
|
130
|
+
await lspClient.initialize({
|
|
131
|
+
processId: process.pid,
|
|
132
|
+
capabilities: capabilities,
|
|
133
|
+
trace: 'off',
|
|
134
|
+
rootUri: null,
|
|
135
|
+
workspaceFolders: workspaceFolders,
|
|
136
|
+
initializationOptions: {
|
|
137
|
+
disableAutomaticTypingAcquisition: true,
|
|
138
|
+
preferences: {
|
|
139
|
+
includeInlayVariableTypeHints: true,
|
|
98
140
|
}
|
|
99
|
-
}, 'workspaceEdit': { 'documentChanges': true },
|
|
100
|
-
'workspaceFolders': true
|
|
101
|
-
},
|
|
102
|
-
'general': {
|
|
103
|
-
'positionEncodings': ['utf-8']
|
|
104
|
-
},
|
|
105
|
-
};
|
|
106
|
-
const rootPath = path.dirname(sketchPath);
|
|
107
|
-
const rootUri = `file://${rootPath}`;
|
|
108
|
-
const workspaceFolders = [{ 'name': 'context-extractor', 'uri': rootUri }];
|
|
109
|
-
await lspClient.initialize({
|
|
110
|
-
processId: process.pid,
|
|
111
|
-
capabilities: capabilities,
|
|
112
|
-
trace: 'off',
|
|
113
|
-
rootUri: null,
|
|
114
|
-
workspaceFolders: workspaceFolders,
|
|
115
|
-
initializationOptions: {
|
|
116
|
-
preferences: {
|
|
117
|
-
includeInlayVariableTypeHints: true
|
|
118
141
|
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
injectHole(sketchFilePath) {
|
|
146
|
+
const sketchDir = path.dirname(sketchFilePath);
|
|
147
|
+
const injectedSketchFilePath = path.join(sketchDir, "injected_sketch.ts");
|
|
148
|
+
const sketchFileContent = fs.readFileSync(sketchFilePath, "utf8");
|
|
149
|
+
const injectedSketchFileContent = `declare function _<T>(): T\n${sketchFileContent}`;
|
|
150
|
+
fs.writeFileSync(injectedSketchFilePath, injectedSketchFileContent);
|
|
121
151
|
}
|
|
122
|
-
async getHoleContext(lspClient, sketchFilePath) {
|
|
152
|
+
async getHoleContext(lspClient, sketchFilePath, logStream) {
|
|
153
|
+
if (logStream) {
|
|
154
|
+
logStream.write(`\n\n=*=*=*=*=*=[begin extracting hole context][${new Date().toISOString()}]\n\n`);
|
|
155
|
+
}
|
|
123
156
|
// For TypeScript programs, we need to inject the hole function before getting its context.
|
|
124
157
|
// NOTE: this can be abstracted to its own method?
|
|
125
158
|
const sketchDir = path.dirname(sketchFilePath);
|
|
126
159
|
const injectedSketchFilePath = path.join(sketchDir, "injected_sketch.ts");
|
|
127
160
|
const sketchFileContent = fs.readFileSync(sketchFilePath, "utf8");
|
|
128
161
|
const injectedSketchFileContent = `declare function _<T>(): T\n${sketchFileContent}`;
|
|
129
|
-
fs.writeFileSync(injectedSketchFilePath, injectedSketchFileContent);
|
|
130
|
-
//
|
|
131
|
-
fs.readdirSync(sketchDir).map(fileName => {
|
|
132
|
-
if (fs.lstatSync(path.join(sketchDir, fileName)).isFile()) {
|
|
133
|
-
lspClient.didOpen({
|
|
134
|
-
textDocument: {
|
|
135
|
-
uri: `file://${sketchDir}/${fileName}`,
|
|
136
|
-
languageId: "typescript",
|
|
137
|
-
text: fs.readFileSync(`${sketchDir}/${fileName}`).toString("ascii"),
|
|
138
|
-
version: 1
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
// Get hole context.
|
|
162
|
+
// fs.writeFileSync(injectedSketchFilePath, injectedSketchFileContent);
|
|
163
|
+
// Get the hole's position.
|
|
144
164
|
const holePattern = /_\(\)/;
|
|
145
165
|
const firstPatternIndex = injectedSketchFileContent.search(holePattern);
|
|
146
|
-
const linePosition = (injectedSketchFileContent
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
166
|
+
const linePosition = (injectedSketchFileContent
|
|
167
|
+
.substring(0, firstPatternIndex)
|
|
168
|
+
.match(/\n/g)).length;
|
|
169
|
+
const characterPosition = firstPatternIndex - (injectedSketchFileContent
|
|
170
|
+
.split("\n", linePosition)
|
|
171
|
+
.join("\n")
|
|
172
|
+
.length) - 1;
|
|
173
|
+
if (lspClient) {
|
|
174
|
+
// Sync client and server by notifying that
|
|
175
|
+
// the client has opened all the files
|
|
176
|
+
// inside the target directory.
|
|
177
|
+
// lspClient.didOpen({
|
|
178
|
+
// textDocument: {
|
|
179
|
+
// uri: injectedSketchFilePath,
|
|
180
|
+
// languageId: "typescript",
|
|
181
|
+
// text: injectedSketchFileContent,
|
|
182
|
+
// version: 1
|
|
183
|
+
// }
|
|
184
|
+
// });
|
|
185
|
+
// fs.readdirSync(sketchDir).map(fileName => {
|
|
186
|
+
// if (fs.lstatSync(path.join(sketchDir, fileName)).isFile()) {
|
|
187
|
+
// lspClient.didOpen({
|
|
188
|
+
// textDocument: {
|
|
189
|
+
// uri: `file://${sketchDir}/${fileName}`,
|
|
190
|
+
// languageId: "typescript",
|
|
191
|
+
// text: fs.readFileSync(`${sketchDir}/${fileName}`).toString("ascii"),
|
|
192
|
+
// version: 1
|
|
193
|
+
// }
|
|
194
|
+
// });
|
|
195
|
+
// }
|
|
196
|
+
// });
|
|
197
|
+
// await (async () => {
|
|
198
|
+
// return new Promise(resolve => setTimeout(resolve, 500))
|
|
199
|
+
// })();
|
|
200
|
+
// const blocker = () => {
|
|
201
|
+
// for (let i = 0; i < 10000; ++i) {
|
|
202
|
+
// console.log(i)
|
|
203
|
+
// }
|
|
204
|
+
// }
|
|
205
|
+
// blocker();
|
|
206
|
+
// console.log(characterPosition, linePosition)
|
|
207
|
+
console.time("hover");
|
|
208
|
+
const holeHoverResult = await lspClient.hover({
|
|
209
|
+
textDocument: {
|
|
210
|
+
uri: injectedSketchFilePath
|
|
211
|
+
},
|
|
212
|
+
position: {
|
|
213
|
+
character: characterPosition,
|
|
214
|
+
line: linePosition
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
console.timeEnd("hover");
|
|
218
|
+
const formattedHoverResult = holeHoverResult.contents.value.split("\n").reduce((acc, curr) => {
|
|
219
|
+
if (curr != "" && curr != "```typescript" && curr != "```") {
|
|
220
|
+
return acc + curr;
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
return acc;
|
|
224
|
+
}
|
|
225
|
+
}, "");
|
|
226
|
+
// console.log(formattedHoverResult)
|
|
227
|
+
// function _<(a: Apple, c: Cherry, b: Banana) => Cherry > (): (a: Apple, c: Cherry, b: Banana) => Cherry
|
|
228
|
+
const holeFunctionPattern = /(function _)(\<.+\>)(\(\): )(.+)/;
|
|
229
|
+
const match = formattedHoverResult.match(holeFunctionPattern);
|
|
230
|
+
const functionName = "_()";
|
|
231
|
+
// console.log("aaa")
|
|
232
|
+
const functionTypeSpan = match[4];
|
|
233
|
+
// console.log("bbb")
|
|
234
|
+
// Clean up and inject the true hole function without the generic type signature.
|
|
235
|
+
// NOTE: this can be abstracted to its own method?
|
|
236
|
+
const trueHoleFunction = `declare function _(): ${functionTypeSpan}`;
|
|
237
|
+
const trueInjectedSketchFileContent = `${trueHoleFunction}\n${sketchFileContent}`;
|
|
238
|
+
fs.writeFileSync(injectedSketchFilePath, trueInjectedSketchFileContent);
|
|
239
|
+
console.time("didChange");
|
|
240
|
+
lspClient.didChange({
|
|
241
|
+
textDocument: {
|
|
242
|
+
uri: `file://${injectedSketchFilePath}`,
|
|
243
|
+
version: 2
|
|
244
|
+
},
|
|
245
|
+
contentChanges: [{
|
|
246
|
+
text: trueInjectedSketchFileContent
|
|
247
|
+
}]
|
|
248
|
+
});
|
|
249
|
+
console.timeEnd("didChange");
|
|
250
|
+
console.time("documentSymbol");
|
|
251
|
+
const sketchSymbol = await lspClient.documentSymbol({
|
|
252
|
+
textDocument: {
|
|
253
|
+
uri: `file://${injectedSketchFilePath}`,
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
console.timeEnd("documentSymbol");
|
|
257
|
+
return {
|
|
258
|
+
fullHoverResult: formattedHoverResult,
|
|
259
|
+
functionName: functionName,
|
|
260
|
+
functionTypeSpan: functionTypeSpan,
|
|
261
|
+
linePosition: linePosition,
|
|
262
|
+
characterPosition: characterPosition,
|
|
263
|
+
holeTypeDefLinePos: 0,
|
|
264
|
+
holeTypeDefCharPos: "declare function _(): ".length,
|
|
265
|
+
// range: { start: { line: 0, character: 0 }, end: { line: 0, character: 52 } }
|
|
266
|
+
range: sketchSymbol[0].location.range,
|
|
267
|
+
source: `file://${injectedSketchFilePath}`,
|
|
268
|
+
trueHoleFunction: trueHoleFunction
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
const holeHoverResult = await this.vscodeImport.VsCode.hover({
|
|
273
|
+
filepath: injectedSketchFilePath,
|
|
274
|
+
position: {
|
|
275
|
+
line: linePosition,
|
|
276
|
+
character: characterPosition
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
const formattedHoverResult = holeHoverResult.text.split("\n").reduce((acc, curr) => {
|
|
280
|
+
if (curr != "" && curr != "```typescript" && curr != "```") {
|
|
281
|
+
return acc + curr;
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
return acc;
|
|
285
|
+
}
|
|
286
|
+
}, "");
|
|
287
|
+
const holeFunctionPattern = /(function _)(\<.+\>)(\(\): )(.+)/;
|
|
288
|
+
const match = formattedHoverResult.match(holeFunctionPattern);
|
|
289
|
+
const functionName = "_()";
|
|
290
|
+
const functionTypeSpan = match[4];
|
|
291
|
+
// Clean up and inject the true hole function without the generic type signature.
|
|
292
|
+
// NOTE: this can be abstracted to its own method?
|
|
293
|
+
const trueHoleFunction = `declare function _(): ${functionTypeSpan}`;
|
|
294
|
+
const trueInjectedSketchFileContent = `${trueHoleFunction}\n${sketchFileContent}`;
|
|
295
|
+
fs.writeFileSync(injectedSketchFilePath, trueInjectedSketchFileContent);
|
|
296
|
+
const sketchSymbol = await this.vscodeImport.VsCode.getDocumentSymbols({
|
|
297
|
+
filepath: injectedSketchFilePath
|
|
298
|
+
});
|
|
299
|
+
return {
|
|
300
|
+
fullHoverResult: formattedHoverResult,
|
|
301
|
+
functionName: functionName,
|
|
302
|
+
functionTypeSpan: functionTypeSpan,
|
|
303
|
+
linePosition: linePosition,
|
|
304
|
+
characterPosition: characterPosition,
|
|
305
|
+
holeTypeDefLinePos: 0,
|
|
306
|
+
holeTypeDefCharPos: "declare function _(): ".length,
|
|
307
|
+
range: sketchSymbol[0].range,
|
|
308
|
+
source: `file://${injectedSketchFilePath}`,
|
|
309
|
+
trueHoleFunction: trueHoleFunction
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
async getHoleContextWithCompilerAPI(sketchFilePath, logStream) {
|
|
314
|
+
if (logStream) {
|
|
315
|
+
// logStream.write("")
|
|
316
|
+
}
|
|
317
|
+
// For TypeScript programs, we need to inject the hole function before getting its context.
|
|
318
|
+
const sketchDir = path.dirname(sketchFilePath);
|
|
319
|
+
const injectedSketchFilePath = path.join(sketchDir, "injected_sketch.ts");
|
|
320
|
+
const sketchFileContent = fs.readFileSync(sketchFilePath, "utf8");
|
|
321
|
+
const injectedSketchFileContent = `declare function _<T>(): T\n${sketchFileContent}`;
|
|
322
|
+
// Get the hole's position.
|
|
323
|
+
const holePattern = /_\(\)/;
|
|
324
|
+
const firstPatternIndex = injectedSketchFileContent.search(holePattern);
|
|
325
|
+
const linePosition = (injectedSketchFileContent
|
|
326
|
+
.substring(0, firstPatternIndex)
|
|
327
|
+
.match(/\n/g)).length;
|
|
328
|
+
const characterPosition = firstPatternIndex - (injectedSketchFileContent
|
|
329
|
+
.split("\n", linePosition)
|
|
330
|
+
.join("\n")
|
|
331
|
+
.length) - 1;
|
|
332
|
+
// const sourceFile = ts.createSourceFile("sample.ts", injectedSketchFileContent, ts.ScriptTarget.Latest, true);
|
|
333
|
+
const sourceFile = this.tsCompilerProgram.getSourceFile(injectedSketchFilePath);
|
|
334
|
+
const position = ts.getPositionOfLineAndCharacter(sourceFile, linePosition, characterPosition);
|
|
335
|
+
function findNode(node) {
|
|
336
|
+
if (position >= node.getStart() && position <= node.getEnd()) {
|
|
337
|
+
// NOTE: this is probably unnecessary if characterPosition accounted for the (), not just _
|
|
338
|
+
if (node.getText() === "_()") {
|
|
339
|
+
return node;
|
|
340
|
+
}
|
|
341
|
+
return ts.forEachChild(node, findNode) || node;
|
|
163
342
|
}
|
|
164
|
-
}
|
|
343
|
+
}
|
|
344
|
+
const targetNode = findNode(sourceFile);
|
|
345
|
+
if (!(targetNode && ts.isCallExpression(targetNode))) {
|
|
346
|
+
console.log("Node not found or not a call expression.");
|
|
347
|
+
throw new Error("Node not found or not a call expression.");
|
|
348
|
+
}
|
|
349
|
+
const type = this.tsCompilerTypeChecker.getTypeAtLocation(targetNode);
|
|
350
|
+
const typeString = this.tsCompilerTypeChecker.typeToString(type);
|
|
351
|
+
const typeStr = `function _<${typeString}>(): ${typeString}`;
|
|
352
|
+
// console.log("TYPE:", typeStr);
|
|
165
353
|
// function _<(a: Apple, c: Cherry, b: Banana) => Cherry > (): (a: Apple, c: Cherry, b: Banana) => Cherry
|
|
166
354
|
const holeFunctionPattern = /(function _)(\<.+\>)(\(\): )(.+)/;
|
|
167
|
-
const match =
|
|
355
|
+
const match = typeStr.match(holeFunctionPattern);
|
|
168
356
|
const functionName = "_()";
|
|
169
357
|
const functionTypeSpan = match[4];
|
|
170
358
|
// Clean up and inject the true hole function without the generic type signature.
|
|
@@ -172,34 +360,93 @@ class TypeScriptDriver {
|
|
|
172
360
|
const trueHoleFunction = `declare function _(): ${functionTypeSpan}`;
|
|
173
361
|
const trueInjectedSketchFileContent = `${trueHoleFunction}\n${sketchFileContent}`;
|
|
174
362
|
fs.writeFileSync(injectedSketchFilePath, trueInjectedSketchFileContent);
|
|
175
|
-
lspClient.didChange({
|
|
176
|
-
textDocument: {
|
|
177
|
-
uri: `file://${injectedSketchFilePath}`,
|
|
178
|
-
version: 2
|
|
179
|
-
},
|
|
180
|
-
contentChanges: [{
|
|
181
|
-
text: trueInjectedSketchFileContent
|
|
182
|
-
}]
|
|
183
|
-
});
|
|
184
|
-
const sketchSymbol = await lspClient.documentSymbol({
|
|
185
|
-
textDocument: {
|
|
186
|
-
uri: `file://${injectedSketchFilePath}`,
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
363
|
return {
|
|
190
|
-
fullHoverResult:
|
|
364
|
+
fullHoverResult: typeStr,
|
|
191
365
|
functionName: functionName,
|
|
192
366
|
functionTypeSpan: functionTypeSpan,
|
|
193
367
|
linePosition: linePosition,
|
|
194
368
|
characterPosition: characterPosition,
|
|
195
369
|
holeTypeDefLinePos: 0,
|
|
196
370
|
holeTypeDefCharPos: "declare function _(): ".length,
|
|
197
|
-
|
|
198
|
-
range: sketchSymbol[0].location.range,
|
|
371
|
+
range: { start: { line: 0, character: 0 }, end: { line: 0, character: 52 } },
|
|
372
|
+
// range: (sketchSymbol![0] as SymbolInformation).location.range,
|
|
199
373
|
source: `file://${injectedSketchFilePath}`,
|
|
200
374
|
trueHoleFunction: trueHoleFunction
|
|
201
375
|
};
|
|
202
376
|
}
|
|
377
|
+
async getHoleContextWithTreesitter(sketchFilePath, cursorPosition, logStream) {
|
|
378
|
+
if (logStream) {
|
|
379
|
+
// logStream.write("")
|
|
380
|
+
}
|
|
381
|
+
// We need to inject the hole @ to trigger an error node.
|
|
382
|
+
const sketchFileContent = fs.readFileSync(sketchFilePath, "utf8");
|
|
383
|
+
const injectedContent = (0, utils_1.insertAtPosition)(sketchFileContent, cursorPosition, "@;");
|
|
384
|
+
// The hole's position is cursorPosition.
|
|
385
|
+
const snip = (0, utils_1.extractSnippet)(injectedContent, cursorPosition, { line: cursorPosition.line, character: cursorPosition.character + 2 });
|
|
386
|
+
// console.log(snip)
|
|
387
|
+
// Use treesitter to parse.
|
|
388
|
+
const ast = await (0, ast_1.getAst)(sketchFilePath, injectedContent);
|
|
389
|
+
if (!ast) {
|
|
390
|
+
throw new Error("failed to get ast");
|
|
391
|
+
}
|
|
392
|
+
const language = (0, tree_sitter_1.getFullLanguageName)(sketchFilePath);
|
|
393
|
+
const query = await (0, tree_sitter_1.getQueryForFile)(sketchFilePath, path.join(__dirname, "tree-sitter-files", "queries", "hole-queries", `${language}.scm`));
|
|
394
|
+
if (!query) {
|
|
395
|
+
throw new Error(`failed to get query for file ${sketchFilePath} and language ${language}`);
|
|
396
|
+
}
|
|
397
|
+
// const matches = query.matches(ast.rootNode);
|
|
398
|
+
// console.log(JSON.stringify(matches))
|
|
399
|
+
// for (const m of matches) {
|
|
400
|
+
// console.log(m)
|
|
401
|
+
// }
|
|
402
|
+
// for (const m of matches) {
|
|
403
|
+
// for (const c of m.captures) {
|
|
404
|
+
// const { name, node } = c;
|
|
405
|
+
// console.log(`${name} →`, node.text, node.startPosition, node.endPosition);
|
|
406
|
+
// }
|
|
407
|
+
// }
|
|
408
|
+
const captures = query.captures(ast.rootNode);
|
|
409
|
+
const res = {
|
|
410
|
+
fullHoverResult: "",
|
|
411
|
+
functionName: "",
|
|
412
|
+
functionTypeSpan: "",
|
|
413
|
+
linePosition: 0,
|
|
414
|
+
characterPosition: 0,
|
|
415
|
+
holeTypeDefLinePos: 0,
|
|
416
|
+
holeTypeDefCharPos: "declare function _(): ".length,
|
|
417
|
+
range: { start: { line: 0, character: 0 }, end: { line: 0, character: 52 } },
|
|
418
|
+
// range: (sketchSymbol![0] as SymbolInformation).location.range,
|
|
419
|
+
source: `file://${sketchFilePath}`,
|
|
420
|
+
trueHoleFunction: ""
|
|
421
|
+
};
|
|
422
|
+
for (const c of captures) {
|
|
423
|
+
const { name, node } = c;
|
|
424
|
+
// console.log(`${name} →`, node.text, node.startPosition, node.endPosition);
|
|
425
|
+
switch (name) {
|
|
426
|
+
case "function.decl": {
|
|
427
|
+
res.fullHoverResult = node.text;
|
|
428
|
+
}
|
|
429
|
+
case "function.name": {
|
|
430
|
+
res.functionName = node.text;
|
|
431
|
+
}
|
|
432
|
+
case "function.type": {
|
|
433
|
+
res.functionTypeSpan = node.text;
|
|
434
|
+
res.range = {
|
|
435
|
+
start: {
|
|
436
|
+
line: node.startPosition.row,
|
|
437
|
+
character: node.startPosition.column,
|
|
438
|
+
},
|
|
439
|
+
end: {
|
|
440
|
+
line: node.endPosition.row,
|
|
441
|
+
character: node.endPosition.column,
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return res;
|
|
448
|
+
}
|
|
449
|
+
// TODO: delete
|
|
203
450
|
async extractRelevantTypes1(lspClient, fullHoverResult, typeName, startLine, endLine, foundSoFar, // identifier -> [full hover result, source]
|
|
204
451
|
currentFile, foundTypeDefinitions, foundSymbols
|
|
205
452
|
// outputFile: fs.WriteStream,
|
|
@@ -294,87 +541,409 @@ class TypeScriptDriver {
|
|
|
294
541
|
return foundSoFar;
|
|
295
542
|
}
|
|
296
543
|
async extractRelevantTypes(lspClient, fullHoverResult, typeName, startLine, foundSoFar, // identifier -> [full hover result, source]
|
|
297
|
-
currentFile, foundContents // uri -> contents
|
|
298
|
-
) {
|
|
299
|
-
|
|
544
|
+
currentFile, foundContents, // uri -> contents
|
|
545
|
+
logStream) {
|
|
546
|
+
if (logStream) {
|
|
547
|
+
logStream.write(`\n\n=*=*=*=*=*=[begin extracting relevant headers][${new Date().toISOString()}]\n\n`);
|
|
548
|
+
}
|
|
549
|
+
// const content = fs.readFileSync(currentFile.slice(7), "utf8");
|
|
300
550
|
// console.log(content)
|
|
301
551
|
await this.extractRelevantTypesHelper(lspClient, fullHoverResult, typeName, startLine, foundSoFar, currentFile, foundContents, 0);
|
|
302
552
|
return foundSoFar;
|
|
303
553
|
}
|
|
554
|
+
async extractRelevantTypesWithTreesitter(lspClient, fullHoverResult, typeName, startLine, foundSoFar, // identifier -> [full hover result, source]
|
|
555
|
+
currentFile, foundContents, // uri -> contents
|
|
556
|
+
logStream) {
|
|
557
|
+
if (logStream) {
|
|
558
|
+
logStream.write(`\n\n=*=*=*=*=*=[begin extracting relevant headers][${new Date().toISOString()}]\n\n`);
|
|
559
|
+
}
|
|
560
|
+
await this.extractRelevantTypesHelperWithTreesitter(lspClient, fullHoverResult, typeName, startLine, foundSoFar, currentFile, foundContents, 0);
|
|
561
|
+
return foundSoFar;
|
|
562
|
+
}
|
|
304
563
|
async extractRelevantTypesHelper(lspClient, fullHoverResult, typeName, startLine, foundSoFar, // identifier -> [full hover result, source]
|
|
305
564
|
currentFile, foundContents, // uri -> contents
|
|
306
565
|
layer) {
|
|
307
|
-
|
|
566
|
+
if (lspClient) {
|
|
567
|
+
// console.log("===", fullHoverResult, layer, startLine, "===")
|
|
568
|
+
// Split the type span into identifiers, where each include the text, line number, and character range.
|
|
569
|
+
// For each identifier, invoke go to type definition.
|
|
570
|
+
if (!foundSoFar.has(typeName)) {
|
|
571
|
+
foundSoFar.set(typeName, { typeSpan: fullHoverResult, sourceFile: currentFile.slice(7) });
|
|
572
|
+
// console.log(fullHoverResult)
|
|
573
|
+
const identifiers = this.typeChecker.extractIdentifiers(fullHoverResult);
|
|
574
|
+
// DEBUG: REMOVE
|
|
575
|
+
// console.log("identifiers")
|
|
576
|
+
// console.log(fullHoverResult)
|
|
577
|
+
// console.dir(identifiers, { depth: null })
|
|
578
|
+
for (const identifier of identifiers) {
|
|
579
|
+
// console.log(`== loop ==`)
|
|
580
|
+
// console.dir(identifier, { depth: null })
|
|
581
|
+
// console.time(`loop ${identifier.name} layer ${layer}`)
|
|
582
|
+
// if (identifier.name === "_") {
|
|
583
|
+
// console.timeEnd(`loop ${identifier.name} layer ${layer}`)
|
|
584
|
+
// continue;
|
|
585
|
+
// };
|
|
586
|
+
// console.log(identifier)
|
|
587
|
+
// console.log(foundSoFar.has(identifier.name))
|
|
588
|
+
if (!foundSoFar.has(identifier.name)) {
|
|
589
|
+
try {
|
|
590
|
+
// const start = performance.now()
|
|
591
|
+
const typeDefinitionResult = await lspClient.typeDefinition({
|
|
592
|
+
textDocument: {
|
|
593
|
+
uri: currentFile
|
|
594
|
+
},
|
|
595
|
+
position: {
|
|
596
|
+
character: identifier.start,
|
|
597
|
+
line: startLine + identifier.line - 1 // startLine is already 1-indexed
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
// const end = performance.now()
|
|
601
|
+
// console.log(end - start)
|
|
602
|
+
// if (identifier.name == "Model") {
|
|
603
|
+
// console.log(identifier)
|
|
604
|
+
// console.dir(typeDefinitionResult, { depth: null })
|
|
605
|
+
// }
|
|
606
|
+
if (typeDefinitionResult && typeDefinitionResult instanceof Array && typeDefinitionResult.length != 0) {
|
|
607
|
+
const tdLocation = typeDefinitionResult[0];
|
|
608
|
+
let content = "";
|
|
609
|
+
if (foundContents.has(tdLocation.uri.slice(7))) {
|
|
610
|
+
content = foundContents.get(tdLocation.uri.slice(7));
|
|
611
|
+
}
|
|
612
|
+
else {
|
|
613
|
+
content = fs.readFileSync(tdLocation.uri.slice(7), "utf8");
|
|
614
|
+
foundContents.set(tdLocation.uri.slice(7), content);
|
|
615
|
+
}
|
|
616
|
+
// console.log(extractSnippet(content, { line: tdLocation.range.start.line, character: tdLocation.range.start.character }, { line: tdLocation.range.end.line, character: tdLocation.range.end.character }))
|
|
617
|
+
const decl = this.typeChecker.findDeclarationForIdentifier(content, tdLocation.range.start.line, tdLocation.range.start.character, tdLocation.range.end.character);
|
|
618
|
+
if (decl) {
|
|
619
|
+
// const ident = this.typeChecker.getIdentifierFromDecl(decl);
|
|
620
|
+
// console.log(ident == identifier.name, ident, identifier.name, decl)
|
|
621
|
+
// console.log(`Decl: ${decl} || Identifier: ${ident}`)
|
|
622
|
+
// console.timeEnd(`loop ${identifier.name} layer ${layer}`)
|
|
623
|
+
await this.extractRelevantTypesHelper(lspClient, decl, identifier.name, tdLocation.range.start.line, foundSoFar, tdLocation.uri, foundContents, layer + 1);
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
// console.log("decl not found")
|
|
627
|
+
// console.timeEnd(`loop ${identifier.name} layer ${layer}`)
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
// console.log("td not found")
|
|
632
|
+
// console.dir(typeDefinitionResult, { depth: null })
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
catch (err) {
|
|
636
|
+
console.log(err);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
else {
|
|
640
|
+
// console.timeEnd(`loop ${identifier.name} layer ${layer}`)
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
// TODO: Test this.
|
|
647
|
+
if (!foundSoFar.has(typeName)) {
|
|
648
|
+
foundSoFar.set(typeName, { typeSpan: fullHoverResult, sourceFile: currentFile.slice(7) });
|
|
649
|
+
const identifiers = this.typeChecker.extractIdentifiers(fullHoverResult);
|
|
650
|
+
for (const identifier of identifiers) {
|
|
651
|
+
if (!foundSoFar.has(identifier.name)) {
|
|
652
|
+
try {
|
|
653
|
+
const typeDefinitionResult = await this.vscodeImport.VsCode.gotoTypeDefinition({
|
|
654
|
+
filepath: currentFile,
|
|
655
|
+
position: {
|
|
656
|
+
line: startLine + identifier.line - 1,
|
|
657
|
+
character: identifier.start
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
if (typeDefinitionResult.length != 0) {
|
|
661
|
+
const tdLocation = typeDefinitionResult[0];
|
|
662
|
+
let content = "";
|
|
663
|
+
if (foundContents.has(tdLocation.filepath)) {
|
|
664
|
+
content = foundContents.get(tdLocation.filepath);
|
|
665
|
+
}
|
|
666
|
+
else {
|
|
667
|
+
content = fs.readFileSync(tdLocation.filepath, "utf8");
|
|
668
|
+
foundContents.set(tdLocation.filepath, content);
|
|
669
|
+
}
|
|
670
|
+
const decl = this.typeChecker.findDeclarationForIdentifier(content, tdLocation.range.start.line, tdLocation.range.start.character, tdLocation.range.end.character);
|
|
671
|
+
if (decl) {
|
|
672
|
+
await this.extractRelevantTypesHelper(lspClient, decl, identifier.name, tdLocation.range.start.line, foundSoFar, tdLocation.filepath, foundContents, layer + 1);
|
|
673
|
+
}
|
|
674
|
+
else {
|
|
675
|
+
console.log("decl not found");
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
else {
|
|
679
|
+
console.log("td not found");
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
catch (err) {
|
|
683
|
+
console.log(err);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
else {
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
async extractRelevantTypesWithCompilerAPI(fullHoverResult, typeName, linePosition, characterPosition, foundSoFar, // identifier -> [full hover result, source]
|
|
693
|
+
currentFile, foundContents, // uri -> contents
|
|
694
|
+
logStream) {
|
|
695
|
+
if (logStream) {
|
|
696
|
+
logStream.write(`\n\n=*=*=*=*=*=[begin extracting relevant headers][${new Date().toISOString()}]\n\n`);
|
|
697
|
+
}
|
|
698
|
+
// const content = fs.readFileSync(currentFile.slice(7), "utf8");
|
|
699
|
+
// console.log(content)
|
|
700
|
+
await this.extractRelevantTypesHelperWithCompilerAPI(fullHoverResult, typeName, linePosition, characterPosition, foundSoFar, currentFile, foundContents, 0);
|
|
701
|
+
return foundSoFar;
|
|
702
|
+
}
|
|
703
|
+
async extractRelevantTypesHelperWithCompilerAPI(fullHoverResult, typeName, linePosition, characterPosition, foundSoFar, // identifier -> [full hover result, source]
|
|
704
|
+
currentFile, foundContents, // uri -> contents
|
|
705
|
+
layer) {
|
|
706
|
+
var _a, _b;
|
|
707
|
+
// console.log("CURRENT TYPE:", typeName, fullHoverResult)
|
|
308
708
|
// Split the type span into identifiers, where each include the text, line number, and character range.
|
|
309
709
|
// For each identifier, invoke go to type definition.
|
|
310
710
|
if (!foundSoFar.has(typeName)) {
|
|
311
711
|
foundSoFar.set(typeName, { typeSpan: fullHoverResult, sourceFile: currentFile.slice(7) });
|
|
312
712
|
const identifiers = this.typeChecker.extractIdentifiers(fullHoverResult);
|
|
313
|
-
|
|
713
|
+
const sourceFile = this.tsCompilerProgram.getSourceFile(currentFile.slice(7));
|
|
314
714
|
for (const identifier of identifiers) {
|
|
315
|
-
// console.log(`== loop ==`)
|
|
316
|
-
// console.dir(identifier, { depth: null })
|
|
317
|
-
// console.time(`loop ${identifier.name} layer ${layer}`)
|
|
318
|
-
// if (identifier.name === "_") {
|
|
319
|
-
// console.timeEnd(`loop ${identifier.name} layer ${layer}`)
|
|
320
|
-
// continue;
|
|
321
|
-
// };
|
|
322
|
-
// console.log(identifier)
|
|
323
|
-
// console.log(foundSoFar.has(identifier.name))
|
|
324
715
|
if (!foundSoFar.has(identifier.name)) {
|
|
325
716
|
try {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
717
|
+
// console.log(identifier, linePosition, characterPosition, linePosition + identifier.line, identifier.start)
|
|
718
|
+
const position = ts.getPositionOfLineAndCharacter(sourceFile, linePosition + identifier.line - 1, identifier.start);
|
|
719
|
+
// const typeDefinitionResult = await lspClient.typeDefinition({
|
|
720
|
+
// textDocument: {
|
|
721
|
+
// uri: currentFile
|
|
722
|
+
// },
|
|
723
|
+
// position: {
|
|
724
|
+
// character: identifier.start,
|
|
725
|
+
// line: startLine + identifier.line - 1 // startLine is already 1-indexed
|
|
726
|
+
// }
|
|
727
|
+
// });
|
|
728
|
+
function findNodeAtPosition(node) {
|
|
729
|
+
if (position >= node.getStart() && position < node.getEnd()) {
|
|
730
|
+
return ts.forEachChild(node, findNodeAtPosition) || node;
|
|
333
731
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
content = foundContents.get(tdLocation.uri.slice(7));
|
|
732
|
+
return undefined;
|
|
733
|
+
}
|
|
734
|
+
function findIdentifierAtPosition(sourceFile, position) {
|
|
735
|
+
function find(node) {
|
|
736
|
+
if (ts.isIdentifier(node) && position >= node.getStart() && position <= node.getEnd()) {
|
|
737
|
+
return node;
|
|
738
|
+
}
|
|
739
|
+
return ts.forEachChild(node, find);
|
|
343
740
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
741
|
+
return find(sourceFile);
|
|
742
|
+
}
|
|
743
|
+
const node = findNodeAtPosition(sourceFile);
|
|
744
|
+
// const node = findIdentifierAtPosition(sourceFile, position);
|
|
745
|
+
if (!node) {
|
|
746
|
+
throw new Error("Node not found");
|
|
747
|
+
}
|
|
748
|
+
const possiblyPrimitiveType = this.tsCompilerTypeChecker.getTypeAtLocation(node);
|
|
749
|
+
if (possiblyPrimitiveType.flags & ts.TypeFlags.String ||
|
|
750
|
+
possiblyPrimitiveType.flags & ts.TypeFlags.Number ||
|
|
751
|
+
possiblyPrimitiveType.flags & ts.TypeFlags.Boolean ||
|
|
752
|
+
possiblyPrimitiveType.flags & ts.TypeFlags.Null ||
|
|
753
|
+
possiblyPrimitiveType.flags & ts.TypeFlags.Undefined ||
|
|
754
|
+
possiblyPrimitiveType.flags & ts.TypeFlags.Void ||
|
|
755
|
+
possiblyPrimitiveType.flags & ts.TypeFlags.ESSymbol ||
|
|
756
|
+
possiblyPrimitiveType.flags & ts.TypeFlags.ESSymbolLike ||
|
|
757
|
+
possiblyPrimitiveType.flags & ts.TypeFlags.UniqueESSymbol ||
|
|
758
|
+
possiblyPrimitiveType.flags & ts.TypeFlags.BigInt) {
|
|
759
|
+
// console.log("Primitive type", this.tsCompilerTypeChecker.typeToString(possiblyPrimitiveType))
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
const symbol = this.tsCompilerTypeChecker.getSymbolAtLocation(node);
|
|
763
|
+
if (!symbol || !((_a = symbol.declarations) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
764
|
+
throw new Error("Symbol not found");
|
|
765
|
+
}
|
|
766
|
+
const trueSymbol = symbol && symbol.flags & ts.SymbolFlags.Alias
|
|
767
|
+
? this.tsCompilerTypeChecker.getAliasedSymbol(symbol)
|
|
768
|
+
: symbol;
|
|
769
|
+
if ((_b = trueSymbol === null || trueSymbol === void 0 ? void 0 : trueSymbol.declarations) === null || _b === void 0 ? void 0 : _b.length) {
|
|
770
|
+
const decl = trueSymbol.declarations[0];
|
|
771
|
+
// console.log(decl)
|
|
772
|
+
if (ts.isTypeAliasDeclaration(decl)) {
|
|
773
|
+
// console.log("DECL TEXT", decl.getText())
|
|
774
|
+
// const rhs = decl.type;
|
|
775
|
+
// const typ = this.tsCompilerTypeChecker.getTypeAtLocation(rhs);
|
|
776
|
+
// const typStr = this.tsCompilerTypeChecker.typeToString(typ);
|
|
777
|
+
//
|
|
778
|
+
// console.log("Resolved type (RHS):", typStr);
|
|
779
|
+
// const rhsSource = rhs.getSourceFile();
|
|
780
|
+
// console.log(symbol.declarations)
|
|
781
|
+
// console.log("DECL", decl)
|
|
782
|
+
// console.log("TYPE AT LOC", this.tsCompilerTypeChecker.getTypeAtLocation(decl))
|
|
783
|
+
// console.log("TYPE TO STR", this.tsCompilerTypeChecker.typeToString(this.tsCompilerTypeChecker.getTypeAtLocation(decl)))
|
|
784
|
+
const declSourceFile = decl.getSourceFile();
|
|
785
|
+
const start = ts.getLineAndCharacterOfPosition(declSourceFile, decl.getStart());
|
|
786
|
+
const end = ts.getLineAndCharacterOfPosition(declSourceFile, decl.getEnd());
|
|
787
|
+
// console.log("LOCATION", start, end, declSourceFile.fileName)
|
|
788
|
+
await this.extractRelevantTypesHelperWithCompilerAPI(decl.getText(), identifier.name, start.line, start.character, foundSoFar, `file://${declSourceFile.fileName}`, foundContents, layer + 1);
|
|
347
789
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
catch (err) {
|
|
793
|
+
console.log(err);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
else {
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
async extractRelevantTypesHelperWithTreesitter(lspClient, fullHoverResult, typeName, startLine, foundSoFar, // identifier -> [full hover result, source]
|
|
802
|
+
currentFile, foundContents, // uri -> contents
|
|
803
|
+
layer) {
|
|
804
|
+
if (lspClient) {
|
|
805
|
+
// Split the type span into identifiers, where each include the text, line number, and character range.
|
|
806
|
+
// For each identifier, invoke go to type definition.
|
|
807
|
+
if (!foundSoFar.has(typeName)) {
|
|
808
|
+
// const identifiers = this.typeChecker.extractIdentifiers(fullHoverResult);
|
|
809
|
+
// const currentFileContent = fs.readFileSync(currentFile, "utf8");
|
|
810
|
+
const ast = await (0, ast_1.getAst)(currentFile, fullHoverResult);
|
|
811
|
+
if (!ast) {
|
|
812
|
+
throw new Error(`failed to get ast for file ${currentFile}`);
|
|
813
|
+
}
|
|
814
|
+
foundSoFar.set(typeName, { typeSpan: fullHoverResult, sourceFile: currentFile.slice(7), ast: ast });
|
|
815
|
+
const language = (0, tree_sitter_1.getFullLanguageName)(currentFile);
|
|
816
|
+
const query = await (0, tree_sitter_1.getQueryForFile)(currentFile, path.join(__dirname, "tree-sitter-files", "queries", "relevant-types-queries", `${language}-extract-identifiers.scm`));
|
|
817
|
+
if (!query) {
|
|
818
|
+
throw new Error(`failed to get query for file ${currentFile} and language ${language}`);
|
|
819
|
+
}
|
|
820
|
+
const identifiers = query.captures(ast.rootNode);
|
|
821
|
+
for (const { name, node } of identifiers) {
|
|
822
|
+
// console.log(`${name} →`, node.text, node.startPosition, node.endPosition);
|
|
823
|
+
if (!foundSoFar.has(node.text)) {
|
|
824
|
+
try {
|
|
825
|
+
const typeDefinitionResult = await lspClient.typeDefinition({
|
|
826
|
+
textDocument: {
|
|
827
|
+
uri: currentFile
|
|
828
|
+
},
|
|
829
|
+
position: {
|
|
830
|
+
character: node.startPosition.column,
|
|
831
|
+
line: startLine + node.startPosition.row
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
if (typeDefinitionResult && typeDefinitionResult instanceof Array && typeDefinitionResult.length != 0) {
|
|
835
|
+
const tdLocation = typeDefinitionResult[0];
|
|
836
|
+
let content = "";
|
|
837
|
+
if (foundContents.has(tdLocation.uri.slice(7))) {
|
|
838
|
+
content = foundContents.get(tdLocation.uri.slice(7));
|
|
839
|
+
}
|
|
840
|
+
else {
|
|
841
|
+
content = fs.readFileSync(tdLocation.uri.slice(7), "utf8");
|
|
842
|
+
foundContents.set(tdLocation.uri.slice(7), content);
|
|
843
|
+
}
|
|
844
|
+
const ast = await (0, ast_1.getAst)(tdLocation.uri, content);
|
|
845
|
+
if (!ast) {
|
|
846
|
+
throw new Error(`failed to get ast for file ${tdLocation.uri}`);
|
|
847
|
+
}
|
|
848
|
+
const decl = (0, tree_sitter_1.findEnclosingTypeDeclaration)(content, tdLocation.range.start.line, tdLocation.range.start.character, ast);
|
|
849
|
+
if (!decl) {
|
|
850
|
+
// throw new Error(`failed to get decl for file ${tdLocation.uri}`);
|
|
851
|
+
console.log(`failed to get decl for file ${tdLocation.uri}`);
|
|
852
|
+
}
|
|
853
|
+
if (decl) {
|
|
854
|
+
await this.extractRelevantTypesHelperWithTreesitter(lspClient, decl.fullText, node.text, tdLocation.range.start.line, foundSoFar, tdLocation.uri, foundContents, layer + 1);
|
|
855
|
+
}
|
|
856
|
+
else {
|
|
857
|
+
// console.log("decl not found")
|
|
858
|
+
}
|
|
355
859
|
}
|
|
356
860
|
else {
|
|
357
|
-
console.log("
|
|
358
|
-
// console.
|
|
861
|
+
// console.log("td not found")
|
|
862
|
+
// console.dir(typeDefinitionResult, { depth: null })
|
|
359
863
|
}
|
|
360
864
|
}
|
|
361
|
-
|
|
362
|
-
console.log(
|
|
363
|
-
// console.dir(typeDefinitionResult, { depth: null })
|
|
865
|
+
catch (err) {
|
|
866
|
+
console.log(err);
|
|
364
867
|
}
|
|
365
868
|
}
|
|
366
|
-
|
|
367
|
-
console.log(
|
|
869
|
+
else {
|
|
870
|
+
// console.log(`foundSoFar has ${node.text}`)
|
|
368
871
|
}
|
|
369
872
|
}
|
|
370
|
-
|
|
371
|
-
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
else {
|
|
876
|
+
// TODO: Test this.
|
|
877
|
+
if (!foundSoFar.has(typeName)) {
|
|
878
|
+
const ast = await (0, ast_1.getAst)(currentFile, fullHoverResult);
|
|
879
|
+
if (!ast) {
|
|
880
|
+
throw new Error(`failed to get ast for file ${currentFile}`);
|
|
881
|
+
}
|
|
882
|
+
foundSoFar.set(typeName, { typeSpan: fullHoverResult, sourceFile: currentFile.slice(7), ast: ast });
|
|
883
|
+
const language = (0, tree_sitter_1.getFullLanguageName)(currentFile);
|
|
884
|
+
const query = await (0, tree_sitter_1.getQueryForFile)(currentFile, path.join(__dirname, "tree-sitter-files", "queries", "relevant-headers-queries", `${language}-extract-identifiers.scm`));
|
|
885
|
+
if (!query) {
|
|
886
|
+
throw new Error(`failed to get query for file ${currentFile} and language ${language}`);
|
|
887
|
+
}
|
|
888
|
+
const identifiers = query.captures(ast.rootNode);
|
|
889
|
+
for (const { name, node } of identifiers) {
|
|
890
|
+
if (!foundSoFar.has(node.text)) {
|
|
891
|
+
try {
|
|
892
|
+
const typeDefinitionResult = await this.vscodeImport.VsCode.gotoTypeDefinition({
|
|
893
|
+
filepath: currentFile,
|
|
894
|
+
position: {
|
|
895
|
+
character: node.startPosition.column,
|
|
896
|
+
line: startLine + node.startPosition.row
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
if (typeDefinitionResult.length != 0) {
|
|
900
|
+
const tdLocation = typeDefinitionResult[0];
|
|
901
|
+
let content = "";
|
|
902
|
+
if (foundContents.has(tdLocation.filepath)) {
|
|
903
|
+
content = foundContents.get(tdLocation.filepath);
|
|
904
|
+
}
|
|
905
|
+
else {
|
|
906
|
+
content = fs.readFileSync(tdLocation.filepath, "utf8");
|
|
907
|
+
foundContents.set(tdLocation.filepath, content);
|
|
908
|
+
}
|
|
909
|
+
const ast = await (0, ast_1.getAst)(tdLocation.filepath, content);
|
|
910
|
+
if (!ast) {
|
|
911
|
+
throw new Error(`failed to get ast for file ${tdLocation.filepath}`);
|
|
912
|
+
}
|
|
913
|
+
const decl = (0, tree_sitter_1.findEnclosingTypeDeclaration)(content, tdLocation.range.start.line, tdLocation.range.start.character, ast);
|
|
914
|
+
if (!decl) {
|
|
915
|
+
// throw new Error(`failed to get decl for file ${tdLocation.uri}`);
|
|
916
|
+
console.log(`failed to get decl for file ${tdLocation.uri}`);
|
|
917
|
+
}
|
|
918
|
+
if (decl) {
|
|
919
|
+
await this.extractRelevantTypesHelper(lspClient, decl.fullText, node.text, tdLocation.range.start.line, foundSoFar, tdLocation.filepath, foundContents, layer + 1);
|
|
920
|
+
}
|
|
921
|
+
else {
|
|
922
|
+
// console.log("decl not found");
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
else {
|
|
926
|
+
// console.log("td not found");
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
catch (err) {
|
|
930
|
+
console.log(err);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
else {
|
|
934
|
+
}
|
|
372
935
|
}
|
|
373
936
|
}
|
|
374
937
|
}
|
|
375
938
|
}
|
|
376
|
-
async extractRelevantHeaders(_, sources, relevantTypes, holeType) {
|
|
939
|
+
async extractRelevantHeaders(_, sources, relevantTypes, holeType, projectRoot) {
|
|
940
|
+
// console.log("extractRelevantHeaders")
|
|
377
941
|
// console.time("extractRelevantHeaders");
|
|
942
|
+
// NOTE: This takes around 550ms.
|
|
943
|
+
// TODO: Move this to the init method.
|
|
944
|
+
// const program = this.typeChecker.createTsCompilerProgram(sources, projectRoot);
|
|
945
|
+
// const checker = program.getTypeChecker();
|
|
946
|
+
// NOTE: program = this.typeChecker.createProgramFromSource("")
|
|
378
947
|
const relevantContext = new Set();
|
|
379
948
|
// NOTE: This is necessary because TypeScript sucks.
|
|
380
949
|
// There is no way to compare objects by value,
|
|
@@ -382,35 +951,81 @@ class TypeScriptDriver {
|
|
|
382
951
|
const relevantContextMap = new Map();
|
|
383
952
|
const trace = [];
|
|
384
953
|
const foundNormalForms = new Map();
|
|
385
|
-
const
|
|
954
|
+
const foundTypeAnalysisResults = new Map();
|
|
955
|
+
// TODO: As long as you have a this binding to the program and the checker, you don't have to pass it around.
|
|
956
|
+
const targetTypes = this.generateTargetTypes(relevantTypes, holeType, this.tsCompilerProgram, this.tsCompilerTypeChecker);
|
|
957
|
+
// console.log("root", projectRoot)
|
|
958
|
+
const seenDecls = new Set();
|
|
386
959
|
// only consider lines that start with let or const
|
|
387
960
|
for (const source of sources) {
|
|
388
961
|
const sourceContent = fs.readFileSync(source).toString("utf8");
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
//
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
}
|
|
962
|
+
// TODO: this can be replaced by using typescript compiler api
|
|
963
|
+
// what really needs to happen is the following:
|
|
964
|
+
// filter by variable and function decls
|
|
965
|
+
// get a d.ts of them (or get a type decl)
|
|
966
|
+
// type decl makes more sense because d.ts format is a bit weird with class methods
|
|
967
|
+
// const start = performance.now()
|
|
968
|
+
const varFuncDecls = this.typeChecker.findTopLevelDeclarations(this.tsCompilerProgram, this.tsCompilerTypeChecker, source);
|
|
969
|
+
// const end = performance.now()
|
|
970
|
+
// console.log("varFuncDecls")
|
|
971
|
+
// console.log(varFuncDecls)
|
|
972
|
+
varFuncDecls.forEach((decl) => {
|
|
973
|
+
// TODO: Memoize decls that are already seen. (update: this moves 10ms from normalize2 to isTypeEquivalent.)
|
|
974
|
+
// const typeAnalysisResult = this.typeChecker.analyzeTypeString(decl.type);
|
|
975
|
+
// console.log(`typeAnalysisResult: ${JSON.stringify(typeAnalysisResult, null, 2)}`)
|
|
976
|
+
// NOTE: debugging
|
|
977
|
+
// console.log("decl**")
|
|
978
|
+
// console.dir(decl, { depth: null })
|
|
979
|
+
const declStr = JSON.stringify(decl);
|
|
980
|
+
if (!seenDecls.has(declStr)) {
|
|
981
|
+
this.extractRelevantHeadersHelper2(decl.declarationText, decl.returnType ? decl.returnType : decl.type, targetTypes, relevantTypes, relevantContext, source, relevantContextMap, trace, foundNormalForms, foundTypeAnalysisResults, this.tsCompilerProgram, this.tsCompilerTypeChecker);
|
|
982
|
+
seenDecls.add(declStr);
|
|
411
983
|
}
|
|
412
|
-
// console.timeEnd(`helper, line: ${line}`);
|
|
413
984
|
});
|
|
985
|
+
// const filteredLines = sourceContent.split("\n").filter((line) => {
|
|
986
|
+
// return line.slice(0, 3) === "let" || line.slice(0, 5) === "const";
|
|
987
|
+
// });
|
|
988
|
+
//
|
|
989
|
+
// // check for relationship between each line and relevant types
|
|
990
|
+
// filteredLines.forEach(line => {
|
|
991
|
+
//
|
|
992
|
+
// // console.time(`helper, line: ${line}`);
|
|
993
|
+
//
|
|
994
|
+
// let tag = false;
|
|
995
|
+
// // if (line === `const initFormState: [[Weekday, TimeOfDay], string] = [["M", "AM"], ""];`) {
|
|
996
|
+
// // tag = true;
|
|
997
|
+
// // }
|
|
998
|
+
// // TODO: Use the compiler API to split this.
|
|
999
|
+
// const splittedLine = line.split(" = ")[0];
|
|
1000
|
+
//
|
|
1001
|
+
// const typeSpanPattern = /(^[^:]*: )(.+)/;
|
|
1002
|
+
// const regexMatch = splittedLine.match(typeSpanPattern)
|
|
1003
|
+
// if (regexMatch) {
|
|
1004
|
+
// const returnTypeSpan = regexMatch[2];
|
|
1005
|
+
// // console.log(`returnTypeSpan: ${returnTypeSpan}`)
|
|
1006
|
+
//
|
|
1007
|
+
// // const typeAnalysisResult = this.typeChecker.analyzeTypeString(returnTypeSpan);
|
|
1008
|
+
// // console.log(`typeAnalysisResult: ${JSON.stringify(typeAnalysisResult, null, 2)}`)
|
|
1009
|
+
//
|
|
1010
|
+
// if (!this.typeChecker.isPrimitive(returnTypeSpan.split(" => ")[1])) {
|
|
1011
|
+
// this.extractRelevantHeadersHelper(
|
|
1012
|
+
// returnTypeSpan,
|
|
1013
|
+
// targetTypes,
|
|
1014
|
+
// relevantTypes,
|
|
1015
|
+
// relevantContext,
|
|
1016
|
+
// splittedLine,
|
|
1017
|
+
// source,
|
|
1018
|
+
// relevantContextMap,
|
|
1019
|
+
// tag,
|
|
1020
|
+
// trace,
|
|
1021
|
+
// foundNormalForms,
|
|
1022
|
+
// foundTypeAnalysisResults
|
|
1023
|
+
// );
|
|
1024
|
+
// }
|
|
1025
|
+
// }
|
|
1026
|
+
//
|
|
1027
|
+
// // console.timeEnd(`helper, line: ${line}`);
|
|
1028
|
+
// });
|
|
414
1029
|
}
|
|
415
1030
|
// console.log(JSON.stringify(relevantContextMap, null, 2))
|
|
416
1031
|
// console.log(relevantContextMap.keys())
|
|
@@ -420,11 +1035,11 @@ class TypeScriptDriver {
|
|
|
420
1035
|
// console.timeEnd("extractRelevantHeaders");
|
|
421
1036
|
return relevantContext;
|
|
422
1037
|
}
|
|
423
|
-
generateTargetTypes(relevantTypes, holeType) {
|
|
1038
|
+
generateTargetTypes(relevantTypes, holeType, program, checker) {
|
|
424
1039
|
// console.time("generateTargetTypes");
|
|
425
1040
|
const targetTypes = new Set();
|
|
426
1041
|
targetTypes.add(holeType);
|
|
427
|
-
this.generateTargetTypesHelper(relevantTypes, holeType, targetTypes);
|
|
1042
|
+
this.generateTargetTypesHelper(relevantTypes, holeType, targetTypes, program, checker);
|
|
428
1043
|
// console.timeEnd("generateTargetTypes");
|
|
429
1044
|
return targetTypes;
|
|
430
1045
|
}
|
|
@@ -454,25 +1069,25 @@ class TypeScriptDriver {
|
|
|
454
1069
|
// }
|
|
455
1070
|
// }
|
|
456
1071
|
// }
|
|
457
|
-
generateTargetTypesHelper(relevantTypes, currType, targetTypes) {
|
|
1072
|
+
generateTargetTypesHelper(relevantTypes, currType, targetTypes, program, checker) {
|
|
458
1073
|
// Run analysis on currType.
|
|
459
|
-
const typeAnalysisResult = this.typeChecker.analyzeTypeString(currType);
|
|
1074
|
+
const typeAnalysisResult = this.typeChecker.analyzeTypeString(currType, program, checker);
|
|
460
1075
|
// Match on its kind.
|
|
461
1076
|
if (this.typeChecker.isFunction2(typeAnalysisResult)) {
|
|
462
1077
|
const rettype = typeAnalysisResult.returnType;
|
|
463
1078
|
targetTypes.add(rettype.text);
|
|
464
|
-
this.generateTargetTypesHelper(relevantTypes, rettype.text, targetTypes);
|
|
1079
|
+
this.generateTargetTypesHelper(relevantTypes, rettype.text, targetTypes, program, checker);
|
|
465
1080
|
}
|
|
466
1081
|
else if (this.typeChecker.isTuple2(typeAnalysisResult)) {
|
|
467
1082
|
typeAnalysisResult.constituents.forEach(constituent => {
|
|
468
1083
|
targetTypes.add(constituent.text);
|
|
469
|
-
this.generateTargetTypesHelper(relevantTypes, constituent.text, targetTypes);
|
|
1084
|
+
this.generateTargetTypesHelper(relevantTypes, constituent.text, targetTypes, program, checker);
|
|
470
1085
|
});
|
|
471
1086
|
}
|
|
472
1087
|
else {
|
|
473
1088
|
if (relevantTypes.has(currType)) {
|
|
474
1089
|
const definition = relevantTypes.get(currType).typeSpan.split(" = ")[1];
|
|
475
|
-
this.generateTargetTypesHelper(relevantTypes, definition, targetTypes);
|
|
1090
|
+
this.generateTargetTypesHelper(relevantTypes, definition, targetTypes, program, checker);
|
|
476
1091
|
}
|
|
477
1092
|
}
|
|
478
1093
|
}
|
|
@@ -504,17 +1119,23 @@ class TypeScriptDriver {
|
|
|
504
1119
|
// }
|
|
505
1120
|
// });
|
|
506
1121
|
// }
|
|
507
|
-
extractRelevantHeadersHelper(typeSpan, targetTypes, relevantTypes, relevantContext, line, source, relevantContextMap, tag, trace, foundNormalForms
|
|
1122
|
+
extractRelevantHeadersHelper(typeSpan, targetTypes, relevantTypes, relevantContext, line, source, relevantContextMap, tag, trace, foundNormalForms, foundTypeAnalysisResults, // filename+typeSpan -> typeAnalysisResult
|
|
1123
|
+
program, checker) {
|
|
508
1124
|
if (tag) {
|
|
509
1125
|
// console.time(`extractRelevantHeadersHelper, typeSpan: ${typeSpan}`)
|
|
510
1126
|
// trace.push(typeSpan)
|
|
511
1127
|
// console.log(trace)
|
|
512
1128
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
1129
|
+
let typeAnalysisResult;
|
|
1130
|
+
if (!foundTypeAnalysisResults.has(source + ":" + typeSpan)) {
|
|
1131
|
+
typeAnalysisResult = this.typeChecker.analyzeTypeString(typeSpan, program, checker);
|
|
1132
|
+
foundTypeAnalysisResults.set(source + ":" + typeSpan, typeAnalysisResult);
|
|
1133
|
+
}
|
|
1134
|
+
else {
|
|
1135
|
+
typeAnalysisResult = foundTypeAnalysisResults.get(source + ":" + typeSpan);
|
|
1136
|
+
}
|
|
516
1137
|
targetTypes.forEach(typ => {
|
|
517
|
-
if (this.isTypeEquivalent(typeSpan, typ, relevantTypes, foundNormalForms)) {
|
|
1138
|
+
if (this.isTypeEquivalent(typeSpan, typ, relevantTypes, foundNormalForms, program, checker)) {
|
|
518
1139
|
// NOTE: This checks for dupes. ctx is an object so you need to check for each field.
|
|
519
1140
|
// relevantContext.add({ typeSpan: line, sourceFile: source });
|
|
520
1141
|
const ctx = { typeSpan: line, sourceFile: source };
|
|
@@ -522,11 +1143,11 @@ class TypeScriptDriver {
|
|
|
522
1143
|
}
|
|
523
1144
|
if (this.typeChecker.isFunction2(typeAnalysisResult)) {
|
|
524
1145
|
const rettype = typeAnalysisResult.returnType;
|
|
525
|
-
this.extractRelevantHeadersHelper(rettype.text, targetTypes, relevantTypes, relevantContext, line, source, relevantContextMap, tag, trace, foundNormalForms);
|
|
1146
|
+
this.extractRelevantHeadersHelper(rettype.text, targetTypes, relevantTypes, relevantContext, line, source, relevantContextMap, tag, trace, foundNormalForms, foundTypeAnalysisResults, program, checker);
|
|
526
1147
|
}
|
|
527
1148
|
else if (this.typeChecker.isTuple2(typeAnalysisResult)) {
|
|
528
1149
|
typeAnalysisResult.constituents.forEach(constituent => {
|
|
529
|
-
this.extractRelevantHeadersHelper(constituent.text, targetTypes, relevantTypes, relevantContext, line, source, relevantContextMap, tag, trace, foundNormalForms);
|
|
1150
|
+
this.extractRelevantHeadersHelper(constituent.text, targetTypes, relevantTypes, relevantContext, line, source, relevantContextMap, tag, trace, foundNormalForms, foundTypeAnalysisResults, program, checker);
|
|
530
1151
|
});
|
|
531
1152
|
}
|
|
532
1153
|
});
|
|
@@ -536,30 +1157,35 @@ class TypeScriptDriver {
|
|
|
536
1157
|
}
|
|
537
1158
|
}
|
|
538
1159
|
// two types are equivalent if they have the same normal forms
|
|
539
|
-
|
|
1160
|
+
// TODO: Create a memo of comparisons made.
|
|
1161
|
+
isTypeEquivalent(t1, t2, relevantTypes, foundNormalForms, program, checker) {
|
|
540
1162
|
// NOTE: BUGFIX
|
|
541
|
-
// console.log(`isTypeEquivalent: ${t1}
|
|
1163
|
+
// console.log(`isTypeEquivalent: ${t1} {}{} ${t2}`)
|
|
542
1164
|
// console.log(t1 == undefined)
|
|
543
1165
|
// console.log(t2 == undefined)
|
|
544
1166
|
let normT1 = "";
|
|
545
1167
|
let normT2 = "";
|
|
546
1168
|
if (foundNormalForms.has(t1)) {
|
|
1169
|
+
// console.log("found t1", true)
|
|
547
1170
|
normT1 = foundNormalForms.get(t1);
|
|
548
1171
|
}
|
|
549
1172
|
else {
|
|
550
|
-
|
|
1173
|
+
// console.log("not found t1", false)
|
|
1174
|
+
normT1 = this.normalize2(t1, relevantTypes, program, checker);
|
|
551
1175
|
foundNormalForms.set(t1, normT1);
|
|
552
1176
|
}
|
|
553
1177
|
if (foundNormalForms.has(t2)) {
|
|
1178
|
+
// console.log("found t2", true)
|
|
554
1179
|
normT2 = foundNormalForms.get(t2);
|
|
555
1180
|
}
|
|
556
1181
|
else {
|
|
557
|
-
|
|
1182
|
+
// console.log("not found t2", false)
|
|
1183
|
+
normT2 = this.normalize2(t2, relevantTypes, program, checker);
|
|
558
1184
|
foundNormalForms.set(t2, normT2);
|
|
559
1185
|
}
|
|
560
1186
|
// const normT1 = foundNormalForms.has(t1) ? foundNormalForms.get(t1) : this.normalize2(t1, relevantTypes);
|
|
561
1187
|
// const normT2 = foundNormalForms.has(t2) ? foundNormalForms.get(t2) : this.normalize2(t2, relevantTypes);
|
|
562
|
-
// console.log(normT1
|
|
1188
|
+
// console.log(`normal forms: ${normT1} {}{} ${normT2}`)
|
|
563
1189
|
return normT1 === normT2;
|
|
564
1190
|
}
|
|
565
1191
|
// return the normal form given a type span and a set of relevant types
|
|
@@ -643,7 +1269,7 @@ class TypeScriptDriver {
|
|
|
643
1269
|
return typeSpan;
|
|
644
1270
|
}
|
|
645
1271
|
}
|
|
646
|
-
normalize2(typeSpan, relevantTypes) {
|
|
1272
|
+
normalize2(typeSpan, relevantTypes, program, checker) {
|
|
647
1273
|
// NOTE: BUGFIX
|
|
648
1274
|
// console.log(`normalize: ${typeSpan}`)
|
|
649
1275
|
// console.log(`normalize: ${typeSpan == undefined}`)
|
|
@@ -655,7 +1281,7 @@ class TypeScriptDriver {
|
|
|
655
1281
|
}
|
|
656
1282
|
// console.log(typeSpan)
|
|
657
1283
|
let normalForm = "";
|
|
658
|
-
const analysisResult = this.typeChecker.analyzeTypeString(typeSpan);
|
|
1284
|
+
const analysisResult = this.typeChecker.analyzeTypeString(typeSpan, program, checker);
|
|
659
1285
|
// console.dir(analysisResult, { depth: null })
|
|
660
1286
|
// pattern matching for typeSpan
|
|
661
1287
|
// if (this.typeChecker.isPrimitive(typeSpan)) {
|
|
@@ -670,7 +1296,7 @@ class TypeScriptDriver {
|
|
|
670
1296
|
elements.forEach(element => {
|
|
671
1297
|
if (element !== "") {
|
|
672
1298
|
const kv = element.split(": ");
|
|
673
|
-
normalForm += kv[0].slice(1, kv[0].length), ": ", this.normalize2(kv[1], relevantTypes);
|
|
1299
|
+
normalForm += kv[0].slice(1, kv[0].length), ": ", this.normalize2(kv[1], relevantTypes, program, checker);
|
|
674
1300
|
normalForm += "; ";
|
|
675
1301
|
}
|
|
676
1302
|
});
|
|
@@ -684,7 +1310,7 @@ class TypeScriptDriver {
|
|
|
684
1310
|
const elements = this.typeChecker.parseTypeArrayString(typeSpan);
|
|
685
1311
|
normalForm += "[";
|
|
686
1312
|
elements.forEach((element, i) => {
|
|
687
|
-
normalForm += this.normalize2(element, relevantTypes);
|
|
1313
|
+
normalForm += this.normalize2(element, relevantTypes, program, checker);
|
|
688
1314
|
if (i < elements.length - 1) {
|
|
689
1315
|
normalForm += ", ";
|
|
690
1316
|
}
|
|
@@ -698,7 +1324,7 @@ class TypeScriptDriver {
|
|
|
698
1324
|
const elements = typeSpan.split(" | ");
|
|
699
1325
|
elements.forEach((element, i) => {
|
|
700
1326
|
normalForm += "(";
|
|
701
|
-
normalForm += this.normalize2(element, relevantTypes);
|
|
1327
|
+
normalForm += this.normalize2(element, relevantTypes, program, checker);
|
|
702
1328
|
normalForm += ")";
|
|
703
1329
|
if (i < elements.length - 1) {
|
|
704
1330
|
normalForm += " | ";
|
|
@@ -710,7 +1336,7 @@ class TypeScriptDriver {
|
|
|
710
1336
|
else if (this.typeChecker.isArray2(analysisResult)) {
|
|
711
1337
|
// console.log(`isArray: ${typeSpan}`)
|
|
712
1338
|
const element = typeSpan.split("[]")[0];
|
|
713
|
-
normalForm += this.normalize2(element, relevantTypes);
|
|
1339
|
+
normalForm += this.normalize2(element, relevantTypes, program, checker);
|
|
714
1340
|
normalForm += "[]";
|
|
715
1341
|
return normalForm;
|
|
716
1342
|
// } else if (this.typeChecker.isTypeAlias(typeSpan)) {
|
|
@@ -722,11 +1348,14 @@ class TypeScriptDriver {
|
|
|
722
1348
|
// console.log("typeSpan:", typeSpan)
|
|
723
1349
|
// console.log("analysis:", analysisResult.text)
|
|
724
1350
|
// console.log(relevantTypes.get(analysisResult.text))
|
|
1351
|
+
if (!relevantTypes.has(analysisResult.text)) {
|
|
1352
|
+
return typeSpan;
|
|
1353
|
+
}
|
|
725
1354
|
const typ = relevantTypes.get(analysisResult.text).typeSpan.split(" = ")[1];
|
|
726
1355
|
if (typ === undefined) {
|
|
727
1356
|
return typeSpan;
|
|
728
1357
|
}
|
|
729
|
-
normalForm += this.normalize2(typ, relevantTypes);
|
|
1358
|
+
normalForm += this.normalize2(typ, relevantTypes, program, checker);
|
|
730
1359
|
return normalForm;
|
|
731
1360
|
}
|
|
732
1361
|
else {
|
|
@@ -734,5 +1363,262 @@ class TypeScriptDriver {
|
|
|
734
1363
|
return typeSpan;
|
|
735
1364
|
}
|
|
736
1365
|
}
|
|
1366
|
+
extractRelevantHeadersHelper2(declText, typeSpan, targetTypes, relevantTypes, relevantContext, source, relevantContextMap, trace, foundNormalForms, foundTypeAnalysisResults, // filename+typeSpan -> typeAnalysisResult
|
|
1367
|
+
program, checker) {
|
|
1368
|
+
// console.log("extractRelevantHeaders2")
|
|
1369
|
+
// if (declText.includes("getBookings")) {
|
|
1370
|
+
// if (declText.slice(0, 11) === "getBookings") {
|
|
1371
|
+
// console.log("toplevel", declText, "-=-=-", typeSpan)
|
|
1372
|
+
// }
|
|
1373
|
+
let typeAnalysisResult;
|
|
1374
|
+
if (!foundTypeAnalysisResults.has(source + ":" + typeSpan)) {
|
|
1375
|
+
typeAnalysisResult = this.typeChecker.analyzeTypeString(typeSpan, program, checker);
|
|
1376
|
+
foundTypeAnalysisResults.set(source + ":" + typeSpan, typeAnalysisResult);
|
|
1377
|
+
}
|
|
1378
|
+
else {
|
|
1379
|
+
typeAnalysisResult = foundTypeAnalysisResults.get(source + ":" + typeSpan);
|
|
1380
|
+
}
|
|
1381
|
+
targetTypes.forEach(typ => {
|
|
1382
|
+
// if (declText.includes("getBookings")) {
|
|
1383
|
+
// if (declText.slice(0, 11) === "getBookings") {
|
|
1384
|
+
// console.log("innermost", declText, "-=-=-", typeSpan)
|
|
1385
|
+
// console.log(this.isTypeEquivalent(typeSpan, typ, relevantTypes, foundNormalForms))
|
|
1386
|
+
// console.log("============")
|
|
1387
|
+
// }
|
|
1388
|
+
if (this.isTypeEquivalent(typeSpan, typ, relevantTypes, foundNormalForms, program, checker)) {
|
|
1389
|
+
// NOTE: This checks for dupes. ctx is an object so you need to check for each field.
|
|
1390
|
+
// relevantContext.add({ typeSpan: line, sourceFile: source });
|
|
1391
|
+
const ctx = { typeSpan: declText, sourceFile: source };
|
|
1392
|
+
relevantContextMap.set(JSON.stringify(ctx), ctx);
|
|
1393
|
+
}
|
|
1394
|
+
if (this.typeChecker.isFunction2(typeAnalysisResult)) {
|
|
1395
|
+
const rettype = typeAnalysisResult.returnType;
|
|
1396
|
+
this.extractRelevantHeadersHelper2(declText, rettype.text, targetTypes, relevantTypes, relevantContext, source, relevantContextMap, trace, foundNormalForms, foundTypeAnalysisResults, program, checker);
|
|
1397
|
+
foundTypeAnalysisResults;
|
|
1398
|
+
}
|
|
1399
|
+
else if (this.typeChecker.isTuple2(typeAnalysisResult)) {
|
|
1400
|
+
typeAnalysisResult.constituents.forEach(constituent => {
|
|
1401
|
+
this.extractRelevantHeadersHelper2(declText, constituent.text, targetTypes, relevantTypes, relevantContext, source, relevantContextMap, trace, foundNormalForms, foundTypeAnalysisResults, program, checker);
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
async extractRelevantHeadersWithTreesitter(_, sources, relevantTypes, holeType, holeIdentifier, projectRoot) {
|
|
1407
|
+
const relevantContext = new Set();
|
|
1408
|
+
// NOTE: This is necessary because TypeScript sucks.
|
|
1409
|
+
// There is no way to compare objects by value,
|
|
1410
|
+
// so sets of objects starts to accumulate tons of duplicates.
|
|
1411
|
+
const relevantContextMap = new Map();
|
|
1412
|
+
const trace = [];
|
|
1413
|
+
const foundNormalForms = new Map();
|
|
1414
|
+
const foundTypeAnalysisResults = new Map();
|
|
1415
|
+
const targetTypes = await this.generateTargetTypesWithTreesitter(relevantTypes, holeType, holeIdentifier);
|
|
1416
|
+
// return new Set<TypeSpanAndSourceFileAndAst>();
|
|
1417
|
+
const seenDecls = new Set();
|
|
1418
|
+
// only consider lines that start with let or const
|
|
1419
|
+
for (const source of sources) {
|
|
1420
|
+
// TODO: this can be replaced by using typescript compiler api
|
|
1421
|
+
// what really needs to happen is the following:
|
|
1422
|
+
// filter by variable and function decls
|
|
1423
|
+
// get a d.ts of them (or get a type decl)
|
|
1424
|
+
// type decl makes more sense because d.ts format is a bit weird with class methods
|
|
1425
|
+
const topLevelDecls = await (0, tree_sitter_1.extractTopLevelDecls)(source);
|
|
1426
|
+
for (const tld of topLevelDecls) {
|
|
1427
|
+
// pattern 0 is let/const, 1 is var, 2 is fun
|
|
1428
|
+
// if (!seenDecls.has(JSON.stringify()) {
|
|
1429
|
+
const originalDeclText = tld.pattern === 2
|
|
1430
|
+
? tld.captures.find(d => d.name === "top.fn.decl").node.text
|
|
1431
|
+
: tld.captures.find(d => d.name === "top.var.decl").node.text;
|
|
1432
|
+
if (tld.pattern === 2) {
|
|
1433
|
+
// build a type span
|
|
1434
|
+
const funcType = (0, tree_sitter_1.extractFunctionTypeFromDecl)(tld);
|
|
1435
|
+
const wrapped = `type __TMP = ${funcType}`;
|
|
1436
|
+
const ast = await (0, ast_1.getAst)("file.ts", wrapped);
|
|
1437
|
+
if (!ast) {
|
|
1438
|
+
throw new Error(`failed to generate ast for ${wrapped}`);
|
|
1439
|
+
}
|
|
1440
|
+
const alias = ast.rootNode.namedChild(0);
|
|
1441
|
+
if (!alias || alias.type !== "type_alias_declaration") {
|
|
1442
|
+
throw new Error("Failed to parse type alias");
|
|
1443
|
+
}
|
|
1444
|
+
const valueNode = alias.childForFieldName("value");
|
|
1445
|
+
if (!valueNode)
|
|
1446
|
+
throw new Error("No type value found");
|
|
1447
|
+
const baseNode = this.unwrapToBaseType(valueNode);
|
|
1448
|
+
await this.extractRelevantHeadersWithTreesitterHelper(originalDeclText, baseNode, targetTypes, relevantTypes, relevantContext, relevantContextMap, foundNormalForms, source);
|
|
1449
|
+
}
|
|
1450
|
+
else {
|
|
1451
|
+
const varTypNode = tld.captures.find(d => d.name === "top.var.type").node;
|
|
1452
|
+
await this.extractRelevantHeadersWithTreesitterHelper(originalDeclText, varTypNode, targetTypes, relevantTypes, relevantContext, relevantContextMap, foundNormalForms, source);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
for (const v of relevantContextMap.values()) {
|
|
1457
|
+
relevantContext.add(v);
|
|
1458
|
+
}
|
|
1459
|
+
return relevantContext;
|
|
1460
|
+
}
|
|
1461
|
+
async extractRelevantHeadersWithTreesitterHelper(originalDeclText, node, targetTypes, relevantTypes, relevantContext, relevantContextMap, foundNormalForms, source) {
|
|
1462
|
+
for (const typ of targetTypes) {
|
|
1463
|
+
if (await this.isTypeEquivalentWithTreesitter(node, typ, relevantTypes, foundNormalForms)) {
|
|
1464
|
+
const ctx = { typeSpan: originalDeclText, sourceFile: source };
|
|
1465
|
+
relevantContextMap.set(JSON.stringify(ctx), ctx);
|
|
1466
|
+
}
|
|
1467
|
+
if (node.type === "function_type") {
|
|
1468
|
+
const retTypeNode = node.namedChildren.find(c => c && c.type === "return_type");
|
|
1469
|
+
if (retTypeNode) {
|
|
1470
|
+
this.extractRelevantHeadersWithTreesitterHelper(originalDeclText, retTypeNode, targetTypes, relevantTypes, relevantContext, relevantContextMap, foundNormalForms, source);
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
else if (node.type === "tuple_type") {
|
|
1474
|
+
for (const c of node.namedChildren) {
|
|
1475
|
+
await this.extractRelevantHeadersWithTreesitterHelper(originalDeclText, c, targetTypes, relevantTypes, relevantContext, relevantContextMap, foundNormalForms, source);
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
async generateTargetTypesWithTreesitter(relevantTypes, holeType, holeIdentifier) {
|
|
1481
|
+
const targetTypes = new Set();
|
|
1482
|
+
// const ast = relevantTypes.get(holeIdentifier)!.ast;
|
|
1483
|
+
const ast = await (0, ast_1.getAst)("file.ts", `type T = ${holeType}`);
|
|
1484
|
+
if (!ast) {
|
|
1485
|
+
throw new Error(`failed to generate ast for ${holeType}`);
|
|
1486
|
+
}
|
|
1487
|
+
const alias = ast.rootNode.namedChild(0);
|
|
1488
|
+
if (!alias || alias.type !== "type_alias_declaration") {
|
|
1489
|
+
throw new Error("Failed to parse type alias");
|
|
1490
|
+
}
|
|
1491
|
+
const valueNode = alias.childForFieldName("value");
|
|
1492
|
+
if (!valueNode)
|
|
1493
|
+
throw new Error("No type value found");
|
|
1494
|
+
// console.log(valueNode.text)
|
|
1495
|
+
const baseNode = this.unwrapToBaseType(valueNode);
|
|
1496
|
+
targetTypes.add(baseNode);
|
|
1497
|
+
await this.generateTargetTypesWithTreesitterHelper(relevantTypes, holeType, targetTypes, baseNode);
|
|
1498
|
+
// console.log(targetTypes)
|
|
1499
|
+
return targetTypes;
|
|
1500
|
+
}
|
|
1501
|
+
unwrapToBaseType(node) {
|
|
1502
|
+
if (["function_type", "tuple_type", "type_identifier", "predefined_type"].includes(node.type)) {
|
|
1503
|
+
return node;
|
|
1504
|
+
}
|
|
1505
|
+
for (const child of node.namedChildren) {
|
|
1506
|
+
const unwrapped = this.unwrapToBaseType(child);
|
|
1507
|
+
if (unwrapped !== child || ["function_type", "tuple_type", "type_identifier", "predefined_type"].includes(unwrapped.type)) {
|
|
1508
|
+
return unwrapped;
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
return node;
|
|
1512
|
+
}
|
|
1513
|
+
async generateTargetTypesWithTreesitterHelper(relevantTypes, currType, targetTypes, node) {
|
|
1514
|
+
var _a;
|
|
1515
|
+
if (!node)
|
|
1516
|
+
return;
|
|
1517
|
+
if (node.type === "function_type") {
|
|
1518
|
+
const returnType = node.childForFieldName("return_type");
|
|
1519
|
+
if (returnType) {
|
|
1520
|
+
targetTypes.add(returnType);
|
|
1521
|
+
await this.generateTargetTypesWithTreesitterHelper(relevantTypes, currType, targetTypes, returnType);
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
if (node.type === "tuple_type") {
|
|
1525
|
+
for (const child of node.namedChildren) {
|
|
1526
|
+
if (child) {
|
|
1527
|
+
targetTypes.add(child);
|
|
1528
|
+
await this.generateTargetTypesWithTreesitterHelper(relevantTypes, currType, targetTypes, child);
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
if (relevantTypes.has(node.text)) {
|
|
1533
|
+
// const ast = relevantTypes.get(node.text)!.ast;
|
|
1534
|
+
const typeSpan = (_a = relevantTypes.get(node.text)) === null || _a === void 0 ? void 0 : _a.typeSpan;
|
|
1535
|
+
// const ast = await getAst("file.ts", `type T = ${typeSpan}`);
|
|
1536
|
+
const ast = await (0, ast_1.getAst)("file.ts", typeSpan);
|
|
1537
|
+
if (!ast) {
|
|
1538
|
+
throw new Error(`failed to generate ast for ${typeSpan}`);
|
|
1539
|
+
}
|
|
1540
|
+
const alias = ast.rootNode.namedChild(0);
|
|
1541
|
+
if (!alias || alias.type !== "type_alias_declaration") {
|
|
1542
|
+
throw new Error("Failed to parse type alias");
|
|
1543
|
+
}
|
|
1544
|
+
const valueNode = alias.childForFieldName("value");
|
|
1545
|
+
if (!valueNode)
|
|
1546
|
+
throw new Error("No type value found");
|
|
1547
|
+
const baseNode = this.unwrapToBaseType(valueNode);
|
|
1548
|
+
await this.generateTargetTypesWithTreesitterHelper(relevantTypes, currType, targetTypes, baseNode);
|
|
1549
|
+
}
|
|
1550
|
+
// if (node.type === "type_identifier" || node.type === "predefined_type") {
|
|
1551
|
+
// return [node.text];
|
|
1552
|
+
// }
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
async isTypeEquivalentWithTreesitter(node, typ, relevantTypes, foundNormalForms) {
|
|
1556
|
+
if (!node || !typ) {
|
|
1557
|
+
return false;
|
|
1558
|
+
}
|
|
1559
|
+
let normT1 = "";
|
|
1560
|
+
let normT2 = "";
|
|
1561
|
+
if (foundNormalForms.has(node.text)) {
|
|
1562
|
+
// console.log("found t1", true)
|
|
1563
|
+
normT1 = foundNormalForms.get(node.text);
|
|
1564
|
+
}
|
|
1565
|
+
else {
|
|
1566
|
+
// console.log("not found t1", false)
|
|
1567
|
+
normT1 = await this.normalizeWithTreesitter(node, relevantTypes);
|
|
1568
|
+
foundNormalForms.set(node.text, normT1);
|
|
1569
|
+
}
|
|
1570
|
+
if (foundNormalForms.has(typ.text)) {
|
|
1571
|
+
// console.log("found t2", true)
|
|
1572
|
+
normT2 = foundNormalForms.get(typ.text);
|
|
1573
|
+
}
|
|
1574
|
+
else {
|
|
1575
|
+
// console.log("not found t2", false)
|
|
1576
|
+
normT2 = await this.normalizeWithTreesitter(typ, relevantTypes);
|
|
1577
|
+
foundNormalForms.set(typ.text, normT2);
|
|
1578
|
+
}
|
|
1579
|
+
// const normT1 = foundNormalForms.has(t1) ? foundNormalForms.get(t1) : this.normalize2(t1, relevantTypes);
|
|
1580
|
+
// const normT2 = foundNormalForms.has(t2) ? foundNormalForms.get(t2) : this.normalize2(t2, relevantTypes);
|
|
1581
|
+
// console.log(`normal forms: ${normT1} {}{} ${normT2}`)
|
|
1582
|
+
return normT1 === normT2;
|
|
1583
|
+
}
|
|
1584
|
+
async normalizeWithTreesitter(node, relevantTypes) {
|
|
1585
|
+
var _a;
|
|
1586
|
+
if (!node)
|
|
1587
|
+
return "";
|
|
1588
|
+
switch (node.type) {
|
|
1589
|
+
case "function_type": {
|
|
1590
|
+
const params = node.child(0); // formal_parameters
|
|
1591
|
+
const returnType = node.childForFieldName("type") || node.namedChildren[1]; // function_type → parameters, =>, return
|
|
1592
|
+
const paramTypes = (params === null || params === void 0 ? void 0 : params.namedChildren.map(param => this.normalizeWithTreesitter(param.childForFieldName("type") || param.namedChildren.at(-1), relevantTypes)).join(", ")) || "";
|
|
1593
|
+
const ret = this.normalizeWithTreesitter(returnType, relevantTypes);
|
|
1594
|
+
return `(${paramTypes}) => ${ret}`;
|
|
1595
|
+
}
|
|
1596
|
+
case "tuple_type": {
|
|
1597
|
+
const elements = node.namedChildren.map(c => this.normalizeWithTreesitter(c, relevantTypes));
|
|
1598
|
+
return `[${elements.join(", ")}]`;
|
|
1599
|
+
}
|
|
1600
|
+
case "union_type": {
|
|
1601
|
+
const parts = node.namedChildren.map(c => this.normalizeWithTreesitter(c, relevantTypes));
|
|
1602
|
+
return parts.join(" | ");
|
|
1603
|
+
}
|
|
1604
|
+
case "type_identifier": {
|
|
1605
|
+
const alias = relevantTypes.get(node.text);
|
|
1606
|
+
if (!alias)
|
|
1607
|
+
return node.text;
|
|
1608
|
+
// Parse the alias's type span
|
|
1609
|
+
const wrapped = `type __TMP = ${alias};`;
|
|
1610
|
+
const tree = await (0, ast_1.getAst)("file.ts", wrapped);
|
|
1611
|
+
const valueNode = (_a = tree.rootNode.descendantsOfType("type_alias_declaration")[0]) === null || _a === void 0 ? void 0 : _a.childForFieldName("value");
|
|
1612
|
+
return this.normalizeWithTreesitter(valueNode, relevantTypes);
|
|
1613
|
+
}
|
|
1614
|
+
case "predefined_type":
|
|
1615
|
+
case "number":
|
|
1616
|
+
case "string":
|
|
1617
|
+
return node.text;
|
|
1618
|
+
default:
|
|
1619
|
+
// Fallback for types like array, etc.
|
|
1620
|
+
return node.text;
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
737
1623
|
}
|
|
738
1624
|
exports.TypeScriptDriver = TypeScriptDriver;
|