@jpoly1219/context-extractor 0.2.6 → 0.2.8
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 +0 -1
- package/dist/src/app.js +30 -10
- package/dist/src/main.js +2 -0
- package/dist/src/ocaml-driver.d.ts +1 -1
- package/dist/src/ocaml-driver.js +3 -3
- package/dist/src/runner.js +28 -1
- package/dist/src/types.d.ts +2 -1
- package/dist/src/typescript-driver.d.ts +11 -5
- package/dist/src/typescript-driver.js +237 -41
- package/dist/src/typescript-type-checker.d.ts +16 -0
- package/dist/src/typescript-type-checker.js +69 -0
- package/package.json +2 -2
package/dist/src/app.d.ts
CHANGED
package/dist/src/app.js
CHANGED
@@ -40,6 +40,13 @@ const typescript_driver_1 = require("./typescript-driver");
|
|
40
40
|
const ocaml_driver_1 = require("./ocaml-driver");
|
41
41
|
const utils_1 = require("./utils");
|
42
42
|
class App {
|
43
|
+
// Optional timeout for forced termination
|
44
|
+
// private timeout = setTimeout(() => {
|
45
|
+
// if (!this.languageServer.killed) {
|
46
|
+
// console.log('Forcibly killing the process...');
|
47
|
+
// this.languageServer.kill('SIGKILL');
|
48
|
+
// }
|
49
|
+
// }, 5000);
|
43
50
|
constructor(language, sketchPath, repoPath) {
|
44
51
|
// private result: {
|
45
52
|
// hole: string;
|
@@ -47,13 +54,6 @@ class App {
|
|
47
54
|
// relevantHeaders: string[];
|
48
55
|
// } | null = null;
|
49
56
|
this.result = null;
|
50
|
-
// Optional timeout for forced termination
|
51
|
-
this.timeout = setTimeout(() => {
|
52
|
-
if (!this.languageServer.killed) {
|
53
|
-
console.log('Forcibly killing the process...');
|
54
|
-
this.languageServer.kill('SIGKILL');
|
55
|
-
}
|
56
|
-
}, 5000);
|
57
57
|
this.language = language;
|
58
58
|
this.sketchPath = sketchPath;
|
59
59
|
this.repoPath = repoPath;
|
@@ -93,6 +93,7 @@ class App {
|
|
93
93
|
const c = new main_1.LspClient(e);
|
94
94
|
this.languageServer = r;
|
95
95
|
this.lspClient = c;
|
96
|
+
// console.log(r.pid)
|
96
97
|
this.languageServer.on('close', (code) => {
|
97
98
|
if (code !== 0) {
|
98
99
|
console.log(`ls process exited with code ${code}`);
|
@@ -100,7 +101,7 @@ class App {
|
|
100
101
|
});
|
101
102
|
// Clear timeout once the process exits
|
102
103
|
this.languageServer.on('exit', () => {
|
103
|
-
clearTimeout(this.timeout);
|
104
|
+
// clearTimeout(this.timeout);
|
104
105
|
console.log('Process terminated cleanly.');
|
105
106
|
});
|
106
107
|
// const logFile = fs.createWriteStream("log.txt");
|
@@ -113,9 +114,17 @@ class App {
|
|
113
114
|
// const outputFile = fs.createWriteStream("output.txt");
|
114
115
|
try {
|
115
116
|
await this.init();
|
117
|
+
// console.time("getHoleContext");
|
116
118
|
const holeContext = await this.languageDriver.getHoleContext(this.lspClient, this.sketchPath);
|
117
|
-
|
119
|
+
// console.timeEnd("getHoleContext");
|
120
|
+
// console.time("extractRelevantTypes");
|
121
|
+
const relevantTypes = await this.languageDriver.extractRelevantTypes(this.lspClient,
|
122
|
+
// NOTE: sometimes fullHoverResult isn't representative of the actual file contents, especially with generic functions.
|
123
|
+
holeContext.trueHoleFunction ? holeContext.trueHoleFunction : holeContext.fullHoverResult, holeContext.functionName, holeContext.range.start.line, new Map(), holeContext.source, new Map());
|
124
|
+
// console.timeEnd("extractRelevantTypes");
|
125
|
+
// console.dir(relevantTypes, { depth: null })
|
118
126
|
// Postprocess the map.
|
127
|
+
// console.time("extractRelevantTypes postprocess");
|
119
128
|
if (this.language === types_1.Language.TypeScript) {
|
120
129
|
relevantTypes.delete("_()");
|
121
130
|
for (const [k, { typeSpan: v, sourceFile: src }] of relevantTypes.entries()) {
|
@@ -125,7 +134,8 @@ class App {
|
|
125
134
|
else if (this.language === types_1.Language.OCaml) {
|
126
135
|
relevantTypes.delete("_");
|
127
136
|
}
|
128
|
-
console.
|
137
|
+
// console.timeEnd("extractRelevantTypes postprocess");
|
138
|
+
// console.time("extractRelevantHeaders repo");
|
129
139
|
let repo = [];
|
130
140
|
if (this.language === types_1.Language.TypeScript) {
|
131
141
|
repo = (0, utils_1.getAllTSFiles)(this.repoPath);
|
@@ -133,8 +143,15 @@ class App {
|
|
133
143
|
else if (this.language === types_1.Language.OCaml) {
|
134
144
|
repo = (0, utils_1.getAllOCamlFiles)(this.repoPath);
|
135
145
|
}
|
146
|
+
// console.timeEnd("extractRelevantHeaders repo");
|
147
|
+
// console.time("extractRelevantHeaders");
|
136
148
|
const relevantHeaders = await this.languageDriver.extractRelevantHeaders(this.lspClient, repo, relevantTypes, holeContext.functionTypeSpan);
|
149
|
+
// const relevantHeaders: { typeSpan: string, sourceFile: string }[] = []
|
150
|
+
// console.timeEnd("extractRelevantHeaders");
|
151
|
+
// console.log(relevantHeaders)
|
152
|
+
// console.log(relevantHeaders.size)
|
137
153
|
// Postprocess the map.
|
154
|
+
// console.time("extractRelevantHaders postprocess");
|
138
155
|
if (this.language === types_1.Language.TypeScript) {
|
139
156
|
relevantTypes.delete("");
|
140
157
|
for (const [k, { typeSpan: v, sourceFile: src }] of relevantTypes.entries()) {
|
@@ -144,6 +161,8 @@ class App {
|
|
144
161
|
obj.typeSpan += ";";
|
145
162
|
}
|
146
163
|
}
|
164
|
+
// console.timeEnd("extractRelevantHeaders postprocess");
|
165
|
+
// console.time("toReturn");
|
147
166
|
const relevantTypesToReturn = new Map();
|
148
167
|
relevantTypes.forEach(({ typeSpan: v, sourceFile: src }, _) => {
|
149
168
|
if (relevantTypesToReturn.has(src)) {
|
@@ -168,6 +187,7 @@ class App {
|
|
168
187
|
relevantHeadersToReturn.set(src, [v]);
|
169
188
|
}
|
170
189
|
});
|
190
|
+
// console.timeEnd("toReturn");
|
171
191
|
this.result = {
|
172
192
|
holeType: holeContext.functionTypeSpan,
|
173
193
|
relevantTypes: relevantTypesToReturn,
|
package/dist/src/main.js
CHANGED
@@ -229,10 +229,12 @@ const extractWithCodeQL = async (sketchPath) => {
|
|
229
229
|
};
|
230
230
|
exports.extractWithCodeQL = extractWithCodeQL;
|
231
231
|
const extractContext = async (language, sketchPath, repoPath) => {
|
232
|
+
// console.time("extractContext")
|
232
233
|
const app = new app_1.App(language, sketchPath, repoPath);
|
233
234
|
await app.run();
|
234
235
|
const res = app.getSavedResult();
|
235
236
|
app.close();
|
237
|
+
// console.timeEnd("extractContext")
|
236
238
|
return res;
|
237
239
|
// if (!getCompletion) {
|
238
240
|
// await app.close()
|
@@ -15,7 +15,7 @@ export declare class OcamlDriver implements LanguageDriver {
|
|
15
15
|
range: Range;
|
16
16
|
source: string;
|
17
17
|
}>;
|
18
|
-
extractRelevantTypes(lspClient: LspClient, fullHoverResult: string, typeName: string, startLine: number,
|
18
|
+
extractRelevantTypes(lspClient: LspClient, fullHoverResult: string, typeName: string, startLine: number, foundSoFar: Map<string, TypeSpanAndSourceFile>, currentFile: string): Promise<Map<string, TypeSpanAndSourceFile>>;
|
19
19
|
extractRelevantHeaders(lspClient: LspClient, sources: string[], relevantTypes: Map<string, TypeSpanAndSourceFile>, holeType: string): Promise<Set<TypeSpanAndSourceFile>>;
|
20
20
|
extractHeaderTypeSpans(lspClient: LspClient, preludeFilePath: string): Promise<{
|
21
21
|
identifier: string;
|
package/dist/src/ocaml-driver.js
CHANGED
@@ -172,12 +172,12 @@ class OcamlDriver {
|
|
172
172
|
source: `file://${sketchFilePath}`
|
173
173
|
};
|
174
174
|
}
|
175
|
-
async extractRelevantTypes(lspClient, fullHoverResult, typeName, startLine,
|
175
|
+
async extractRelevantTypes(lspClient, fullHoverResult, typeName, startLine, foundSoFar, currentFile) {
|
176
176
|
if (!foundSoFar.has(typeName)) {
|
177
177
|
foundSoFar.set(typeName, { typeSpan: fullHoverResult, sourceFile: currentFile.slice(7) });
|
178
178
|
// outputFile.write(`${fullHoverResult};\n`);
|
179
179
|
const content = fs.readFileSync(currentFile.slice(7), "utf8");
|
180
|
-
for (let linePos = startLine; linePos <=
|
180
|
+
for (let linePos = startLine; linePos <= fullHoverResult.length; ++linePos) {
|
181
181
|
const numOfCharsInLine = parseInt((0, child_process_1.execSync)(`wc -m <<< "${content.split("\n")[linePos]}"`, { shell: "/bin/bash" }).toString());
|
182
182
|
for (let charPos = 0; charPos < numOfCharsInLine; ++charPos) {
|
183
183
|
try {
|
@@ -224,7 +224,7 @@ class OcamlDriver {
|
|
224
224
|
// TODO: this can potentially be its own method. the driver would require some way to get type context.
|
225
225
|
// potentially, this type checker can be its own class.
|
226
226
|
const identifier = this.typeChecker.getIdentifierFromDecl(snippetInRange);
|
227
|
-
await this.extractRelevantTypes(lspClient, snippetInRange, identifier, matchingSymbolRange.start.line,
|
227
|
+
await this.extractRelevantTypes(lspClient, snippetInRange, identifier, matchingSymbolRange.start.line, foundSoFar, typeDefinitionResult[0].uri);
|
228
228
|
}
|
229
229
|
}
|
230
230
|
}
|
package/dist/src/runner.js
CHANGED
@@ -9,8 +9,35 @@ const types_1 = require("./types");
|
|
9
9
|
// extract("/home/jacob/projects/context-extractor/targets/emojipaint/sketch.ts").then(r => console.log("emojipaint\n", r));
|
10
10
|
(async () => {
|
11
11
|
try {
|
12
|
-
|
12
|
+
let x;
|
13
|
+
// x = await extractContext(
|
14
|
+
// Language.TypeScript,
|
15
|
+
// "/home/jacob/projects/context-extractor/targets/todo/sketch.ts",
|
16
|
+
// "/home/jacob/projects/context-extractor/targets/todo/",
|
17
|
+
// )
|
18
|
+
// console.dir(x, { depth: null })
|
19
|
+
//
|
20
|
+
// x = await extractContext(
|
21
|
+
// Language.TypeScript,
|
22
|
+
// "/home/jacob/projects/context-extractor/targets/playlist/sketch.ts",
|
23
|
+
// "/home/jacob/projects/context-extractor/targets/playlist/",
|
24
|
+
// )
|
25
|
+
// console.dir(x, { depth: null })
|
26
|
+
//
|
27
|
+
// x = await extractContext(
|
28
|
+
// Language.TypeScript,
|
29
|
+
// "/home/jacob/projects/context-extractor/targets/passwords/sketch.ts",
|
30
|
+
// "/home/jacob/projects/context-extractor/targets/passwords/",
|
31
|
+
// )
|
32
|
+
// console.dir(x, { depth: null })
|
33
|
+
x = await (0, main_1.extractContext)(types_1.Language.TypeScript, "/home/jacob/projects/context-extractor/targets/booking/sketch.ts", "/home/jacob/projects/context-extractor/targets/booking/");
|
13
34
|
console.dir(x, { depth: null });
|
35
|
+
// x = await extractContext(
|
36
|
+
// Language.TypeScript,
|
37
|
+
// "/home/jacob/projects/context-extractor/targets/emojipaint/sketch.ts",
|
38
|
+
// "/home/jacob/projects/context-extractor/targets/emojipaint/",
|
39
|
+
// )
|
40
|
+
// console.dir(x, { depth: null })
|
14
41
|
// const y = await completeWithLLM(
|
15
42
|
// x!,
|
16
43
|
// Language.TypeScript,
|
package/dist/src/types.d.ts
CHANGED
@@ -72,8 +72,9 @@ interface LanguageDriver {
|
|
72
72
|
holeTypeDefCharPos: number;
|
73
73
|
range: Range;
|
74
74
|
source: string;
|
75
|
+
trueHoleFunction?: string;
|
75
76
|
}>;
|
76
|
-
extractRelevantTypes: (lspClient: LspClient, fullHoverResult: string, typeName: string, startLine: number,
|
77
|
+
extractRelevantTypes: (lspClient: LspClient, fullHoverResult: string, typeName: string, startLine: number, foundSoFar: Map<string, TypeSpanAndSourceFile>, currentFile: string, foundContents: Map<string, string>) => Promise<Map<string, TypeSpanAndSourceFile>>;
|
77
78
|
extractRelevantHeaders: (lspClient: LspClient, sources: string[], relevantTypes: Map<string, TypeSpanAndSourceFile>, holeType: string) => Promise<Set<TypeSpanAndSourceFile>>;
|
78
79
|
}
|
79
80
|
type Filepath = string;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { LspClient, Range } from "../ts-lsp-client-dist/src/main";
|
1
|
+
import { LspClient, Location, Range, SymbolInformation } from "../ts-lsp-client-dist/src/main";
|
2
2
|
import { LanguageDriver, TypeSpanAndSourceFile } from "./types";
|
3
3
|
import { TypeScriptTypeChecker } from "./typescript-type-checker";
|
4
4
|
export declare class TypeScriptDriver implements LanguageDriver {
|
@@ -14,14 +14,20 @@ export declare class TypeScriptDriver implements LanguageDriver {
|
|
14
14
|
holeTypeDefCharPos: number;
|
15
15
|
range: Range;
|
16
16
|
source: string;
|
17
|
+
trueHoleFunction: string;
|
17
18
|
}>;
|
18
|
-
|
19
|
-
currentFile: string): Promise<Map<string, TypeSpanAndSourceFile>>;
|
19
|
+
extractRelevantTypes1(lspClient: LspClient, fullHoverResult: string, typeName: string, startLine: number, endLine: number, foundSoFar: Map<string, TypeSpanAndSourceFile>, // identifier -> [full hover result, source]
|
20
|
+
currentFile: string, foundTypeDefinitions: Map<string, Location[]>, foundSymbols: Map<string, SymbolInformation[]>): Promise<Map<string, TypeSpanAndSourceFile>>;
|
21
|
+
extractRelevantTypes(lspClient: LspClient, fullHoverResult: string, typeName: string, startLine: number, foundSoFar: Map<string, TypeSpanAndSourceFile>, // identifier -> [full hover result, source]
|
22
|
+
currentFile: string, foundContents: Map<string, string>): Promise<Map<string, TypeSpanAndSourceFile>>;
|
23
|
+
extractRelevantTypesHelper(lspClient: LspClient, fullHoverResult: string, typeName: string, startLine: number, foundSoFar: Map<string, TypeSpanAndSourceFile>, // identifier -> [full hover result, source]
|
24
|
+
currentFile: string, foundContents: Map<string, string>, // uri -> contents
|
25
|
+
layer: number): Promise<void>;
|
20
26
|
extractRelevantHeaders(_: LspClient, sources: string[], relevantTypes: Map<string, TypeSpanAndSourceFile>, holeType: string): Promise<Set<TypeSpanAndSourceFile>>;
|
21
27
|
generateTargetTypes(relevantTypes: Map<string, TypeSpanAndSourceFile>, holeType: string): Set<string>;
|
22
28
|
generateTargetTypesHelper(relevantTypes: Map<string, TypeSpanAndSourceFile>, currType: string, targetTypes: Set<string>): void;
|
23
|
-
extractRelevantHeadersHelper(typeSpan: string, targetTypes: Set<string>, relevantTypes: Map<string, TypeSpanAndSourceFile>, relevantContext: Set<TypeSpanAndSourceFile>, line: string, source: string): void;
|
24
|
-
isTypeEquivalent(t1: string, t2: string, relevantTypes: Map<string, TypeSpanAndSourceFile>): boolean;
|
29
|
+
extractRelevantHeadersHelper(typeSpan: string, targetTypes: Set<string>, relevantTypes: Map<string, TypeSpanAndSourceFile>, relevantContext: Set<TypeSpanAndSourceFile>, line: string, source: string, relevantContextMap: Map<string, TypeSpanAndSourceFile>, tag: boolean, trace: string[], foundNormalForms: Map<string, string>): void;
|
30
|
+
isTypeEquivalent(t1: string, t2: string, relevantTypes: Map<string, TypeSpanAndSourceFile>, foundNormalForms: Map<string, string>): boolean;
|
25
31
|
normalize(typeSpan: string, relevantTypes: Map<string, TypeSpanAndSourceFile>): string;
|
26
32
|
normalize2(typeSpan: string, relevantTypes: Map<string, TypeSpanAndSourceFile>): string;
|
27
33
|
}
|
@@ -196,18 +196,26 @@ class TypeScriptDriver {
|
|
196
196
|
holeTypeDefCharPos: "declare function _(): ".length,
|
197
197
|
// range: { start: { line: 0, character: 0 }, end: { line: 0, character: 52 } }
|
198
198
|
range: sketchSymbol[0].location.range,
|
199
|
-
source: `file://${injectedSketchFilePath}
|
199
|
+
source: `file://${injectedSketchFilePath}`,
|
200
|
+
trueHoleFunction: trueHoleFunction
|
200
201
|
};
|
201
202
|
}
|
202
|
-
async
|
203
|
-
currentFile
|
203
|
+
async extractRelevantTypes1(lspClient, fullHoverResult, typeName, startLine, endLine, foundSoFar, // identifier -> [full hover result, source]
|
204
|
+
currentFile, foundTypeDefinitions, foundSymbols
|
205
|
+
// outputFile: fs.WriteStream,
|
206
|
+
) {
|
204
207
|
if (!foundSoFar.has(typeName)) {
|
205
208
|
foundSoFar.set(typeName, { typeSpan: fullHoverResult, sourceFile: currentFile.slice(7) });
|
206
209
|
// outputFile.write(`${fullHoverResult};\n`);
|
207
210
|
const content = fs.readFileSync(currentFile.slice(7), "utf8");
|
208
211
|
for (let linePos = startLine; linePos <= endLine; ++linePos) {
|
212
|
+
// TODO: use a platform-agnostic command here
|
209
213
|
const numOfCharsInLine = parseInt((0, child_process_1.execSync)(`wc -m <<< "${content.split("\n")[linePos]}"`, { shell: "/bin/bash" }).toString());
|
210
|
-
|
214
|
+
const numOfCharsInLine2 = content.split("\n")[linePos].length;
|
215
|
+
const numOfCharsInLine3 = [...content.split("\n")[linePos]].map(c => c.codePointAt(0)).length;
|
216
|
+
// console.log(numOfCharsInLine === numOfCharsInLine2, content.split("\n")[linePos], numOfCharsInLine, numOfCharsInLine2, numOfCharsInLine3)
|
217
|
+
// console.time(`===loop ${content.split("\n")[linePos]}===`);
|
218
|
+
for (let charPos = 0; charPos < numOfCharsInLine2; ++charPos) {
|
211
219
|
try {
|
212
220
|
const typeDefinitionResult = await lspClient.typeDefinition({
|
213
221
|
textDocument: {
|
@@ -218,41 +226,162 @@ class TypeScriptDriver {
|
|
218
226
|
line: linePos
|
219
227
|
}
|
220
228
|
});
|
229
|
+
// if (content.split("\n")[linePos] === `type Action = AddBooking | CancelBooking | ClearBookings;`) {
|
230
|
+
// console.dir(typeDefinitionResult, { depth: null })
|
231
|
+
// console.log(charPos)
|
232
|
+
// }
|
221
233
|
if (typeDefinitionResult && typeDefinitionResult instanceof Array && typeDefinitionResult.length != 0) {
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
234
|
+
const tdResultStr = JSON.stringify(typeDefinitionResult);
|
235
|
+
if (!foundTypeDefinitions.has(tdResultStr)) {
|
236
|
+
foundTypeDefinitions.set(tdResultStr, typeDefinitionResult);
|
237
|
+
// Use documentSymbol instead of hover.
|
238
|
+
// This prevents type alias "squashing" done by tsserver.
|
239
|
+
// This also allows for grabbing the entire definition range and not just the symbol range.
|
240
|
+
// PERF: feels like this could be memoized to improve performance.
|
241
|
+
// console.time("docSymbol")
|
242
|
+
const tdUri = typeDefinitionResult[0].uri;
|
243
|
+
let documentSymbolResult;
|
244
|
+
if (foundSymbols.has(tdUri)) {
|
245
|
+
documentSymbolResult = foundSymbols.get(tdUri);
|
246
|
+
}
|
247
|
+
else {
|
248
|
+
documentSymbolResult = await lspClient.documentSymbol({
|
249
|
+
textDocument: {
|
250
|
+
uri: typeDefinitionResult[0].uri
|
251
|
+
}
|
252
|
+
});
|
253
|
+
foundSymbols.set(tdUri, documentSymbolResult);
|
254
|
+
}
|
255
|
+
// console.timeEnd("docSymbol")
|
256
|
+
// console.time("dsMap")
|
257
|
+
const dsMap = documentSymbolResult.reduce((m, obj) => {
|
258
|
+
m.set(obj.location.range.start.line, obj.location.range);
|
259
|
+
return m;
|
260
|
+
}, new Map());
|
261
|
+
// console.timeEnd("dsMap")
|
262
|
+
// console.log("\n")
|
263
|
+
// console.dir(typeDefinitionResult, { depth: null })
|
264
|
+
// console.dir(documentSymbolResult, { depth: null })
|
265
|
+
// console.log("\n")
|
266
|
+
// grab if the line number of typeDefinitionResult and documentSymbolResult matches
|
267
|
+
// const dsMap = documentSymbolResult!.reduce((m, obj) => {
|
268
|
+
// m.set((obj as SymbolInformation).location.range.start.line, (obj as SymbolInformation).location.range as unknown as Range);
|
269
|
+
// return m;
|
270
|
+
// }, new Map<number, Range>());
|
271
|
+
const matchingSymbolRange = dsMap.get(typeDefinitionResult[0].range.start.line);
|
272
|
+
if (matchingSymbolRange) {
|
273
|
+
const snippetInRange = (0, utils_1.extractSnippet)(fs.readFileSync(typeDefinitionResult[0].uri.slice(7)).toString("utf8"), matchingSymbolRange.start, matchingSymbolRange.end);
|
274
|
+
// TODO: this can potentially be its own method. the driver would require some way to get type context.
|
275
|
+
// potentially, this type checker can be its own class.
|
276
|
+
const identifier = this.typeChecker.getIdentifierFromDecl(snippetInRange);
|
277
|
+
await this.extractRelevantTypes1(lspClient, snippetInRange, identifier, matchingSymbolRange.start.line, matchingSymbolRange.end.line, foundSoFar, typeDefinitionResult[0].uri, foundTypeDefinitions, foundSymbols
|
278
|
+
// outputFile,
|
279
|
+
);
|
229
280
|
}
|
230
|
-
});
|
231
|
-
// grab if the line number of typeDefinitionResult and documentSymbolResult matches
|
232
|
-
const dsMap = documentSymbolResult.reduce((m, obj) => {
|
233
|
-
m.set(obj.location.range.start.line, obj.location.range);
|
234
|
-
return m;
|
235
|
-
}, new Map());
|
236
|
-
const matchingSymbolRange = dsMap.get(typeDefinitionResult[0].range.start.line);
|
237
|
-
if (matchingSymbolRange) {
|
238
|
-
const snippetInRange = (0, utils_1.extractSnippet)(fs.readFileSync(typeDefinitionResult[0].uri.slice(7)).toString("utf8"), matchingSymbolRange.start, matchingSymbolRange.end);
|
239
|
-
// TODO: this can potentially be its own method. the driver would require some way to get type context.
|
240
|
-
// potentially, this type checker can be its own class.
|
241
|
-
const identifier = this.typeChecker.getIdentifierFromDecl(snippetInRange);
|
242
|
-
await this.extractRelevantTypes(lspClient, snippetInRange, identifier, matchingSymbolRange.start.line, matchingSymbolRange.end.line, foundSoFar, typeDefinitionResult[0].uri);
|
243
281
|
}
|
244
282
|
}
|
283
|
+
// else {
|
284
|
+
// console.log(`already found ${tdResultStr}!`)
|
285
|
+
// }
|
245
286
|
}
|
246
287
|
catch (err) {
|
247
288
|
console.log(`${err}`);
|
248
289
|
}
|
249
290
|
}
|
291
|
+
// console.timeEnd(`===loop ${content.split("\n")[linePos]}===`);
|
250
292
|
}
|
251
293
|
}
|
252
294
|
return foundSoFar;
|
253
295
|
}
|
296
|
+
async extractRelevantTypes(lspClient, fullHoverResult, typeName, startLine, foundSoFar, // identifier -> [full hover result, source]
|
297
|
+
currentFile, foundContents // uri -> contents
|
298
|
+
) {
|
299
|
+
const content = fs.readFileSync(currentFile.slice(7), "utf8");
|
300
|
+
// console.log(content)
|
301
|
+
await this.extractRelevantTypesHelper(lspClient, fullHoverResult, typeName, startLine, foundSoFar, currentFile, foundContents, 0);
|
302
|
+
return foundSoFar;
|
303
|
+
}
|
304
|
+
async extractRelevantTypesHelper(lspClient, fullHoverResult, typeName, startLine, foundSoFar, // identifier -> [full hover result, source]
|
305
|
+
currentFile, foundContents, // uri -> contents
|
306
|
+
layer) {
|
307
|
+
// console.log("===", fullHoverResult, layer, startLine, "===")
|
308
|
+
// Split the type span into identifiers, where each include the text, line number, and character range.
|
309
|
+
// For each identifier, invoke go to type definition.
|
310
|
+
if (!foundSoFar.has(typeName)) {
|
311
|
+
foundSoFar.set(typeName, { typeSpan: fullHoverResult, sourceFile: currentFile.slice(7) });
|
312
|
+
const identifiers = this.typeChecker.extractIdentifiers(fullHoverResult);
|
313
|
+
// console.dir(identifiers, { depth: null })
|
314
|
+
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
|
+
if (!foundSoFar.has(identifier.name)) {
|
325
|
+
try {
|
326
|
+
const typeDefinitionResult = await lspClient.typeDefinition({
|
327
|
+
textDocument: {
|
328
|
+
uri: currentFile
|
329
|
+
},
|
330
|
+
position: {
|
331
|
+
character: identifier.start,
|
332
|
+
line: startLine + identifier.line - 1 // startLine is already 1-indexed
|
333
|
+
}
|
334
|
+
});
|
335
|
+
// if (identifier.name == "Model") {
|
336
|
+
// console.dir(typeDefinitionResult, { depth: null })
|
337
|
+
// }
|
338
|
+
if (typeDefinitionResult && typeDefinitionResult instanceof Array && typeDefinitionResult.length != 0) {
|
339
|
+
const tdLocation = typeDefinitionResult[0];
|
340
|
+
let content = "";
|
341
|
+
if (foundContents.has(tdLocation.uri.slice(7))) {
|
342
|
+
content = foundContents.get(tdLocation.uri.slice(7));
|
343
|
+
}
|
344
|
+
else {
|
345
|
+
content = fs.readFileSync(tdLocation.uri.slice(7), "utf8");
|
346
|
+
foundContents.set(tdLocation.uri.slice(7), content);
|
347
|
+
}
|
348
|
+
const decl = this.typeChecker.findDeclarationForIdentifier(content, tdLocation.range.start.line, tdLocation.range.start.character, tdLocation.range.end.character);
|
349
|
+
if (decl) {
|
350
|
+
const ident = this.typeChecker.getIdentifierFromDecl(decl);
|
351
|
+
// console.log(ident == identifier.name, ident, identifier.name, decl)
|
352
|
+
// console.log(`Decl: ${decl} || Identifier: ${ident}`)
|
353
|
+
// console.timeEnd(`loop ${identifier.name} layer ${layer}`)
|
354
|
+
await this.extractRelevantTypesHelper(lspClient, decl, ident, tdLocation.range.start.line, foundSoFar, tdLocation.uri, foundContents, layer + 1);
|
355
|
+
}
|
356
|
+
else {
|
357
|
+
console.log("decl not found");
|
358
|
+
// console.timeEnd(`loop ${identifier.name} layer ${layer}`)
|
359
|
+
}
|
360
|
+
}
|
361
|
+
else {
|
362
|
+
console.log("td not found");
|
363
|
+
// console.dir(typeDefinitionResult, { depth: null })
|
364
|
+
}
|
365
|
+
}
|
366
|
+
catch (err) {
|
367
|
+
console.log(err);
|
368
|
+
}
|
369
|
+
}
|
370
|
+
else {
|
371
|
+
// console.timeEnd(`loop ${identifier.name} layer ${layer}`)
|
372
|
+
}
|
373
|
+
}
|
374
|
+
}
|
375
|
+
}
|
254
376
|
async extractRelevantHeaders(_, sources, relevantTypes, holeType) {
|
377
|
+
// console.time("extractRelevantHeaders");
|
255
378
|
const relevantContext = new Set();
|
379
|
+
// NOTE: This is necessary because TypeScript sucks.
|
380
|
+
// There is no way to compare objects by value,
|
381
|
+
// so sets of objects starts to accumulate tons of duplicates.
|
382
|
+
const relevantContextMap = new Map();
|
383
|
+
const trace = [];
|
384
|
+
const foundNormalForms = new Map();
|
256
385
|
const targetTypes = this.generateTargetTypes(relevantTypes, holeType);
|
257
386
|
// only consider lines that start with let or const
|
258
387
|
for (const source of sources) {
|
@@ -262,24 +391,41 @@ class TypeScriptDriver {
|
|
262
391
|
});
|
263
392
|
// check for relationship between each line and relevant types
|
264
393
|
filteredLines.forEach(line => {
|
394
|
+
// console.time(`helper, line: ${line}`);
|
395
|
+
let tag = false;
|
396
|
+
if (line === `const initFormState: [[Weekday, TimeOfDay], string] = [["M", "AM"], ""];`) {
|
397
|
+
tag = true;
|
398
|
+
}
|
265
399
|
// TODO: Use the compiler API to split this.
|
266
400
|
const splittedLine = line.split(" = ")[0];
|
267
401
|
const typeSpanPattern = /(^[^:]*: )(.+)/;
|
268
402
|
const regexMatch = splittedLine.match(typeSpanPattern);
|
269
403
|
if (regexMatch) {
|
270
404
|
const returnTypeSpan = regexMatch[2];
|
405
|
+
// console.log(`returnTypeSpan: ${returnTypeSpan}`)
|
406
|
+
// const typeAnalysisResult = this.typeChecker.analyzeTypeString(returnTypeSpan);
|
407
|
+
// console.log(`typeAnalysisResult: ${JSON.stringify(typeAnalysisResult, null, 2)}`)
|
271
408
|
if (!this.typeChecker.isPrimitive(returnTypeSpan.split(" => ")[1])) {
|
272
|
-
this.extractRelevantHeadersHelper(returnTypeSpan, targetTypes, relevantTypes, relevantContext, splittedLine, source);
|
409
|
+
this.extractRelevantHeadersHelper(returnTypeSpan, targetTypes, relevantTypes, relevantContext, splittedLine, source, relevantContextMap, tag, trace, foundNormalForms);
|
273
410
|
}
|
274
411
|
}
|
412
|
+
// console.timeEnd(`helper, line: ${line}`);
|
275
413
|
});
|
276
414
|
}
|
415
|
+
// console.log(JSON.stringify(relevantContextMap, null, 2))
|
416
|
+
// console.log(relevantContextMap.keys())
|
417
|
+
for (const v of relevantContextMap.values()) {
|
418
|
+
relevantContext.add(v);
|
419
|
+
}
|
420
|
+
// console.timeEnd("extractRelevantHeaders");
|
277
421
|
return relevantContext;
|
278
422
|
}
|
279
423
|
generateTargetTypes(relevantTypes, holeType) {
|
424
|
+
// console.time("generateTargetTypes");
|
280
425
|
const targetTypes = new Set();
|
281
426
|
targetTypes.add(holeType);
|
282
427
|
this.generateTargetTypesHelper(relevantTypes, holeType, targetTypes);
|
428
|
+
// console.timeEnd("generateTargetTypes");
|
283
429
|
return targetTypes;
|
284
430
|
}
|
285
431
|
// generateTargetTypesHelper(
|
@@ -358,31 +504,62 @@ class TypeScriptDriver {
|
|
358
504
|
// }
|
359
505
|
// });
|
360
506
|
// }
|
361
|
-
extractRelevantHeadersHelper(typeSpan, targetTypes, relevantTypes, relevantContext, line, source) {
|
507
|
+
extractRelevantHeadersHelper(typeSpan, targetTypes, relevantTypes, relevantContext, line, source, relevantContextMap, tag, trace, foundNormalForms) {
|
508
|
+
if (tag) {
|
509
|
+
// console.time(`extractRelevantHeadersHelper, typeSpan: ${typeSpan}`)
|
510
|
+
// trace.push(typeSpan)
|
511
|
+
// console.log(trace)
|
512
|
+
}
|
513
|
+
// TODO: this can probably done at the top level.
|
514
|
+
// analyzeTypeString is recursive by itself.
|
362
515
|
const typeAnalysisResult = this.typeChecker.analyzeTypeString(typeSpan);
|
363
516
|
targetTypes.forEach(typ => {
|
364
|
-
if (this.isTypeEquivalent(typeSpan, typ, relevantTypes)) {
|
365
|
-
|
517
|
+
if (this.isTypeEquivalent(typeSpan, typ, relevantTypes, foundNormalForms)) {
|
518
|
+
// NOTE: This checks for dupes. ctx is an object so you need to check for each field.
|
519
|
+
// relevantContext.add({ typeSpan: line, sourceFile: source });
|
520
|
+
const ctx = { typeSpan: line, sourceFile: source };
|
521
|
+
relevantContextMap.set(JSON.stringify(ctx), ctx);
|
366
522
|
}
|
367
523
|
if (this.typeChecker.isFunction2(typeAnalysisResult)) {
|
368
524
|
const rettype = typeAnalysisResult.returnType;
|
369
|
-
this.extractRelevantHeadersHelper(rettype.text, targetTypes, relevantTypes, relevantContext, line, source);
|
525
|
+
this.extractRelevantHeadersHelper(rettype.text, targetTypes, relevantTypes, relevantContext, line, source, relevantContextMap, tag, trace, foundNormalForms);
|
370
526
|
}
|
371
527
|
else if (this.typeChecker.isTuple2(typeAnalysisResult)) {
|
372
528
|
typeAnalysisResult.constituents.forEach(constituent => {
|
373
|
-
this.extractRelevantHeadersHelper(constituent.text, targetTypes, relevantTypes, relevantContext, line, source);
|
529
|
+
this.extractRelevantHeadersHelper(constituent.text, targetTypes, relevantTypes, relevantContext, line, source, relevantContextMap, tag, trace, foundNormalForms);
|
374
530
|
});
|
375
531
|
}
|
376
532
|
});
|
533
|
+
if (tag) {
|
534
|
+
// console.log("\n\n\n")
|
535
|
+
// console.timeEnd(`extractRelevantHeadersHelper, typeSpan: ${typeSpan}`)
|
536
|
+
}
|
377
537
|
}
|
378
538
|
// two types are equivalent if they have the same normal forms
|
379
|
-
isTypeEquivalent(t1, t2, relevantTypes) {
|
539
|
+
isTypeEquivalent(t1, t2, relevantTypes, foundNormalForms) {
|
380
540
|
// NOTE: BUGFIX
|
381
541
|
// console.log(`isTypeEquivalent: ${t1}, ${t2}`)
|
382
542
|
// console.log(t1 == undefined)
|
383
543
|
// console.log(t2 == undefined)
|
384
|
-
|
385
|
-
|
544
|
+
let normT1 = "";
|
545
|
+
let normT2 = "";
|
546
|
+
if (foundNormalForms.has(t1)) {
|
547
|
+
normT1 = foundNormalForms.get(t1);
|
548
|
+
}
|
549
|
+
else {
|
550
|
+
normT1 = this.normalize2(t1, relevantTypes);
|
551
|
+
foundNormalForms.set(t1, normT1);
|
552
|
+
}
|
553
|
+
if (foundNormalForms.has(t2)) {
|
554
|
+
normT2 = foundNormalForms.get(t2);
|
555
|
+
}
|
556
|
+
else {
|
557
|
+
normT2 = this.normalize2(t2, relevantTypes);
|
558
|
+
foundNormalForms.set(t2, normT2);
|
559
|
+
}
|
560
|
+
// const normT1 = foundNormalForms.has(t1) ? foundNormalForms.get(t1) : this.normalize2(t1, relevantTypes);
|
561
|
+
// const normT2 = foundNormalForms.has(t2) ? foundNormalForms.get(t2) : this.normalize2(t2, relevantTypes);
|
562
|
+
// console.log(normT1, normT2)
|
386
563
|
return normT1 === normT2;
|
387
564
|
}
|
388
565
|
// return the normal form given a type span and a set of relevant types
|
@@ -390,9 +567,16 @@ class TypeScriptDriver {
|
|
390
567
|
normalize(typeSpan, relevantTypes) {
|
391
568
|
// NOTE: BUGFIX
|
392
569
|
// console.log(`normalize: ${typeSpan}`)
|
570
|
+
// console.log(`normalize: ${typeSpan}`)
|
571
|
+
// console.log(`normalize: ${typeSpan == undefined}`)
|
393
572
|
if (typeSpan.slice(typeSpan.length - 2) == " =") {
|
394
|
-
typeSpan = typeSpan.slice(typeSpan.length - 2);
|
573
|
+
typeSpan = typeSpan.slice(0, typeSpan.length - 2);
|
395
574
|
}
|
575
|
+
// DEBUG
|
576
|
+
// if (typeSpan.slice(typeSpan.length - 1) == ";") {
|
577
|
+
// typeSpan = typeSpan.slice(0, typeSpan.length - 1);
|
578
|
+
// }
|
579
|
+
// console.log(typeSpan)
|
396
580
|
let normalForm = "";
|
397
581
|
// pattern matching for typeSpan
|
398
582
|
if (this.typeChecker.isPrimitive(typeSpan)) {
|
@@ -462,11 +646,17 @@ class TypeScriptDriver {
|
|
462
646
|
normalize2(typeSpan, relevantTypes) {
|
463
647
|
// NOTE: BUGFIX
|
464
648
|
// console.log(`normalize: ${typeSpan}`)
|
649
|
+
// console.log(`normalize: ${typeSpan == undefined}`)
|
465
650
|
if (typeSpan.slice(typeSpan.length - 2) == " =") {
|
466
|
-
typeSpan = typeSpan.slice(typeSpan.length - 2);
|
651
|
+
typeSpan = typeSpan.slice(0, typeSpan.length - 2);
|
652
|
+
}
|
653
|
+
if (typeSpan.slice(typeSpan.length - 1) == ";") {
|
654
|
+
typeSpan = typeSpan.slice(0, typeSpan.length - 1);
|
467
655
|
}
|
656
|
+
// console.log(typeSpan)
|
468
657
|
let normalForm = "";
|
469
658
|
const analysisResult = this.typeChecker.analyzeTypeString(typeSpan);
|
659
|
+
// console.dir(analysisResult, { depth: null })
|
470
660
|
// pattern matching for typeSpan
|
471
661
|
// if (this.typeChecker.isPrimitive(typeSpan)) {
|
472
662
|
if (this.typeChecker.isPrimitive2(analysisResult)) {
|
@@ -480,7 +670,7 @@ class TypeScriptDriver {
|
|
480
670
|
elements.forEach(element => {
|
481
671
|
if (element !== "") {
|
482
672
|
const kv = element.split(": ");
|
483
|
-
normalForm += kv[0].slice(1, kv[0].length), ": ", this.
|
673
|
+
normalForm += kv[0].slice(1, kv[0].length), ": ", this.normalize2(kv[1], relevantTypes);
|
484
674
|
normalForm += "; ";
|
485
675
|
}
|
486
676
|
});
|
@@ -494,7 +684,7 @@ class TypeScriptDriver {
|
|
494
684
|
const elements = this.typeChecker.parseTypeArrayString(typeSpan);
|
495
685
|
normalForm += "[";
|
496
686
|
elements.forEach((element, i) => {
|
497
|
-
normalForm += this.
|
687
|
+
normalForm += this.normalize2(element, relevantTypes);
|
498
688
|
if (i < elements.length - 1) {
|
499
689
|
normalForm += ", ";
|
500
690
|
}
|
@@ -508,7 +698,7 @@ class TypeScriptDriver {
|
|
508
698
|
const elements = typeSpan.split(" | ");
|
509
699
|
elements.forEach((element, i) => {
|
510
700
|
normalForm += "(";
|
511
|
-
normalForm += this.
|
701
|
+
normalForm += this.normalize2(element, relevantTypes);
|
512
702
|
normalForm += ")";
|
513
703
|
if (i < elements.length - 1) {
|
514
704
|
normalForm += " | ";
|
@@ -520,17 +710,23 @@ class TypeScriptDriver {
|
|
520
710
|
else if (this.typeChecker.isArray2(analysisResult)) {
|
521
711
|
// console.log(`isArray: ${typeSpan}`)
|
522
712
|
const element = typeSpan.split("[]")[0];
|
523
|
-
normalForm += this.
|
713
|
+
normalForm += this.normalize2(element, relevantTypes);
|
524
714
|
normalForm += "[]";
|
525
715
|
return normalForm;
|
526
716
|
// } else if (this.typeChecker.isTypeAlias(typeSpan)) {
|
527
717
|
}
|
528
718
|
else if (this.typeChecker.isTypeAlias2(analysisResult)) {
|
529
|
-
|
719
|
+
// console.log("ALERT!!!!!!")
|
720
|
+
// console.dir(relevantTypes, { depth: null })
|
721
|
+
// console.dir(analysisResult, { depth: null })
|
722
|
+
// console.log("typeSpan:", typeSpan)
|
723
|
+
// console.log("analysis:", analysisResult.text)
|
724
|
+
// console.log(relevantTypes.get(analysisResult.text))
|
725
|
+
const typ = relevantTypes.get(analysisResult.text).typeSpan.split(" = ")[1];
|
530
726
|
if (typ === undefined) {
|
531
727
|
return typeSpan;
|
532
728
|
}
|
533
|
-
normalForm += this.
|
729
|
+
normalForm += this.normalize2(typ, relevantTypes);
|
534
730
|
return normalForm;
|
535
731
|
}
|
536
732
|
else {
|
@@ -61,4 +61,20 @@ export declare class TypeScriptTypeChecker implements TypeChecker {
|
|
61
61
|
isUnion2(typeAnalysisResult: TypeAnalysis): boolean;
|
62
62
|
isArray2(typeAnalysisResult: TypeAnalysis): boolean;
|
63
63
|
isTypeAlias2(typeAnalysisResult: TypeAnalysis): boolean;
|
64
|
+
extractIdentifiers(code: string): {
|
65
|
+
name: string;
|
66
|
+
start: number;
|
67
|
+
end: number;
|
68
|
+
line: number;
|
69
|
+
column: number;
|
70
|
+
}[];
|
71
|
+
extractIdentifiersHelper(node: ts.Node, identifiers: Set<string>): void;
|
72
|
+
extractIdentifiersWithPosHelper(sourceFile: ts.SourceFile, node: ts.Node, identifiers: {
|
73
|
+
name: string;
|
74
|
+
start: number;
|
75
|
+
end: number;
|
76
|
+
line: number;
|
77
|
+
column: number;
|
78
|
+
}[]): void;
|
79
|
+
findDeclarationForIdentifier(sourceCode: string, targetLine: number, targetCharStart: number, targetCharEnd: number): string | null;
|
64
80
|
}
|
@@ -386,6 +386,9 @@ class TypeScriptTypeChecker {
|
|
386
386
|
type: this.analyzeTypeNode(element.type, checker)
|
387
387
|
};
|
388
388
|
}
|
389
|
+
else if (ts.isNamedTupleMember(element)) {
|
390
|
+
return this.analyzeTypeNode(element.type, checker);
|
391
|
+
}
|
389
392
|
else {
|
390
393
|
return this.analyzeTypeNode(element, checker);
|
391
394
|
}
|
@@ -508,5 +511,71 @@ class TypeScriptTypeChecker {
|
|
508
511
|
isTypeAlias2(typeAnalysisResult) {
|
509
512
|
return typeAnalysisResult.kind === "TypeReference";
|
510
513
|
}
|
514
|
+
extractIdentifiers(code) {
|
515
|
+
const sourceFile = ts.createSourceFile("sample.ts", code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
516
|
+
const identifiers = [];
|
517
|
+
this.extractIdentifiersWithPosHelper(sourceFile, sourceFile, identifiers);
|
518
|
+
return identifiers;
|
519
|
+
}
|
520
|
+
extractIdentifiersHelper(node, identifiers) {
|
521
|
+
if (ts.isIdentifier(node)) {
|
522
|
+
// console.log(node.kind, node.text)
|
523
|
+
if (ts.isTypeReferenceNode(node.parent) ||
|
524
|
+
ts.isTypeAliasDeclaration(node.parent) ||
|
525
|
+
ts.isInterfaceDeclaration(node.parent) ||
|
526
|
+
ts.isClassDeclaration(node.parent) ||
|
527
|
+
ts.isFunctionTypeNode(node.parent)) {
|
528
|
+
identifiers.add(node.getText());
|
529
|
+
}
|
530
|
+
}
|
531
|
+
node.forEachChild(child => this.extractIdentifiersHelper(child, identifiers));
|
532
|
+
}
|
533
|
+
extractIdentifiersWithPosHelper(sourceFile, node, identifiers) {
|
534
|
+
if (ts.isIdentifier(node)) {
|
535
|
+
// console.log(node.kind, node.text)
|
536
|
+
if (ts.isTypeReferenceNode(node.parent) ||
|
537
|
+
ts.isTypeAliasDeclaration(node.parent) ||
|
538
|
+
ts.isInterfaceDeclaration(node.parent) ||
|
539
|
+
ts.isClassDeclaration(node.parent) ||
|
540
|
+
ts.isFunctionTypeNode(node.parent)) {
|
541
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
542
|
+
identifiers.push({
|
543
|
+
name: node.getText(),
|
544
|
+
start: node.getStart(),
|
545
|
+
end: node.getEnd(),
|
546
|
+
line: line + 1, // Convert 0-based to 1-based
|
547
|
+
column: character + 1 // Convert 0-based to 1-based
|
548
|
+
});
|
549
|
+
}
|
550
|
+
}
|
551
|
+
node.forEachChild(child => this.extractIdentifiersWithPosHelper(sourceFile, child, identifiers));
|
552
|
+
}
|
553
|
+
findDeclarationForIdentifier(sourceCode, targetLine, targetCharStart, targetCharEnd) {
|
554
|
+
const sourceFile = ts.createSourceFile("sample.ts", sourceCode, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
555
|
+
function findDeclarationForIdentifierHelper(node) {
|
556
|
+
if (ts.isTypeAliasDeclaration(node)) {
|
557
|
+
const identifier = node.name;
|
558
|
+
// Get start and end positions for identifier
|
559
|
+
const startPos = sourceFile.getLineAndCharacterOfPosition(identifier.getStart());
|
560
|
+
const endPos = sourceFile.getLineAndCharacterOfPosition(identifier.getEnd());
|
561
|
+
// Match the identifier position
|
562
|
+
if (startPos.line === targetLine &&
|
563
|
+
startPos.character === targetCharStart &&
|
564
|
+
endPos.character === targetCharEnd) {
|
565
|
+
// Extract full declaration text from source code
|
566
|
+
return sourceCode.slice(node.getStart(), node.getEnd());
|
567
|
+
}
|
568
|
+
}
|
569
|
+
return ts.forEachChild(node, findDeclarationForIdentifierHelper) || null;
|
570
|
+
}
|
571
|
+
const declarationText = findDeclarationForIdentifierHelper(sourceFile);
|
572
|
+
// console.log(`declarationText: ${declarationText}`)
|
573
|
+
if (declarationText) {
|
574
|
+
return declarationText;
|
575
|
+
}
|
576
|
+
else {
|
577
|
+
return null;
|
578
|
+
}
|
579
|
+
}
|
511
580
|
}
|
512
581
|
exports.TypeScriptTypeChecker = TypeScriptTypeChecker;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@jpoly1219/context-extractor",
|
3
|
-
"version": "0.2.
|
3
|
+
"version": "0.2.8",
|
4
4
|
"description": "Extract relevant context from an incomplete program sketch.",
|
5
5
|
"repository": {
|
6
6
|
"type": "git",
|
@@ -12,7 +12,7 @@
|
|
12
12
|
"dist/"
|
13
13
|
],
|
14
14
|
"scripts": {
|
15
|
-
"build": "npx tsc && npm run copy-deps",
|
15
|
+
"build": "rm -rf dist/ && npx tsc && npm run copy-deps",
|
16
16
|
"copy-deps": "npm run copy-lsp-client && npm run copy-ocaml",
|
17
17
|
"copy-lsp-client": "shx cp -r ts-lsp-client-dist dist/",
|
18
18
|
"copy-ocaml": "shx mkdir -p dist/src/ocaml-utils/ && shx cp -r src/ocaml-utils/_build dist/src/ocaml-utils/_build/",
|