@wrongstack/tools 0.309.1 → 0.310.1
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/_regex.d.ts +6 -34
- package/dist/bash.js +3 -3
- package/dist/builtin.d.ts +17 -14
- package/dist/builtin.js +3028 -1675
- package/dist/codebase-index/binary-frame.d.ts +57 -8
- package/dist/codebase-index/codebase-incoming-calls-tool.d.ts +6 -0
- package/dist/codebase-index/codebase-outgoing-calls-tool.d.ts +6 -0
- package/dist/codebase-index/codebase-search-tool.d.ts +15 -5
- package/dist/codebase-index/index-service.d.ts +3 -19
- package/dist/codebase-index/index.js +2735 -1415
- package/dist/codebase-index/indexer.d.ts +3 -0
- package/dist/codebase-index/parser-batch.d.ts +53 -0
- package/dist/codebase-index/parser-dispatch.d.ts +32 -0
- package/dist/codebase-index/parser-output.d.ts +14 -0
- package/dist/codebase-index/parser-worker-pool.d.ts +57 -4
- package/dist/codebase-index/parser-worker-script.d.ts +5 -2
- package/dist/codebase-index/parser-worker-script.js +4042 -0
- package/dist/codebase-index/project-server-cache.d.ts +16 -0
- package/dist/codebase-index/project-server-client.d.ts +2 -2
- package/dist/codebase-index/project-server-query-cache.d.ts +88 -0
- package/dist/codebase-index/project-server.js +2846 -1308
- package/dist/codebase-index/py-parser.d.ts +5 -0
- package/dist/codebase-index/schema.d.ts +14 -1
- package/dist/codebase-index/sqlite-runtime.d.ts +2 -2
- package/dist/codebase-index/tree-sitter/queries.d.ts +30 -3
- package/dist/codebase-index/tree-sitter/visitor.d.ts +2 -1
- package/dist/codebase-index/vector-search.d.ts +12 -0
- package/dist/codebase-index/wal-maintenance.d.ts +58 -0
- package/dist/codebase-index/worker-protocol/contracts.d.ts +44 -0
- package/dist/codebase-index/worker-protocol.d.ts +17 -1
- package/dist/codebase-index/worker.js +2300 -1020
- package/dist/codebase-index/writer-admin.d.ts +11 -0
- package/dist/codebase-index/writer-helpers.d.ts +31 -1
- package/dist/codebase-index/writer-mutations.d.ts +0 -6
- package/dist/codebase-index/writer-schema.d.ts +2 -2
- package/dist/codebase-index/writer.d.ts +15 -0
- package/dist/edit.js +2511 -1203
- package/dist/exec.js +5 -3
- package/dist/grep.js +5 -124
- package/dist/index.js +3028 -1738
- package/dist/json.js +5 -124
- package/dist/kanban.js +130 -0
- package/dist/logs.js +5 -121
- package/dist/pack.js +3028 -1675
- package/dist/patch.js +2524 -1216
- package/dist/plan.js +106 -0
- package/dist/read.js +2506 -1198
- package/dist/replace.js +2487 -1295
- package/dist/search.js +6 -2
- package/dist/session-kanban.js +24 -16
- package/dist/task.js +106 -0
- package/dist/todo.js +106 -0
- package/dist/tool-tier.d.ts +11 -0
- package/dist/tool-tier.js +3040 -1679
- package/dist/tree.js +14 -3
- package/dist/win32.js +3 -3
- package/dist/write.js +2513 -1205
- package/package.json +5 -4
|
@@ -0,0 +1,4042 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res, err) => function __init() {
|
|
4
|
+
if (err) throw err[0];
|
|
5
|
+
try {
|
|
6
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
7
|
+
} catch (e) {
|
|
8
|
+
throw err = [e], e;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// src/_win32-resolve.ts
|
|
17
|
+
import * as fs from "node:fs";
|
|
18
|
+
import * as path from "node:path";
|
|
19
|
+
function resolveWin32Command(cmd) {
|
|
20
|
+
if (process.platform !== "win32") return cmd;
|
|
21
|
+
if (cmd.includes("/") || cmd.includes("\\") || path.extname(cmd.replace(/\//g, "\\"))) {
|
|
22
|
+
return cmd;
|
|
23
|
+
}
|
|
24
|
+
const pathext = (process.env["PATHEXT"] ?? ".COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC").toLowerCase().split(";");
|
|
25
|
+
const pathDirs = (process.env["PATH"] ?? "").split(path.delimiter);
|
|
26
|
+
for (const dir of pathDirs) {
|
|
27
|
+
const base = path.join(dir, cmd);
|
|
28
|
+
for (const ext of pathext) {
|
|
29
|
+
const full = `${base}${ext}`;
|
|
30
|
+
try {
|
|
31
|
+
fs.accessSync(full, fs.constants.X_OK);
|
|
32
|
+
return full;
|
|
33
|
+
} catch {
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return cmd;
|
|
38
|
+
}
|
|
39
|
+
var init_win32_resolve = __esm({
|
|
40
|
+
"src/_win32-resolve.ts"() {
|
|
41
|
+
"use strict";
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// src/codebase-index/parser-output.ts
|
|
46
|
+
function coerceSymbols(value) {
|
|
47
|
+
if (!Array.isArray(value)) return [];
|
|
48
|
+
return value.flatMap((entry) => {
|
|
49
|
+
const candidate = entry;
|
|
50
|
+
if (typeof candidate.name !== "string" || typeof candidate.kind !== "string") return [];
|
|
51
|
+
return [
|
|
52
|
+
{
|
|
53
|
+
name: candidate.name,
|
|
54
|
+
kind: candidate.kind,
|
|
55
|
+
line: typeof candidate.line === "number" ? candidate.line : 1,
|
|
56
|
+
col: typeof candidate.col === "number" ? candidate.col : 0,
|
|
57
|
+
signature: typeof candidate.signature === "string" ? candidate.signature : "",
|
|
58
|
+
scope: typeof candidate.scope === "string" ? candidate.scope : ""
|
|
59
|
+
}
|
|
60
|
+
];
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function coerceRefs(value, lang) {
|
|
64
|
+
if (!Array.isArray(value)) return [];
|
|
65
|
+
return value.flatMap((entry) => {
|
|
66
|
+
const candidate = entry;
|
|
67
|
+
if (typeof candidate.toName !== "string" || !candidate.toName) return [];
|
|
68
|
+
if (typeof candidate.callType !== "string" || !CALL_TYPES.has(candidate.callType)) return [];
|
|
69
|
+
const module = typeof candidate.module === "string" && candidate.module ? candidate.module : void 0;
|
|
70
|
+
return [
|
|
71
|
+
{
|
|
72
|
+
fromId: 0,
|
|
73
|
+
toName: candidate.toName,
|
|
74
|
+
callType: candidate.callType,
|
|
75
|
+
line: typeof candidate.line === "number" ? candidate.line : 1,
|
|
76
|
+
lang,
|
|
77
|
+
module
|
|
78
|
+
}
|
|
79
|
+
];
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
function parseParserOutput(stdout, lang) {
|
|
83
|
+
const trimmed = stdout.trim();
|
|
84
|
+
if (!trimmed) return { symbols: [], refs: [] };
|
|
85
|
+
let parsed;
|
|
86
|
+
try {
|
|
87
|
+
parsed = JSON.parse(trimmed);
|
|
88
|
+
} catch {
|
|
89
|
+
return { symbols: [], refs: [] };
|
|
90
|
+
}
|
|
91
|
+
if (Array.isArray(parsed)) return { symbols: coerceSymbols(parsed), refs: [] };
|
|
92
|
+
const record = parsed;
|
|
93
|
+
return {
|
|
94
|
+
symbols: coerceSymbols(record.symbols),
|
|
95
|
+
refs: dedupeRefs(coerceRefs(record.refs, lang))
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function parseParserBatchOutput(stdout, lang) {
|
|
99
|
+
const trimmed = stdout.trim();
|
|
100
|
+
if (!trimmed) return [];
|
|
101
|
+
let parsed;
|
|
102
|
+
try {
|
|
103
|
+
parsed = JSON.parse(trimmed);
|
|
104
|
+
} catch {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
if (!parsed || typeof parsed !== "object") return [];
|
|
108
|
+
const results = parsed.results;
|
|
109
|
+
if (!Array.isArray(results)) return [];
|
|
110
|
+
return results.flatMap((entry) => {
|
|
111
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) return [];
|
|
112
|
+
const candidate = entry;
|
|
113
|
+
if (typeof candidate.file !== "string" || !candidate.file) return [];
|
|
114
|
+
return [
|
|
115
|
+
{
|
|
116
|
+
file: candidate.file,
|
|
117
|
+
error: typeof candidate.error === "string" && candidate.error ? candidate.error : void 0,
|
|
118
|
+
symbols: coerceSymbols(candidate.symbols),
|
|
119
|
+
refs: dedupeRefs(coerceRefs(candidate.refs, lang))
|
|
120
|
+
}
|
|
121
|
+
];
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function dedupeRefs(refs) {
|
|
125
|
+
const seen = /* @__PURE__ */ new Set();
|
|
126
|
+
return refs.filter((ref) => {
|
|
127
|
+
const key = `${ref.toName}:${ref.callType}:${ref.line}:${ref.module ?? ""}`;
|
|
128
|
+
if (seen.has(key)) return false;
|
|
129
|
+
seen.add(key);
|
|
130
|
+
return true;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
var CALL_TYPES;
|
|
134
|
+
var init_parser_output = __esm({
|
|
135
|
+
"src/codebase-index/parser-output.ts"() {
|
|
136
|
+
"use strict";
|
|
137
|
+
CALL_TYPES = /* @__PURE__ */ new Set([
|
|
138
|
+
"call",
|
|
139
|
+
"type_ref",
|
|
140
|
+
"inherit",
|
|
141
|
+
"implement",
|
|
142
|
+
"import"
|
|
143
|
+
]);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// src/codebase-index/spawn-gate.ts
|
|
148
|
+
function withSpawnGate(fn) {
|
|
149
|
+
const run = chain.then(fn, fn);
|
|
150
|
+
chain = run.then(
|
|
151
|
+
() => void 0,
|
|
152
|
+
() => void 0
|
|
153
|
+
);
|
|
154
|
+
return run;
|
|
155
|
+
}
|
|
156
|
+
var chain;
|
|
157
|
+
var init_spawn_gate = __esm({
|
|
158
|
+
"src/codebase-index/spawn-gate.ts"() {
|
|
159
|
+
"use strict";
|
|
160
|
+
chain = Promise.resolve();
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// src/codebase-index/generic-parser.ts
|
|
165
|
+
var generic_parser_exports = {};
|
|
166
|
+
__export(generic_parser_exports, {
|
|
167
|
+
GENERIC_MAX_FILE_CHARS: () => GENERIC_MAX_FILE_CHARS,
|
|
168
|
+
GENERIC_MAX_SYMBOLS_DEFAULT: () => GENERIC_MAX_SYMBOLS_DEFAULT,
|
|
169
|
+
parseGeneric: () => parseGeneric,
|
|
170
|
+
parseSymbols: () => parseSymbols
|
|
171
|
+
});
|
|
172
|
+
function patternsFor(lang) {
|
|
173
|
+
return LANG_PATTERNS[lang] ?? LANG_PATTERNS.other ?? [];
|
|
174
|
+
}
|
|
175
|
+
function looksBinary(content) {
|
|
176
|
+
if (content.includes("\0")) return true;
|
|
177
|
+
const sample = content.slice(0, 2048);
|
|
178
|
+
if (sample.length === 0) return false;
|
|
179
|
+
let bad = 0;
|
|
180
|
+
for (let i = 0; i < sample.length; i++) {
|
|
181
|
+
const c = sample.charCodeAt(i);
|
|
182
|
+
if (c === 9 || c === 10 || c === 13) continue;
|
|
183
|
+
if (c < 32 || c === 127) bad++;
|
|
184
|
+
}
|
|
185
|
+
return bad / sample.length > 0.1;
|
|
186
|
+
}
|
|
187
|
+
function newlineOffsets2(content) {
|
|
188
|
+
const offsets = [];
|
|
189
|
+
for (let i = 0; i < content.length; i++) {
|
|
190
|
+
if (content.charCodeAt(i) === 10) offsets.push(i);
|
|
191
|
+
}
|
|
192
|
+
return offsets;
|
|
193
|
+
}
|
|
194
|
+
function lineColAt(offsets, index) {
|
|
195
|
+
let low = 0;
|
|
196
|
+
let high = offsets.length;
|
|
197
|
+
while (low < high) {
|
|
198
|
+
const mid = low + high >>> 1;
|
|
199
|
+
if ((offsets[mid] ?? 0) < index) low = mid + 1;
|
|
200
|
+
else high = mid;
|
|
201
|
+
}
|
|
202
|
+
const lastNl = low > 0 ? offsets[low - 1] : -1;
|
|
203
|
+
return { line: low + 1, col: index - lastNl };
|
|
204
|
+
}
|
|
205
|
+
function parseGeneric(opts) {
|
|
206
|
+
const { file, lang } = opts;
|
|
207
|
+
const maxSymbols = opts.maxSymbols ?? GENERIC_MAX_SYMBOLS_DEFAULT;
|
|
208
|
+
const mtimeMs = Date.now();
|
|
209
|
+
if (!opts.content || looksBinary(opts.content)) {
|
|
210
|
+
return { file, lang, symbols: [], mtimeMs };
|
|
211
|
+
}
|
|
212
|
+
const content = opts.content.length > GENERIC_MAX_FILE_CHARS ? opts.content.slice(0, GENERIC_MAX_FILE_CHARS) : opts.content;
|
|
213
|
+
const patterns = patternsFor(lang);
|
|
214
|
+
const symbols = [];
|
|
215
|
+
const seen = /* @__PURE__ */ new Set();
|
|
216
|
+
const nlOffsets = newlineOffsets2(content);
|
|
217
|
+
for (const pattern of patterns) {
|
|
218
|
+
const re = new RegExp(
|
|
219
|
+
pattern.re.source,
|
|
220
|
+
pattern.re.flags.includes("g") ? pattern.re.flags : `${pattern.re.flags}g`
|
|
221
|
+
);
|
|
222
|
+
re.lastIndex = 0;
|
|
223
|
+
for (const match of content.matchAll(re)) {
|
|
224
|
+
if (symbols.length >= maxSymbols) break;
|
|
225
|
+
let name = (match[1] ?? match[2] ?? "").trim();
|
|
226
|
+
if (lang === "md" && match[2]) name = match[2].trim();
|
|
227
|
+
if (!name || name.length > 200) continue;
|
|
228
|
+
name = name.replace(/^#+\s*/, "").replace(/["'`]/g, "");
|
|
229
|
+
if (!name || KEYWORDS.has(name.toLowerCase())) continue;
|
|
230
|
+
if (!/^[A-Za-z_#.@/\w][\w.\-:/#!?]*$/.test(name) && lang !== "md" && lang !== "toml") {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const { line, col } = lineColAt(nlOffsets, match.index ?? 0);
|
|
234
|
+
const key = `${name}\0${line}\0${pattern.kind}`;
|
|
235
|
+
if (seen.has(key)) continue;
|
|
236
|
+
seen.add(key);
|
|
237
|
+
const nl = content.indexOf("\n", match.index);
|
|
238
|
+
const lineText = content.slice(match.index, nl === -1 ? content.length : nl);
|
|
239
|
+
const signature = (lineText || name).trim().slice(0, 500);
|
|
240
|
+
symbols.push({
|
|
241
|
+
id: 0,
|
|
242
|
+
lang,
|
|
243
|
+
kind: lang === "md" ? "namespace" : pattern.kind,
|
|
244
|
+
name: name.slice(0, 200),
|
|
245
|
+
file,
|
|
246
|
+
line,
|
|
247
|
+
col,
|
|
248
|
+
signature,
|
|
249
|
+
docComment: "",
|
|
250
|
+
scope: "",
|
|
251
|
+
text: `${name} ${signature}`.trim().slice(0, 1e3)
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
if (symbols.length >= maxSymbols) break;
|
|
255
|
+
}
|
|
256
|
+
return { file, lang, symbols, mtimeMs };
|
|
257
|
+
}
|
|
258
|
+
async function parseSymbols(opts) {
|
|
259
|
+
return parseGeneric(opts);
|
|
260
|
+
}
|
|
261
|
+
var C_LIKE, LANG_PATTERNS, KEYWORDS, GENERIC_MAX_SYMBOLS_DEFAULT, GENERIC_MAX_FILE_CHARS;
|
|
262
|
+
var init_generic_parser = __esm({
|
|
263
|
+
"src/codebase-index/generic-parser.ts"() {
|
|
264
|
+
"use strict";
|
|
265
|
+
C_LIKE = [
|
|
266
|
+
{ re: /\b(?:class|struct|enum|interface|union)\s+([A-Za-z_]\w*)/g, kind: "class" },
|
|
267
|
+
{
|
|
268
|
+
re: /\b(?:public|private|protected|static|final|async|override|virtual|inline|export)?\s*(?:[\w:<>[\]\s*&]+)\s+([A-Za-z_]\w*)\s*\([^;{]*\)\s*(?:const)?\s*[{;]/g,
|
|
269
|
+
kind: "function"
|
|
270
|
+
},
|
|
271
|
+
{ re: /\b(?:namespace)\s+([A-Za-z_]\w*)/g, kind: "namespace" }
|
|
272
|
+
];
|
|
273
|
+
LANG_PATTERNS = {
|
|
274
|
+
py: [
|
|
275
|
+
{ re: /^(?:async\s+)?def\s+([A-Za-z_]\w*)/gm, kind: "function" },
|
|
276
|
+
{ re: /^class\s+([A-Za-z_]\w*)/gm, kind: "class" },
|
|
277
|
+
{ re: /^([A-Za-z_]\w*)\s*=/gm, kind: "var" }
|
|
278
|
+
],
|
|
279
|
+
go: [
|
|
280
|
+
{ re: /^func\s+(?:\([^)]*\)\s*)?([A-Za-z_]\w*)\s*\(/gm, kind: "function" },
|
|
281
|
+
{ re: /^type\s+([A-Za-z_]\w*)\b/gm, kind: "type" },
|
|
282
|
+
{ re: /^(?:const|var)\s+([A-Za-z_]\w*)\b/gm, kind: "const" },
|
|
283
|
+
{ re: /^package\s+([A-Za-z_]\w*)/gm, kind: "namespace" }
|
|
284
|
+
],
|
|
285
|
+
rs: [
|
|
286
|
+
{ re: /\bfn\s+([A-Za-z_]\w*)/g, kind: "function" },
|
|
287
|
+
{ re: /\bstruct\s+([A-Za-z_]\w*)/g, kind: "struct" },
|
|
288
|
+
{ re: /\benum\s+([A-Za-z_]\w*)/g, kind: "enum" },
|
|
289
|
+
{ re: /\btrait\s+([A-Za-z_]\w*)/g, kind: "trait" },
|
|
290
|
+
{ re: /\bimpl(?:\s*<[^>]+>)?\s+([A-Za-z_]\w*)/g, kind: "impl" },
|
|
291
|
+
{ re: /\b(?:const|static)\s+([A-Za-z_]\w*)/g, kind: "const" },
|
|
292
|
+
{ re: /\bmod\s+([A-Za-z_]\w*)/g, kind: "mod" }
|
|
293
|
+
],
|
|
294
|
+
c: C_LIKE,
|
|
295
|
+
cpp: C_LIKE,
|
|
296
|
+
java: [
|
|
297
|
+
{ re: /\b(?:class|interface|enum|record)\s+([A-Za-z_]\w*)/g, kind: "class" },
|
|
298
|
+
{
|
|
299
|
+
re: /\b(?:public|private|protected|static|final|abstract|synchronized|native|default|\s)+\s*[\w.<>,[\]\s]+\s+([A-Za-z_]\w*)\s*\(/g,
|
|
300
|
+
kind: "method"
|
|
301
|
+
}
|
|
302
|
+
],
|
|
303
|
+
csharp: [
|
|
304
|
+
{ re: /\b(?:class|interface|struct|enum|record)\s+([A-Za-z_]\w*)/g, kind: "class" },
|
|
305
|
+
{ re: /\bnamespace\s+([A-Za-z_.\w]+)/g, kind: "namespace" },
|
|
306
|
+
{
|
|
307
|
+
re: /\b(?:public|private|protected|internal|static|async|override|virtual|\s)+\s*[\w.<>,[\]\s]+\s+([A-Za-z_]\w*)\s*\(/g,
|
|
308
|
+
kind: "method"
|
|
309
|
+
}
|
|
310
|
+
],
|
|
311
|
+
php: [
|
|
312
|
+
{ re: /\bfunction\s+([A-Za-z_]\w*)/g, kind: "function" },
|
|
313
|
+
{ re: /\bclass\s+([A-Za-z_]\w*)/g, kind: "class" },
|
|
314
|
+
{ re: /\binterface\s+([A-Za-z_]\w*)/g, kind: "interface" },
|
|
315
|
+
{ re: /\bnamespace\s+([A-Za-z_\\]+)/g, kind: "namespace" }
|
|
316
|
+
],
|
|
317
|
+
ruby: [
|
|
318
|
+
{ re: /^\s*def\s+(?:self\.)?([A-Za-z_]\w*[!?]?)/gm, kind: "function" },
|
|
319
|
+
{ re: /^\s*class\s+([A-Za-z_]\w*)/gm, kind: "class" },
|
|
320
|
+
{ re: /^\s*module\s+([A-Za-z_]\w*)/gm, kind: "namespace" }
|
|
321
|
+
],
|
|
322
|
+
swift: [
|
|
323
|
+
{ re: /\b(?:func)\s+([A-Za-z_]\w*)/g, kind: "function" },
|
|
324
|
+
{ re: /\b(?:class|struct|enum|protocol|actor)\s+([A-Za-z_]\w*)/g, kind: "class" }
|
|
325
|
+
],
|
|
326
|
+
kotlin: [
|
|
327
|
+
{ re: /\b(?:fun)\s+([A-Za-z_]\w*)/g, kind: "function" },
|
|
328
|
+
{
|
|
329
|
+
re: /\b(?:class|interface|object|enum\s+class|data\s+class)\s+([A-Za-z_]\w*)/g,
|
|
330
|
+
kind: "class"
|
|
331
|
+
}
|
|
332
|
+
],
|
|
333
|
+
scala: [
|
|
334
|
+
{ re: /\b(?:def)\s+([A-Za-z_]\w*)/g, kind: "function" },
|
|
335
|
+
{ re: /\b(?:class|object|trait|enum)\s+([A-Za-z_]\w*)/g, kind: "class" }
|
|
336
|
+
],
|
|
337
|
+
shell: [
|
|
338
|
+
{ re: /^(?:function\s+)?([A-Za-z_]\w*)\s*\(\)\s*\{/gm, kind: "function" },
|
|
339
|
+
{ re: /^([A-Za-z_][\w]*)\s*\(\)\s*\{/gm, kind: "function" }
|
|
340
|
+
],
|
|
341
|
+
sql: [
|
|
342
|
+
{
|
|
343
|
+
re: /\bCREATE\s+(?:OR\s+REPLACE\s+)?(?:TABLE|VIEW|INDEX|FUNCTION|PROCEDURE|TRIGGER)\s+(?:IF\s+NOT\s+EXISTS\s+)?([A-Za-z_"][\w."]*)/gi,
|
|
344
|
+
kind: "type"
|
|
345
|
+
}
|
|
346
|
+
],
|
|
347
|
+
md: [{ re: /^(#{1,6})\s+(.+)$/gm, kind: "namespace" }],
|
|
348
|
+
toml: [{ re: /^\[([^\]]+)\]/gm, kind: "namespace" }],
|
|
349
|
+
html: [
|
|
350
|
+
{ re: /\bid\s*=\s*["']([^"']+)["']/gi, kind: "property" },
|
|
351
|
+
{ re: /<(?:script|template|style)\b/gi, kind: "namespace" }
|
|
352
|
+
],
|
|
353
|
+
css: [
|
|
354
|
+
{ re: /^\s*([.#]?[A-Za-z_][\w-]*)\s*\{/gm, kind: "type" },
|
|
355
|
+
{ re: /@(?:keyframes|media|supports)\s+([^{\s]+)/g, kind: "namespace" }
|
|
356
|
+
],
|
|
357
|
+
vue: [
|
|
358
|
+
{
|
|
359
|
+
re: /\b(?:function|const|let|var|class|export\s+(?:default\s+)?(?:function|class|const))\s+([A-Za-z_]\w*)/g,
|
|
360
|
+
kind: "function"
|
|
361
|
+
},
|
|
362
|
+
{ re: /<(?:script|template|style)\b/gi, kind: "namespace" }
|
|
363
|
+
],
|
|
364
|
+
svelte: [
|
|
365
|
+
{
|
|
366
|
+
re: /\b(?:function|const|let|var|class|export\s+(?:default\s+)?(?:function|class|const))\s+([A-Za-z_]\w*)/g,
|
|
367
|
+
kind: "function"
|
|
368
|
+
}
|
|
369
|
+
],
|
|
370
|
+
dart: [
|
|
371
|
+
{ re: /\b(?:class|enum|mixin|extension)\s+([A-Za-z_]\w*)/g, kind: "class" },
|
|
372
|
+
{ re: /\b([A-Za-z_]\w*)\s*\([^;]*\)\s*(?:async\s*)?\{/g, kind: "function" }
|
|
373
|
+
],
|
|
374
|
+
lua: [
|
|
375
|
+
{ re: /\bfunction\s+([A-Za-z_.:]\w*)/g, kind: "function" },
|
|
376
|
+
{ re: /\blocal\s+function\s+([A-Za-z_]\w*)/g, kind: "function" }
|
|
377
|
+
],
|
|
378
|
+
r: [
|
|
379
|
+
{ re: /([A-Za-z.]\w*)\s*<-\s*function\s*\(/g, kind: "function" },
|
|
380
|
+
{ re: /([A-Za-z.]\w*)\s*=\s*function\s*\(/g, kind: "function" }
|
|
381
|
+
],
|
|
382
|
+
proto: [
|
|
383
|
+
{ re: /\b(?:message|service|enum)\s+([A-Za-z_]\w*)/g, kind: "type" },
|
|
384
|
+
{ re: /\brpc\s+([A-Za-z_]\w*)/g, kind: "function" }
|
|
385
|
+
],
|
|
386
|
+
graphql: [
|
|
387
|
+
{ re: /\b(?:type|interface|enum|input|union|scalar)\s+([A-Za-z_]\w*)/g, kind: "type" },
|
|
388
|
+
{ re: /\b(?:query|mutation|subscription)\s+([A-Za-z_]\w*)/g, kind: "function" }
|
|
389
|
+
],
|
|
390
|
+
zig: [
|
|
391
|
+
{ re: /\b(?:fn|pub\s+fn)\s+([A-Za-z_]\w*)/g, kind: "function" },
|
|
392
|
+
{ re: /\b(?:const|var)\s+([A-Za-z_]\w*)/g, kind: "const" },
|
|
393
|
+
{ re: /\b(?:struct|enum|union)\s*\{/g, kind: "type" }
|
|
394
|
+
],
|
|
395
|
+
elixir: [
|
|
396
|
+
{ re: /\bdef(?:p|macro|macrop)?\s+([A-Za-z_]\w*[!?]?)/g, kind: "function" },
|
|
397
|
+
// Dotted module names must be captured whole: `alias Foo.Bar` resolves
|
|
398
|
+
// against this symbol, and a `Foo`-only capture never matches it.
|
|
399
|
+
{ re: /\bdefmodule\s+([A-Z][\w.]*)/g, kind: "namespace" }
|
|
400
|
+
],
|
|
401
|
+
haskell: [
|
|
402
|
+
// Target of `import Data.List`.
|
|
403
|
+
{ re: /^module\s+([A-Z][\w.]*)/gm, kind: "namespace" },
|
|
404
|
+
{ re: /^([A-Za-z_]\w*)\s*::/gm, kind: "function" },
|
|
405
|
+
{ re: /\bdata\s+([A-Za-z_]\w*)/g, kind: "type" },
|
|
406
|
+
{ re: /\btype\s+(?:family\s+)?([A-Za-z_]\w*)/g, kind: "type" },
|
|
407
|
+
{ re: /\bclass\s+([A-Za-z_]\w*)/g, kind: "class" }
|
|
408
|
+
],
|
|
409
|
+
other: [
|
|
410
|
+
{ re: /^(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_]\w*)/gm, kind: "function" },
|
|
411
|
+
{ re: /^(?:export\s+)?class\s+([A-Za-z_]\w*)/gm, kind: "class" },
|
|
412
|
+
{ re: /^(?:export\s+)?(?:const|let|var)\s+([A-Za-z_]\w*)/gm, kind: "const" },
|
|
413
|
+
{ re: /^(?:async\s+)?def\s+([A-Za-z_]\w*)/gm, kind: "function" },
|
|
414
|
+
{ re: /^func\s+(?:\([^)]*\)\s*)?([A-Za-z_]\w*)/gm, kind: "function" },
|
|
415
|
+
{ re: /\bfn\s+([A-Za-z_]\w*)/g, kind: "function" },
|
|
416
|
+
{ re: /^(?:target|PHONY)\s*:/gm, kind: "namespace" }
|
|
417
|
+
]
|
|
418
|
+
};
|
|
419
|
+
KEYWORDS = /* @__PURE__ */ new Set([
|
|
420
|
+
"if",
|
|
421
|
+
"else",
|
|
422
|
+
"for",
|
|
423
|
+
"while",
|
|
424
|
+
"switch",
|
|
425
|
+
"case",
|
|
426
|
+
"return",
|
|
427
|
+
"break",
|
|
428
|
+
"continue",
|
|
429
|
+
"new",
|
|
430
|
+
"delete",
|
|
431
|
+
"typeof",
|
|
432
|
+
"instanceof",
|
|
433
|
+
"void",
|
|
434
|
+
"null",
|
|
435
|
+
"true",
|
|
436
|
+
"false",
|
|
437
|
+
"this",
|
|
438
|
+
"super",
|
|
439
|
+
"import",
|
|
440
|
+
"export",
|
|
441
|
+
"from",
|
|
442
|
+
"as",
|
|
443
|
+
"default",
|
|
444
|
+
"public",
|
|
445
|
+
"private",
|
|
446
|
+
"protected",
|
|
447
|
+
"static",
|
|
448
|
+
"final",
|
|
449
|
+
"class",
|
|
450
|
+
"struct",
|
|
451
|
+
"enum",
|
|
452
|
+
"interface",
|
|
453
|
+
"function",
|
|
454
|
+
"def",
|
|
455
|
+
"fn",
|
|
456
|
+
"func",
|
|
457
|
+
"var",
|
|
458
|
+
"let",
|
|
459
|
+
"const",
|
|
460
|
+
"package",
|
|
461
|
+
"namespace",
|
|
462
|
+
"module",
|
|
463
|
+
"using",
|
|
464
|
+
"include",
|
|
465
|
+
"require",
|
|
466
|
+
"select",
|
|
467
|
+
"from",
|
|
468
|
+
"where",
|
|
469
|
+
"and",
|
|
470
|
+
"or",
|
|
471
|
+
"not",
|
|
472
|
+
"in",
|
|
473
|
+
"is",
|
|
474
|
+
"try",
|
|
475
|
+
"catch",
|
|
476
|
+
"finally",
|
|
477
|
+
"throw",
|
|
478
|
+
"async",
|
|
479
|
+
"await",
|
|
480
|
+
"yield"
|
|
481
|
+
]);
|
|
482
|
+
GENERIC_MAX_SYMBOLS_DEFAULT = 500;
|
|
483
|
+
GENERIC_MAX_FILE_CHARS = 512 * 1024;
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
// src/codebase-index/languages.ts
|
|
488
|
+
import * as path3 from "node:path";
|
|
489
|
+
function detectLang(file) {
|
|
490
|
+
const base = path3.basename(file);
|
|
491
|
+
const lowerBase = base.toLowerCase();
|
|
492
|
+
if (lowerBase.endsWith(".d.ts") || lowerBase.endsWith(".d.mts") || lowerBase.endsWith(".d.cts")) {
|
|
493
|
+
return "ts";
|
|
494
|
+
}
|
|
495
|
+
const special = SPECIAL_FILENAMES[lowerBase];
|
|
496
|
+
if (special) return special;
|
|
497
|
+
const ext = path3.extname(base).toLowerCase();
|
|
498
|
+
if (!ext) return null;
|
|
499
|
+
return EXT_TO_LANG[ext] ?? null;
|
|
500
|
+
}
|
|
501
|
+
var EXT_TO_LANG, INDEXABLE_EXTENSIONS, SPECIAL_FILENAMES, LANG_FAMILY, LANG_FAMILY_ENTRIES;
|
|
502
|
+
var init_languages = __esm({
|
|
503
|
+
"src/codebase-index/languages.ts"() {
|
|
504
|
+
"use strict";
|
|
505
|
+
EXT_TO_LANG = {
|
|
506
|
+
// TypeScript / JavaScript (first-class TS compiler API)
|
|
507
|
+
".ts": "ts",
|
|
508
|
+
".mts": "ts",
|
|
509
|
+
".cts": "ts",
|
|
510
|
+
".tsx": "tsx",
|
|
511
|
+
".js": "js",
|
|
512
|
+
".mjs": "js",
|
|
513
|
+
".cjs": "js",
|
|
514
|
+
".jsx": "jsx",
|
|
515
|
+
// First-class native/spawn parsers (+ regex fallback)
|
|
516
|
+
".go": "go",
|
|
517
|
+
".py": "py",
|
|
518
|
+
".pyi": "py",
|
|
519
|
+
".pyw": "py",
|
|
520
|
+
".rs": "rs",
|
|
521
|
+
".json": "json",
|
|
522
|
+
".jsonc": "json",
|
|
523
|
+
".yaml": "yaml",
|
|
524
|
+
".yml": "yaml",
|
|
525
|
+
// C family
|
|
526
|
+
".c": "c",
|
|
527
|
+
".h": "c",
|
|
528
|
+
".cc": "cpp",
|
|
529
|
+
".cpp": "cpp",
|
|
530
|
+
".cxx": "cpp",
|
|
531
|
+
".hh": "cpp",
|
|
532
|
+
".hpp": "cpp",
|
|
533
|
+
".hxx": "cpp",
|
|
534
|
+
// JVM / .NET
|
|
535
|
+
".java": "java",
|
|
536
|
+
".cs": "csharp",
|
|
537
|
+
".kt": "kotlin",
|
|
538
|
+
".kts": "kotlin",
|
|
539
|
+
".scala": "scala",
|
|
540
|
+
".sc": "scala",
|
|
541
|
+
// Scripting
|
|
542
|
+
".php": "php",
|
|
543
|
+
".rb": "ruby",
|
|
544
|
+
".swift": "swift",
|
|
545
|
+
".dart": "dart",
|
|
546
|
+
".lua": "lua",
|
|
547
|
+
".r": "r",
|
|
548
|
+
".R": "r",
|
|
549
|
+
".pl": "other",
|
|
550
|
+
".pm": "other",
|
|
551
|
+
// Systems / functional
|
|
552
|
+
".zig": "zig",
|
|
553
|
+
".ex": "elixir",
|
|
554
|
+
".exs": "elixir",
|
|
555
|
+
".hs": "haskell",
|
|
556
|
+
".lhs": "haskell",
|
|
557
|
+
// Shell / data / docs / web
|
|
558
|
+
".sh": "shell",
|
|
559
|
+
".bash": "shell",
|
|
560
|
+
".zsh": "shell",
|
|
561
|
+
".ps1": "shell",
|
|
562
|
+
".sql": "sql",
|
|
563
|
+
".md": "md",
|
|
564
|
+
".mdx": "md",
|
|
565
|
+
".toml": "toml",
|
|
566
|
+
".html": "html",
|
|
567
|
+
".htm": "html",
|
|
568
|
+
".css": "css",
|
|
569
|
+
".scss": "css",
|
|
570
|
+
".less": "css",
|
|
571
|
+
".vue": "vue",
|
|
572
|
+
".svelte": "svelte",
|
|
573
|
+
".proto": "proto",
|
|
574
|
+
".graphql": "graphql",
|
|
575
|
+
".gql": "graphql"
|
|
576
|
+
};
|
|
577
|
+
INDEXABLE_EXTENSIONS = Object.freeze(
|
|
578
|
+
[...new Set(Object.keys(EXT_TO_LANG).map((e) => e.toLowerCase()))].sort()
|
|
579
|
+
);
|
|
580
|
+
SPECIAL_FILENAMES = {
|
|
581
|
+
makefile: "other",
|
|
582
|
+
gnumakefile: "other",
|
|
583
|
+
dockerfile: "other",
|
|
584
|
+
"docker-compose.yml": "yaml",
|
|
585
|
+
"docker-compose.yaml": "yaml",
|
|
586
|
+
"cmakelists.txt": "other",
|
|
587
|
+
gemfile: "ruby",
|
|
588
|
+
rakefile: "ruby",
|
|
589
|
+
procfile: "other",
|
|
590
|
+
justfile: "other"
|
|
591
|
+
};
|
|
592
|
+
LANG_FAMILY = {
|
|
593
|
+
// Single-compilation-unit family: a .vue/.svelte script block is JS/TS and
|
|
594
|
+
// imports from — and is imported by — plain .ts files.
|
|
595
|
+
ts: "js",
|
|
596
|
+
tsx: "js",
|
|
597
|
+
js: "js",
|
|
598
|
+
jsx: "js",
|
|
599
|
+
vue: "js",
|
|
600
|
+
svelte: "js",
|
|
601
|
+
go: "go",
|
|
602
|
+
py: "py",
|
|
603
|
+
rs: "rs",
|
|
604
|
+
// The JVM resolves across languages: Kotlin and Scala call Java directly.
|
|
605
|
+
java: "jvm",
|
|
606
|
+
kotlin: "jvm",
|
|
607
|
+
scala: "jvm",
|
|
608
|
+
csharp: "dotnet",
|
|
609
|
+
// A .h header is consumed by both C and C++ translation units.
|
|
610
|
+
c: "c",
|
|
611
|
+
cpp: "c",
|
|
612
|
+
ruby: "ruby",
|
|
613
|
+
php: "php",
|
|
614
|
+
swift: "swift",
|
|
615
|
+
dart: "dart",
|
|
616
|
+
elixir: "elixir",
|
|
617
|
+
haskell: "haskell",
|
|
618
|
+
zig: "zig",
|
|
619
|
+
lua: "lua",
|
|
620
|
+
r: "r",
|
|
621
|
+
shell: "shell",
|
|
622
|
+
sql: "sql",
|
|
623
|
+
json: "data",
|
|
624
|
+
yaml: "data",
|
|
625
|
+
toml: "data",
|
|
626
|
+
html: "web",
|
|
627
|
+
css: "web",
|
|
628
|
+
proto: "proto",
|
|
629
|
+
graphql: "graphql",
|
|
630
|
+
md: "other",
|
|
631
|
+
other: "other"
|
|
632
|
+
};
|
|
633
|
+
LANG_FAMILY_ENTRIES = Object.freeze(
|
|
634
|
+
Object.entries(LANG_FAMILY).map(
|
|
635
|
+
([lang, family]) => Object.freeze([lang, family])
|
|
636
|
+
)
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
// src/codebase-index/py-parser.ts
|
|
642
|
+
var py_parser_exports = {};
|
|
643
|
+
__export(py_parser_exports, {
|
|
644
|
+
detectLang: () => detectLang,
|
|
645
|
+
parseSymbols: () => parseSymbols2,
|
|
646
|
+
resolvePythonBinary: () => resolvePythonBinary
|
|
647
|
+
});
|
|
648
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
649
|
+
import * as fs3 from "node:fs/promises";
|
|
650
|
+
import * as os2 from "node:os";
|
|
651
|
+
import * as path4 from "node:path";
|
|
652
|
+
async function parseSymbols2(opts) {
|
|
653
|
+
const { file, content, lang } = opts;
|
|
654
|
+
try {
|
|
655
|
+
const native = await withSpawnGate(() => syncPyParse(file, content, lang));
|
|
656
|
+
if (native !== null) return native;
|
|
657
|
+
} catch {
|
|
658
|
+
}
|
|
659
|
+
return parseGeneric({ file, content, lang: lang === "py" ? "py" : lang });
|
|
660
|
+
}
|
|
661
|
+
async function resolvePython() {
|
|
662
|
+
const candidates = process.platform === "win32" ? ["python3", "python", "py"] : ["python3", "python"];
|
|
663
|
+
for (const name of candidates) {
|
|
664
|
+
const resolved = resolveWin32Command(name);
|
|
665
|
+
if (!await commandIsAvailable(resolved)) continue;
|
|
666
|
+
return resolved;
|
|
667
|
+
}
|
|
668
|
+
return null;
|
|
669
|
+
}
|
|
670
|
+
function commandIsAvailable(command) {
|
|
671
|
+
return new Promise((resolve) => {
|
|
672
|
+
let settled = false;
|
|
673
|
+
const proc = spawn2(command, ["--version"], {
|
|
674
|
+
stdio: "ignore",
|
|
675
|
+
windowsHide: true
|
|
676
|
+
});
|
|
677
|
+
const finish = (available) => {
|
|
678
|
+
if (settled) return;
|
|
679
|
+
settled = true;
|
|
680
|
+
clearTimeout(timer);
|
|
681
|
+
resolve(available);
|
|
682
|
+
};
|
|
683
|
+
const timer = setTimeout(() => {
|
|
684
|
+
proc.kill("SIGKILL");
|
|
685
|
+
finish(false);
|
|
686
|
+
}, 5e3);
|
|
687
|
+
timer.unref?.();
|
|
688
|
+
proc.once("error", () => finish(false));
|
|
689
|
+
proc.once("close", (code) => finish(code === 0));
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
function spawnPyParser(pyBinary, scriptPath, filePath, content) {
|
|
693
|
+
return new Promise((resolve, reject) => {
|
|
694
|
+
let settled = false;
|
|
695
|
+
const proc = spawn2(pyBinary, [scriptPath, filePath], {
|
|
696
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
697
|
+
windowsHide: true
|
|
698
|
+
});
|
|
699
|
+
proc.on("error", (err) => {
|
|
700
|
+
if (settled) return;
|
|
701
|
+
settled = true;
|
|
702
|
+
reject(err);
|
|
703
|
+
});
|
|
704
|
+
proc.stdin?.write(content);
|
|
705
|
+
proc.stdin?.end();
|
|
706
|
+
let stdout = "";
|
|
707
|
+
proc.stdout?.on("data", (chunk) => {
|
|
708
|
+
stdout += chunk.toString();
|
|
709
|
+
});
|
|
710
|
+
proc.stderr?.resume();
|
|
711
|
+
const timer = setTimeout(() => {
|
|
712
|
+
if (settled) return;
|
|
713
|
+
settled = true;
|
|
714
|
+
proc.kill("SIGKILL");
|
|
715
|
+
reject(new Error("timeout"));
|
|
716
|
+
}, 15e3);
|
|
717
|
+
timer.unref?.();
|
|
718
|
+
proc.on("close", (code) => {
|
|
719
|
+
if (settled) return;
|
|
720
|
+
settled = true;
|
|
721
|
+
clearTimeout(timer);
|
|
722
|
+
resolve({ code, stdout });
|
|
723
|
+
});
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
function resolvePythonBinary() {
|
|
727
|
+
cachedPyBinary ??= resolvePython();
|
|
728
|
+
return cachedPyBinary;
|
|
729
|
+
}
|
|
730
|
+
async function syncPyParse(filePath, content, lang) {
|
|
731
|
+
try {
|
|
732
|
+
if (!_cachedScriptPath) {
|
|
733
|
+
const tmpDir = path4.join(os2.tmpdir(), "ws-py-parse");
|
|
734
|
+
await fs3.mkdir(tmpDir, { recursive: true });
|
|
735
|
+
_cachedScriptPath = path4.join(tmpDir, "parse.py");
|
|
736
|
+
await fs3.writeFile(_cachedScriptPath, PY_PARSE_SCRIPT, "utf8");
|
|
737
|
+
}
|
|
738
|
+
cachedPyBinary ??= resolvePython();
|
|
739
|
+
const pyBinary = await cachedPyBinary;
|
|
740
|
+
if (!pyBinary) return null;
|
|
741
|
+
const { code, stdout } = await spawnPyParser(pyBinary, _cachedScriptPath, filePath, content);
|
|
742
|
+
if (code !== 0 || !stdout.trim()) {
|
|
743
|
+
return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
|
|
744
|
+
}
|
|
745
|
+
const { symbols: raw, refs } = parseParserOutput(stdout, lang);
|
|
746
|
+
const symbols = raw.map((s) => ({
|
|
747
|
+
id: 0,
|
|
748
|
+
lang,
|
|
749
|
+
kind: s.kind,
|
|
750
|
+
name: s.name,
|
|
751
|
+
file: filePath,
|
|
752
|
+
line: s.line,
|
|
753
|
+
col: s.col,
|
|
754
|
+
signature: s.signature ?? "",
|
|
755
|
+
docComment: "",
|
|
756
|
+
scope: s.scope ?? "",
|
|
757
|
+
text: `${s.name} ${s.signature ?? ""}`.trim()
|
|
758
|
+
}));
|
|
759
|
+
return { file: filePath, lang, symbols, refs, mtimeMs: Date.now() };
|
|
760
|
+
} catch {
|
|
761
|
+
return null;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
var PY_PARSE_SCRIPT, _cachedScriptPath, cachedPyBinary;
|
|
765
|
+
var init_py_parser = __esm({
|
|
766
|
+
"src/codebase-index/py-parser.ts"() {
|
|
767
|
+
"use strict";
|
|
768
|
+
init_win32_resolve();
|
|
769
|
+
init_generic_parser();
|
|
770
|
+
init_parser_output();
|
|
771
|
+
init_spawn_gate();
|
|
772
|
+
init_languages();
|
|
773
|
+
PY_PARSE_SCRIPT = `import ast, json, sys, os
|
|
774
|
+
|
|
775
|
+
def get_name(node):
|
|
776
|
+
if isinstance(node, ast.Name):
|
|
777
|
+
return node.id
|
|
778
|
+
elif isinstance(node, ast.Attribute):
|
|
779
|
+
return get_name(node.value) + "." + node.attr
|
|
780
|
+
elif isinstance(node, ast.Subscript):
|
|
781
|
+
return get_name(node.value)
|
|
782
|
+
elif isinstance(node, ast.Call):
|
|
783
|
+
return get_name(node.func)
|
|
784
|
+
elif isinstance(node, ast.Constant):
|
|
785
|
+
return str(node.value)
|
|
786
|
+
return ""
|
|
787
|
+
|
|
788
|
+
def get_decorators(node):
|
|
789
|
+
decs = []
|
|
790
|
+
for dec in node.decorator_list:
|
|
791
|
+
decs.append(get_name(dec))
|
|
792
|
+
return decs
|
|
793
|
+
|
|
794
|
+
def get_bases(node):
|
|
795
|
+
bases = []
|
|
796
|
+
for base in node.bases:
|
|
797
|
+
bases.append(get_name(base))
|
|
798
|
+
return bases
|
|
799
|
+
|
|
800
|
+
def get_args(args):
|
|
801
|
+
parts = []
|
|
802
|
+
for arg in args.args:
|
|
803
|
+
parts.append(arg.arg)
|
|
804
|
+
return ", ".join(parts)
|
|
805
|
+
|
|
806
|
+
def get_returns(node):
|
|
807
|
+
if node.returns is None:
|
|
808
|
+
return ""
|
|
809
|
+
return get_name(node.returns)
|
|
810
|
+
|
|
811
|
+
class Sym:
|
|
812
|
+
def __init__(self, name, kind, line, col, signature, scope):
|
|
813
|
+
self.name = name
|
|
814
|
+
self.kind = kind
|
|
815
|
+
self.line = line
|
|
816
|
+
self.col = col
|
|
817
|
+
self.signature = signature
|
|
818
|
+
self.scope = scope
|
|
819
|
+
def to_dict(self):
|
|
820
|
+
return {
|
|
821
|
+
"name": self.name,
|
|
822
|
+
"kind": self.kind,
|
|
823
|
+
"line": self.line,
|
|
824
|
+
"col": self.col,
|
|
825
|
+
"signature": self.signature,
|
|
826
|
+
"scope": self.scope,
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
def is_private(name):
|
|
830
|
+
return name.startswith("__") and not name.endswith("__")
|
|
831
|
+
|
|
832
|
+
def leaf_name(node):
|
|
833
|
+
# Declared name of the callee: \`join\` of \`os.path.join\`. Matches how the
|
|
834
|
+
# TypeScript and Go extractors record call refs, so resolution behaves the
|
|
835
|
+
# same across languages.
|
|
836
|
+
if isinstance(node, ast.Attribute):
|
|
837
|
+
return node.attr
|
|
838
|
+
if isinstance(node, ast.Name):
|
|
839
|
+
return node.id
|
|
840
|
+
return get_name(node).split(".")[-1]
|
|
841
|
+
|
|
842
|
+
syms = []
|
|
843
|
+
refs = []
|
|
844
|
+
errors = []
|
|
845
|
+
|
|
846
|
+
try:
|
|
847
|
+
source = sys.stdin.read()
|
|
848
|
+
tree = ast.parse(source, filename=sys.argv[1])
|
|
849
|
+
except Exception as e:
|
|
850
|
+
errors.append(str(e))
|
|
851
|
+
print(json.dumps({"symbols": [], "refs": []}))
|
|
852
|
+
sys.exit(0)
|
|
853
|
+
|
|
854
|
+
# Module-level scope
|
|
855
|
+
module_scope = os.path.basename(sys.argv[1])[:-3] # strip .py
|
|
856
|
+
|
|
857
|
+
class ModuleVisitor(ast.NodeVisitor):
|
|
858
|
+
def __init__(self):
|
|
859
|
+
self.scope_stack = [module_scope]
|
|
860
|
+
|
|
861
|
+
def visit_ClassDef(self, node):
|
|
862
|
+
bases = get_bases(node)
|
|
863
|
+
decs = get_decorators(node)
|
|
864
|
+
sig = "class " + node.name
|
|
865
|
+
if bases:
|
|
866
|
+
sig += "(" + ", ".join(bases) + ")"
|
|
867
|
+
sig += ": ..."
|
|
868
|
+
syms.append(Sym(
|
|
869
|
+
name=node.name,
|
|
870
|
+
kind="class",
|
|
871
|
+
line=node.lineno,
|
|
872
|
+
col=node.col_offset,
|
|
873
|
+
signature=sig,
|
|
874
|
+
scope=".".join(self.scope_stack) + "." + node.name,
|
|
875
|
+
))
|
|
876
|
+
self.scope_stack.append(node.name)
|
|
877
|
+
self.generic_visit(node)
|
|
878
|
+
self.scope_stack.pop()
|
|
879
|
+
|
|
880
|
+
def visit_FunctionDef(self, node):
|
|
881
|
+
decs = get_decorators(node)
|
|
882
|
+
args = get_args(node.args)
|
|
883
|
+
returns = get_returns(node)
|
|
884
|
+
is_async = isinstance(node, ast.AsyncFunctionDef)
|
|
885
|
+
|
|
886
|
+
kind = "function"
|
|
887
|
+
prefix = "def "
|
|
888
|
+
if decs:
|
|
889
|
+
for d in decs:
|
|
890
|
+
if d.endswith(".staticmethod"):
|
|
891
|
+
kind = "staticmethod"
|
|
892
|
+
elif d.endswith(".classmethod"):
|
|
893
|
+
kind = "classmethod"
|
|
894
|
+
elif d == "property":
|
|
895
|
+
kind = "property"
|
|
896
|
+
|
|
897
|
+
if is_async:
|
|
898
|
+
kind = "async_" + kind
|
|
899
|
+
|
|
900
|
+
sig = f"{prefix}{node.name}({args})"
|
|
901
|
+
if returns:
|
|
902
|
+
sig += f" -> {returns}"
|
|
903
|
+
scope = ".".join(self.scope_stack) + "." + node.name
|
|
904
|
+
|
|
905
|
+
syms.append(Sym(
|
|
906
|
+
name=node.name,
|
|
907
|
+
kind=kind,
|
|
908
|
+
line=node.lineno,
|
|
909
|
+
col=node.col_offset,
|
|
910
|
+
signature=sig,
|
|
911
|
+
scope=scope,
|
|
912
|
+
))
|
|
913
|
+
# Don't descend into function bodies to avoid local symbols
|
|
914
|
+
# self.generic_visit(node)
|
|
915
|
+
|
|
916
|
+
def visit_AsyncFunctionDef(self, node):
|
|
917
|
+
# Treat as function
|
|
918
|
+
self.visit_FunctionDef(node)
|
|
919
|
+
|
|
920
|
+
def visit_Assign(self, node):
|
|
921
|
+
for target in node.targets:
|
|
922
|
+
if isinstance(target, ast.Name):
|
|
923
|
+
name = target.id
|
|
924
|
+
if is_private(name):
|
|
925
|
+
continue
|
|
926
|
+
# Infer constness from UPPER_CASE naming
|
|
927
|
+
kind = "const" if name.isupper() else "var"
|
|
928
|
+
col = target.col_offset if hasattr(target, 'col_offset') else 0
|
|
929
|
+
syms.append(Sym(
|
|
930
|
+
name=name,
|
|
931
|
+
kind=kind,
|
|
932
|
+
line=node.lineno,
|
|
933
|
+
col=col,
|
|
934
|
+
signature=f"{name} = ...",
|
|
935
|
+
scope=".".join(self.scope_stack),
|
|
936
|
+
))
|
|
937
|
+
|
|
938
|
+
def visit_AnnAssign(self, node):
|
|
939
|
+
if isinstance(node.target, ast.Name):
|
|
940
|
+
name = node.target.id
|
|
941
|
+
if is_private(name):
|
|
942
|
+
return
|
|
943
|
+
kind = "const" if name.isupper() else "var"
|
|
944
|
+
col = node.target.col_offset if hasattr(node.target, 'col_offset') else 0
|
|
945
|
+
sig = f"{name}: {get_name(node.annotation)}"
|
|
946
|
+
if node.value:
|
|
947
|
+
sig += " = ..."
|
|
948
|
+
syms.append(Sym(
|
|
949
|
+
name=name,
|
|
950
|
+
kind=kind,
|
|
951
|
+
line=node.lineno,
|
|
952
|
+
col=col,
|
|
953
|
+
signature=sig,
|
|
954
|
+
scope=".".join(self.scope_stack),
|
|
955
|
+
))
|
|
956
|
+
|
|
957
|
+
def visit_Import(self, node):
|
|
958
|
+
for alias in node.names:
|
|
959
|
+
name = alias.asname or alias.name
|
|
960
|
+
syms.append(Sym(
|
|
961
|
+
name=name,
|
|
962
|
+
kind="import",
|
|
963
|
+
line=node.lineno,
|
|
964
|
+
col=node.col_offset,
|
|
965
|
+
signature=f"import {alias.name}",
|
|
966
|
+
scope=".".join(self.scope_stack),
|
|
967
|
+
))
|
|
968
|
+
|
|
969
|
+
def visit_ImportFrom(self, node):
|
|
970
|
+
module = node.module or ""
|
|
971
|
+
for alias in node.names:
|
|
972
|
+
name = alias.asname or alias.name
|
|
973
|
+
syms.append(Sym(
|
|
974
|
+
name=name,
|
|
975
|
+
kind="import",
|
|
976
|
+
line=node.lineno,
|
|
977
|
+
col=node.col_offset,
|
|
978
|
+
signature=f"from {module} import {alias.name}",
|
|
979
|
+
scope=".".join(self.scope_stack),
|
|
980
|
+
))
|
|
981
|
+
|
|
982
|
+
visitor = ModuleVisitor()
|
|
983
|
+
visitor.visit(tree)
|
|
984
|
+
|
|
985
|
+
# Refs need a separate full walk: ModuleVisitor deliberately does not descend
|
|
986
|
+
# into function bodies (it would index locals as symbols), but that is exactly
|
|
987
|
+
# where the calls are.
|
|
988
|
+
for node in ast.walk(tree):
|
|
989
|
+
if isinstance(node, ast.Call):
|
|
990
|
+
name = leaf_name(node.func)
|
|
991
|
+
if name:
|
|
992
|
+
refs.append({"toName": name, "callType": "call", "line": node.lineno})
|
|
993
|
+
elif isinstance(node, ast.Import):
|
|
994
|
+
for alias in node.names:
|
|
995
|
+
refs.append({
|
|
996
|
+
"toName": alias.name.split(".")[-1],
|
|
997
|
+
"callType": "import",
|
|
998
|
+
"line": node.lineno,
|
|
999
|
+
"module": alias.name,
|
|
1000
|
+
})
|
|
1001
|
+
elif isinstance(node, ast.ImportFrom):
|
|
1002
|
+
# PEP 328: node.level is the number of leading dots. Preserving them is
|
|
1003
|
+
# what lets the resolver walk up from the importing file's package \u2014
|
|
1004
|
+
# dropping them made \`from .foo import X\` indistinguishable from an
|
|
1005
|
+
# absolute \`foo\`.
|
|
1006
|
+
module = ("." * (node.level or 0)) + (node.module or "")
|
|
1007
|
+
for alias in node.names:
|
|
1008
|
+
refs.append({
|
|
1009
|
+
"toName": alias.name,
|
|
1010
|
+
"callType": "import",
|
|
1011
|
+
"line": node.lineno,
|
|
1012
|
+
"module": module,
|
|
1013
|
+
})
|
|
1014
|
+
elif isinstance(node, ast.ClassDef):
|
|
1015
|
+
for base in node.bases:
|
|
1016
|
+
name = leaf_name(base)
|
|
1017
|
+
if name:
|
|
1018
|
+
refs.append({"toName": name, "callType": "inherit", "line": node.lineno})
|
|
1019
|
+
|
|
1020
|
+
print(json.dumps({"symbols": [s.to_dict() for s in syms], "refs": refs}))
|
|
1021
|
+
`;
|
|
1022
|
+
_cachedScriptPath = null;
|
|
1023
|
+
}
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
// src/codebase-index/ts-parser.ts
|
|
1027
|
+
var ts_parser_exports = {};
|
|
1028
|
+
__export(ts_parser_exports, {
|
|
1029
|
+
detectLang: () => detectLang,
|
|
1030
|
+
parseSymbols: () => parseSymbols3
|
|
1031
|
+
});
|
|
1032
|
+
function loadTypescript() {
|
|
1033
|
+
tsLoad ??= import("@typescript/typescript6").then((m) => {
|
|
1034
|
+
ts = m.default ?? m;
|
|
1035
|
+
return ts;
|
|
1036
|
+
});
|
|
1037
|
+
return tsLoad;
|
|
1038
|
+
}
|
|
1039
|
+
function kindMap() {
|
|
1040
|
+
kindMapCache ??= {
|
|
1041
|
+
[ts.SyntaxKind.ClassDeclaration]: "class",
|
|
1042
|
+
[ts.SyntaxKind.InterfaceDeclaration]: "interface",
|
|
1043
|
+
[ts.SyntaxKind.EnumDeclaration]: "enum",
|
|
1044
|
+
[ts.SyntaxKind.TypeAliasDeclaration]: "type",
|
|
1045
|
+
[ts.SyntaxKind.FunctionDeclaration]: "function",
|
|
1046
|
+
[ts.SyntaxKind.MethodDeclaration]: "method",
|
|
1047
|
+
[ts.SyntaxKind.GetAccessor]: "property",
|
|
1048
|
+
[ts.SyntaxKind.SetAccessor]: "property",
|
|
1049
|
+
[ts.SyntaxKind.PropertyDeclaration]: "property",
|
|
1050
|
+
[ts.SyntaxKind.Parameter]: "parameter",
|
|
1051
|
+
[ts.SyntaxKind.NamespaceExportDeclaration]: "namespace"
|
|
1052
|
+
};
|
|
1053
|
+
return kindMapCache;
|
|
1054
|
+
}
|
|
1055
|
+
function kindOf(node) {
|
|
1056
|
+
if (ts.isVariableDeclaration(node)) {
|
|
1057
|
+
const parent = node.parent;
|
|
1058
|
+
if (ts.isVariableDeclarationList(parent)) {
|
|
1059
|
+
const flags = parent.flags;
|
|
1060
|
+
if (flags & ts.NodeFlags.Let) return "let";
|
|
1061
|
+
if (flags & ts.NodeFlags.Const) return "const";
|
|
1062
|
+
return "var";
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
if (ts.isModuleDeclaration(node)) return "namespace";
|
|
1066
|
+
return kindMap()[node.kind] ?? null;
|
|
1067
|
+
}
|
|
1068
|
+
function getSignature(printer, node, sourceFile) {
|
|
1069
|
+
const raw = printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
|
|
1070
|
+
return raw.replace(/\s+/g, " ").slice(0, 500);
|
|
1071
|
+
}
|
|
1072
|
+
function getJsDoc(node, sourceFile) {
|
|
1073
|
+
const fullText = sourceFile.getFullText();
|
|
1074
|
+
const nodePos = node.getFullStart();
|
|
1075
|
+
const comments = ts.getLeadingCommentRanges(fullText, nodePos);
|
|
1076
|
+
if (!comments) return "";
|
|
1077
|
+
for (const range of comments) {
|
|
1078
|
+
const commentText = fullText.slice(range.pos, range.end);
|
|
1079
|
+
const trimmed = commentText.trim();
|
|
1080
|
+
if (trimmed.startsWith("/**") && trimmed.endsWith("*/")) {
|
|
1081
|
+
const inner = trimmed.slice(3, -2).replace(/^[ \t]*\*[ ]?/gm, "").trim();
|
|
1082
|
+
return inner.split("\n")[0]?.trim().slice(0, 200) ?? "";
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
return "";
|
|
1086
|
+
}
|
|
1087
|
+
function pushScopeName(node, parts) {
|
|
1088
|
+
if (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isEnumDeclaration(node) || ts.isTypeAliasDeclaration(node)) {
|
|
1089
|
+
parts.push(node.name?.text ?? "Anon");
|
|
1090
|
+
} else if (ts.isMethodDeclaration(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node) || ts.isPropertyDeclaration(node) || ts.isFunctionDeclaration(node)) {
|
|
1091
|
+
if (node.name && ts.isIdentifier(node.name)) {
|
|
1092
|
+
parts.push(node.name.text);
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
async function parseSymbols3(opts) {
|
|
1097
|
+
const { file, content, lang } = opts;
|
|
1098
|
+
await loadTypescript();
|
|
1099
|
+
let sourceFile;
|
|
1100
|
+
try {
|
|
1101
|
+
sourceFile = ts.createSourceFile(file, content, ts.ScriptTarget.Latest, true);
|
|
1102
|
+
} catch {
|
|
1103
|
+
return { file, lang, symbols: [], mtimeMs: Date.now() };
|
|
1104
|
+
}
|
|
1105
|
+
const symbols = [];
|
|
1106
|
+
const refs = [];
|
|
1107
|
+
const printer = ts.createPrinter({});
|
|
1108
|
+
function visit(node, funcDepth, scopeParts) {
|
|
1109
|
+
const kind = kindOf(node);
|
|
1110
|
+
if (kind) {
|
|
1111
|
+
if ((kind === "const" || kind === "let" || kind === "var" || kind === "parameter") && funcDepth > 0) {
|
|
1112
|
+
} else {
|
|
1113
|
+
const nameNode = node.name;
|
|
1114
|
+
if (!nameNode || !ts.isIdentifier(nameNode)) {
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
1117
|
+
const name = nameNode.text;
|
|
1118
|
+
const pos2 = nameNode.getStart(sourceFile);
|
|
1119
|
+
const { line: line2, character } = sourceFile.getLineAndCharacterOfPosition(pos2);
|
|
1120
|
+
const scope = scopeParts.join(".");
|
|
1121
|
+
const signature = getSignature(printer, node, sourceFile);
|
|
1122
|
+
const docComment = getJsDoc(node, sourceFile);
|
|
1123
|
+
const text = [name, signature, docComment].filter(Boolean).join(" | ");
|
|
1124
|
+
symbols.push({
|
|
1125
|
+
id: 0,
|
|
1126
|
+
lang,
|
|
1127
|
+
kind,
|
|
1128
|
+
name,
|
|
1129
|
+
file,
|
|
1130
|
+
line: line2 + 1,
|
|
1131
|
+
col: character,
|
|
1132
|
+
signature,
|
|
1133
|
+
docComment,
|
|
1134
|
+
scope,
|
|
1135
|
+
text
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
const pos = node.getStart(sourceFile);
|
|
1140
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(pos);
|
|
1141
|
+
const lineNum = line + 1;
|
|
1142
|
+
if (ts.isCallExpression(node)) {
|
|
1143
|
+
const expr = node.expression;
|
|
1144
|
+
if (ts.isIdentifier(expr)) {
|
|
1145
|
+
refs.push({ fromId: 0, toName: expr.text, callType: "call", line: lineNum });
|
|
1146
|
+
}
|
|
1147
|
+
} else if (ts.isPropertyAccessExpression(node)) {
|
|
1148
|
+
if (ts.isIdentifier(node.expression)) {
|
|
1149
|
+
refs.push({ fromId: 0, toName: node.expression.text, callType: "call", line: lineNum });
|
|
1150
|
+
}
|
|
1151
|
+
} else if (ts.isTypeReferenceNode(node)) {
|
|
1152
|
+
const name = getTypeName(node.typeName);
|
|
1153
|
+
if (name) refs.push({ fromId: 0, toName: name, callType: "type_ref", line: lineNum });
|
|
1154
|
+
} else if (ts.isHeritageClause(node)) {
|
|
1155
|
+
for (const t of node.types) {
|
|
1156
|
+
const name = getTypeName(t.expression);
|
|
1157
|
+
if (name)
|
|
1158
|
+
refs.push({
|
|
1159
|
+
fromId: 0,
|
|
1160
|
+
toName: name,
|
|
1161
|
+
callType: node.token === ts.SyntaxKind.ExtendsKeyword ? "inherit" : "implement",
|
|
1162
|
+
line: lineNum
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
} else if (ts.isImportDeclaration(node)) {
|
|
1166
|
+
emitImportSpecifierRefs(node, refs, lineNum);
|
|
1167
|
+
} else if (ts.isExportDeclaration(node) && node.moduleSpecifier) {
|
|
1168
|
+
emitExportSpecifierRefs(node, refs, lineNum);
|
|
1169
|
+
}
|
|
1170
|
+
const scopeIdx = scopeParts.length;
|
|
1171
|
+
pushScopeName(node, scopeParts);
|
|
1172
|
+
const childFuncDepth = ts.isFunctionLike(node) ? funcDepth + 1 : funcDepth;
|
|
1173
|
+
ts.forEachChild(node, (child) => visit(child, childFuncDepth, scopeParts));
|
|
1174
|
+
scopeParts.length = scopeIdx;
|
|
1175
|
+
}
|
|
1176
|
+
visit(sourceFile, 0, []);
|
|
1177
|
+
return { file, lang, symbols, refs: deduplicateRefs(refs), mtimeMs: Date.now() };
|
|
1178
|
+
}
|
|
1179
|
+
function getTypeName(name) {
|
|
1180
|
+
if (ts.isIdentifier(name)) return name.text;
|
|
1181
|
+
if (ts.isQualifiedName(name)) return `${getTypeName(name.left)}.${name.right.text}`;
|
|
1182
|
+
return "";
|
|
1183
|
+
}
|
|
1184
|
+
function deduplicateRefs(refs) {
|
|
1185
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1186
|
+
return refs.filter((r) => {
|
|
1187
|
+
const key = `${r.toName}:${r.callType}:${r.line}:${r.module ?? ""}`;
|
|
1188
|
+
if (seen.has(key)) return false;
|
|
1189
|
+
seen.add(key);
|
|
1190
|
+
return true;
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
1193
|
+
function getImportSpecifierName(spec) {
|
|
1194
|
+
return spec.propertyName?.text ?? spec.name.text;
|
|
1195
|
+
}
|
|
1196
|
+
function emitImportSpecifierRefs(node, refs, lineNum) {
|
|
1197
|
+
const module = moduleSpecifierOf(node.moduleSpecifier);
|
|
1198
|
+
const clause = node.importClause;
|
|
1199
|
+
if (!clause) {
|
|
1200
|
+
if (module) {
|
|
1201
|
+
refs.push({ fromId: 0, toName: module, callType: "import", line: lineNum, module });
|
|
1202
|
+
}
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
if (clause.name) {
|
|
1206
|
+
refs.push({ fromId: 0, toName: clause.name.text, callType: "import", line: lineNum, module });
|
|
1207
|
+
}
|
|
1208
|
+
const bindings = clause.namedBindings;
|
|
1209
|
+
if (!bindings) return;
|
|
1210
|
+
if (ts.isNamedImports(bindings)) {
|
|
1211
|
+
for (const element of bindings.elements) {
|
|
1212
|
+
refs.push({
|
|
1213
|
+
fromId: 0,
|
|
1214
|
+
toName: getImportSpecifierName(element),
|
|
1215
|
+
callType: "import",
|
|
1216
|
+
line: lineNum,
|
|
1217
|
+
module
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
} else if (ts.isNamespaceImport(bindings)) {
|
|
1221
|
+
refs.push({
|
|
1222
|
+
fromId: 0,
|
|
1223
|
+
toName: bindings.name.text,
|
|
1224
|
+
callType: "import",
|
|
1225
|
+
line: lineNum,
|
|
1226
|
+
module
|
|
1227
|
+
});
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
function moduleSpecifierOf(node) {
|
|
1231
|
+
return node && ts.isStringLiteral(node) ? node.text : void 0;
|
|
1232
|
+
}
|
|
1233
|
+
function emitExportSpecifierRefs(node, refs, lineNum) {
|
|
1234
|
+
const module = moduleSpecifierOf(node.moduleSpecifier);
|
|
1235
|
+
const clause = node.exportClause;
|
|
1236
|
+
if (clause && ts.isNamespaceExport(clause)) {
|
|
1237
|
+
refs.push({ fromId: 0, toName: clause.name.text, callType: "import", line: lineNum, module });
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
if (clause && ts.isNamedExports(clause)) {
|
|
1241
|
+
for (const element of clause.elements) {
|
|
1242
|
+
const originalName = element.propertyName?.text ?? element.name.text;
|
|
1243
|
+
refs.push({ fromId: 0, toName: originalName, callType: "import", line: lineNum, module });
|
|
1244
|
+
}
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
if (module) {
|
|
1248
|
+
refs.push({ fromId: 0, toName: module, callType: "import", line: lineNum, module });
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
var ts, tsLoad, kindMapCache;
|
|
1252
|
+
var init_ts_parser = __esm({
|
|
1253
|
+
"src/codebase-index/ts-parser.ts"() {
|
|
1254
|
+
"use strict";
|
|
1255
|
+
init_languages();
|
|
1256
|
+
tsLoad = null;
|
|
1257
|
+
kindMapCache = null;
|
|
1258
|
+
}
|
|
1259
|
+
});
|
|
1260
|
+
|
|
1261
|
+
// src/codebase-index/go-parser.ts
|
|
1262
|
+
var go_parser_exports = {};
|
|
1263
|
+
__export(go_parser_exports, {
|
|
1264
|
+
detectLang: () => detectLang,
|
|
1265
|
+
parseSymbols: () => parseSymbols4
|
|
1266
|
+
});
|
|
1267
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
1268
|
+
import * as os3 from "node:os";
|
|
1269
|
+
import * as path5 from "node:path";
|
|
1270
|
+
import * as fs4 from "node:fs/promises";
|
|
1271
|
+
async function parseSymbols4(opts) {
|
|
1272
|
+
const { file, content, lang } = opts;
|
|
1273
|
+
try {
|
|
1274
|
+
const parsed = await withSpawnGate(() => syncGoParse(file, content, lang));
|
|
1275
|
+
if (parsed.symbols.length > 0) {
|
|
1276
|
+
return parsed;
|
|
1277
|
+
}
|
|
1278
|
+
const fallback = fallbackParse(file, content, lang);
|
|
1279
|
+
return parsed.refs?.length ? { ...fallback, refs: parsed.refs } : fallback;
|
|
1280
|
+
} catch {
|
|
1281
|
+
return fallbackParse(file, content, lang);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
function fallbackParse(filePath, content, lang) {
|
|
1285
|
+
if (!/^\s*package\s+[A-Za-z_]\w*/m.test(content) || hasUnbalancedDelimiters(content)) {
|
|
1286
|
+
return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
|
|
1287
|
+
}
|
|
1288
|
+
const symbols = [];
|
|
1289
|
+
const packageName = content.match(/^\s*package\s+([A-Za-z_]\w*)/m)?.[1] ?? "";
|
|
1290
|
+
const lines = content.split(/\r?\n/);
|
|
1291
|
+
for (const [idx, line] of lines.entries()) {
|
|
1292
|
+
const trimmed = line.trimStart();
|
|
1293
|
+
const col = line.length - trimmed.length + 1;
|
|
1294
|
+
const fn = /^func\s+(?:\([^)]*\)\s*)?([A-Za-z_]\w*)\s*\(/.exec(trimmed);
|
|
1295
|
+
if (fn?.[1]) {
|
|
1296
|
+
addFallbackSymbol(symbols, {
|
|
1297
|
+
filePath,
|
|
1298
|
+
lang,
|
|
1299
|
+
kind: trimmed.startsWith("func (") ? "method" : "function",
|
|
1300
|
+
name: fn[1],
|
|
1301
|
+
line: idx + 1,
|
|
1302
|
+
col,
|
|
1303
|
+
signature: trimmed,
|
|
1304
|
+
scope: packageName ? `${packageName}.${fn[1]}` : fn[1]
|
|
1305
|
+
});
|
|
1306
|
+
continue;
|
|
1307
|
+
}
|
|
1308
|
+
const typeDecl = /^type\s+([A-Za-z_]\w*)\b/.exec(trimmed);
|
|
1309
|
+
if (typeDecl?.[1]) {
|
|
1310
|
+
addFallbackSymbol(symbols, {
|
|
1311
|
+
filePath,
|
|
1312
|
+
lang,
|
|
1313
|
+
kind: "type",
|
|
1314
|
+
name: typeDecl[1],
|
|
1315
|
+
line: idx + 1,
|
|
1316
|
+
col,
|
|
1317
|
+
signature: trimmed,
|
|
1318
|
+
scope: packageName
|
|
1319
|
+
});
|
|
1320
|
+
continue;
|
|
1321
|
+
}
|
|
1322
|
+
const valueDecl = /^(const|var)\s+([A-Za-z_]\w*)\b/.exec(trimmed);
|
|
1323
|
+
if (valueDecl?.[1] && valueDecl[2]) {
|
|
1324
|
+
addFallbackSymbol(symbols, {
|
|
1325
|
+
filePath,
|
|
1326
|
+
lang,
|
|
1327
|
+
kind: valueDecl[1],
|
|
1328
|
+
name: valueDecl[2],
|
|
1329
|
+
line: idx + 1,
|
|
1330
|
+
col,
|
|
1331
|
+
signature: trimmed,
|
|
1332
|
+
scope: packageName
|
|
1333
|
+
});
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
return { file: filePath, lang, symbols, mtimeMs: Date.now() };
|
|
1337
|
+
}
|
|
1338
|
+
function addFallbackSymbol(symbols, opts) {
|
|
1339
|
+
symbols.push({
|
|
1340
|
+
id: 0,
|
|
1341
|
+
lang: opts.lang,
|
|
1342
|
+
kind: opts.kind,
|
|
1343
|
+
name: opts.name,
|
|
1344
|
+
file: opts.filePath,
|
|
1345
|
+
line: opts.line,
|
|
1346
|
+
col: opts.col,
|
|
1347
|
+
signature: opts.signature,
|
|
1348
|
+
docComment: "",
|
|
1349
|
+
scope: opts.scope,
|
|
1350
|
+
text: `${opts.name} ${opts.signature}`.trim()
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
function hasUnbalancedDelimiters(content) {
|
|
1354
|
+
const pairs = { "(": ")", "[": "]", "{": "}" };
|
|
1355
|
+
const closers = new Set(Object.values(pairs));
|
|
1356
|
+
const stack = [];
|
|
1357
|
+
for (const ch of content) {
|
|
1358
|
+
if (pairs[ch]) {
|
|
1359
|
+
stack.push(pairs[ch]);
|
|
1360
|
+
} else if (closers.has(ch) && stack.pop() !== ch) {
|
|
1361
|
+
return true;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
return stack.length > 0;
|
|
1365
|
+
}
|
|
1366
|
+
async function syncGoParse(filePath, content, lang) {
|
|
1367
|
+
try {
|
|
1368
|
+
let scriptPath = _cachedGoScriptPath;
|
|
1369
|
+
if (!scriptPath) {
|
|
1370
|
+
const tmpDir = await fs4.mkdtemp(path5.join(os3.tmpdir(), "ws-go-parse-"));
|
|
1371
|
+
scriptPath = path5.join(tmpDir, "parse.go");
|
|
1372
|
+
await fs4.writeFile(scriptPath, GO_PARSE_SCRIPT, "utf8");
|
|
1373
|
+
_cachedGoScriptPath = scriptPath;
|
|
1374
|
+
}
|
|
1375
|
+
const goBinary = resolveWin32Command("go");
|
|
1376
|
+
const goResult = await new Promise(
|
|
1377
|
+
(resolve, reject) => {
|
|
1378
|
+
let settled = false;
|
|
1379
|
+
const proc = spawn3(goBinary, ["run", scriptPath], {
|
|
1380
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1381
|
+
windowsHide: true
|
|
1382
|
+
});
|
|
1383
|
+
proc.on("error", (err) => {
|
|
1384
|
+
if (settled) return;
|
|
1385
|
+
settled = true;
|
|
1386
|
+
reject(err);
|
|
1387
|
+
});
|
|
1388
|
+
let stdout2 = "";
|
|
1389
|
+
proc.stdout?.on("data", (chunk) => {
|
|
1390
|
+
stdout2 += chunk.toString();
|
|
1391
|
+
});
|
|
1392
|
+
proc.stderr?.resume();
|
|
1393
|
+
proc.stdin?.write(content);
|
|
1394
|
+
proc.stdin?.end();
|
|
1395
|
+
const timer = setTimeout(() => {
|
|
1396
|
+
if (settled) return;
|
|
1397
|
+
settled = true;
|
|
1398
|
+
proc.kill("SIGKILL");
|
|
1399
|
+
reject(new Error("timeout"));
|
|
1400
|
+
}, 15e3);
|
|
1401
|
+
timer.unref?.();
|
|
1402
|
+
proc.on("close", (code2) => {
|
|
1403
|
+
if (settled) return;
|
|
1404
|
+
settled = true;
|
|
1405
|
+
clearTimeout(timer);
|
|
1406
|
+
resolve({ code: code2, stdout: stdout2 });
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
);
|
|
1410
|
+
const { code, stdout } = goResult;
|
|
1411
|
+
if (code !== 0 || !stdout.trim()) {
|
|
1412
|
+
return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
|
|
1413
|
+
}
|
|
1414
|
+
const { symbols: rawSymbols, refs } = parseParserOutput(stdout, lang);
|
|
1415
|
+
const symbols = rawSymbols.map((s) => ({
|
|
1416
|
+
id: 0,
|
|
1417
|
+
lang,
|
|
1418
|
+
kind: s.kind,
|
|
1419
|
+
name: s.name,
|
|
1420
|
+
file: filePath,
|
|
1421
|
+
line: s.line,
|
|
1422
|
+
col: s.col,
|
|
1423
|
+
signature: s.signature ?? "",
|
|
1424
|
+
docComment: "",
|
|
1425
|
+
scope: s.scope ?? "",
|
|
1426
|
+
text: `${s.name} ${s.signature ?? ""}`.trim()
|
|
1427
|
+
}));
|
|
1428
|
+
return { file: filePath, lang, symbols, refs, mtimeMs: Date.now() };
|
|
1429
|
+
} catch {
|
|
1430
|
+
return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
var GO_PARSE_SCRIPT, _cachedGoScriptPath;
|
|
1434
|
+
var init_go_parser = __esm({
|
|
1435
|
+
"src/codebase-index/go-parser.ts"() {
|
|
1436
|
+
"use strict";
|
|
1437
|
+
init_win32_resolve();
|
|
1438
|
+
init_parser_output();
|
|
1439
|
+
init_spawn_gate();
|
|
1440
|
+
init_languages();
|
|
1441
|
+
GO_PARSE_SCRIPT = `
|
|
1442
|
+
package main
|
|
1443
|
+
|
|
1444
|
+
import (
|
|
1445
|
+
"encoding/json"
|
|
1446
|
+
"fmt"
|
|
1447
|
+
"go/ast"
|
|
1448
|
+
"go/parser"
|
|
1449
|
+
"go/token"
|
|
1450
|
+
"io"
|
|
1451
|
+
"os"
|
|
1452
|
+
"strconv"
|
|
1453
|
+
"strings"
|
|
1454
|
+
)
|
|
1455
|
+
|
|
1456
|
+
type Sym struct {
|
|
1457
|
+
Name string \`json:"name"\`
|
|
1458
|
+
Kind string \`json:"kind"\`
|
|
1459
|
+
Line int \`json:"line"\`
|
|
1460
|
+
Col int \`json:"col"\`
|
|
1461
|
+
Signature string \`json:"signature"\`
|
|
1462
|
+
Scope string \`json:"scope"\`
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
// Ref is a cross-reference emitted alongside the symbols, so one \`go run\`
|
|
1466
|
+
// yields both. Module is the import path for CallType "import", else empty.
|
|
1467
|
+
type Ref struct {
|
|
1468
|
+
ToName string \`json:"toName"\`
|
|
1469
|
+
CallType string \`json:"callType"\`
|
|
1470
|
+
Line int \`json:"line"\`
|
|
1471
|
+
Module string \`json:"module"\`
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
type Result struct {
|
|
1475
|
+
Symbols []Sym \`json:"symbols"\`
|
|
1476
|
+
Refs []Ref \`json:"refs"\`
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
func emptyResult() string {
|
|
1480
|
+
return "{\\"symbols\\":[],\\"refs\\":[]}"
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
func main() {
|
|
1484
|
+
src, err := io.ReadAll(os.Stdin)
|
|
1485
|
+
if err != nil {
|
|
1486
|
+
fmt.Print(emptyResult())
|
|
1487
|
+
return
|
|
1488
|
+
}
|
|
1489
|
+
fset := token.NewFileSet()
|
|
1490
|
+
node, err := parser.ParseFile(fset, "src.go", src, 0)
|
|
1491
|
+
if err != nil {
|
|
1492
|
+
fmt.Print(emptyResult())
|
|
1493
|
+
return
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
var syms []Sym
|
|
1497
|
+
|
|
1498
|
+
// Package-level scope
|
|
1499
|
+
pkgScope := node.Name.Name
|
|
1500
|
+
|
|
1501
|
+
// Collect all top-level declarations
|
|
1502
|
+
for _, decl := range node.Decls {
|
|
1503
|
+
switch d := decl.(type) {
|
|
1504
|
+
case *ast.FuncDecl:
|
|
1505
|
+
name := d.Name.Name
|
|
1506
|
+
kind := "function"
|
|
1507
|
+
scope := pkgScope
|
|
1508
|
+
if d.Recv != nil && len(d.Recv.List) > 0 {
|
|
1509
|
+
scope = pkgScope + "." + recvTypeName(d.Recv.List[0].Type) + "." + name
|
|
1510
|
+
kind = "method"
|
|
1511
|
+
} else {
|
|
1512
|
+
scope = pkgScope + "." + name
|
|
1513
|
+
}
|
|
1514
|
+
pos := fset.Position(d.Pos())
|
|
1515
|
+
sig := formatFuncSig(d)
|
|
1516
|
+
syms = append(syms, Sym{Name: name, Kind: kind, Line: pos.Line, Col: pos.Column, Signature: sig, Scope: scope})
|
|
1517
|
+
|
|
1518
|
+
case *ast.GenDecl:
|
|
1519
|
+
for _, spec := range d.Specs {
|
|
1520
|
+
switch s := spec.(type) {
|
|
1521
|
+
case *ast.TypeSpec:
|
|
1522
|
+
name := s.Name.Name
|
|
1523
|
+
pos := fset.Position(s.Pos())
|
|
1524
|
+
sig := "type " + name
|
|
1525
|
+
if s.TypeParams != nil {
|
|
1526
|
+
sig += formatTypeParams(s.TypeParams)
|
|
1527
|
+
}
|
|
1528
|
+
if st, ok := s.Type.(*ast.StructType); ok {
|
|
1529
|
+
sig += " = struct { " + formatFields(st.Fields.List) + " }"
|
|
1530
|
+
} else if it, ok := s.Type.(*ast.InterfaceType); ok {
|
|
1531
|
+
sig += " = interface { " + formatMethods(it.Methods.List) + " }"
|
|
1532
|
+
} else {
|
|
1533
|
+
sig += " = " + formatType(s.Type)
|
|
1534
|
+
}
|
|
1535
|
+
syms = append(syms, Sym{Name: name, Kind: "type", Line: pos.Line, Col: pos.Column, Signature: sig, Scope: pkgScope})
|
|
1536
|
+
|
|
1537
|
+
case *ast.ValueSpec:
|
|
1538
|
+
for _, n := range s.Names {
|
|
1539
|
+
name := n.Name
|
|
1540
|
+
pos := fset.Position(n.Pos())
|
|
1541
|
+
kind := "var"
|
|
1542
|
+
if d.Tok == token.CONST {
|
|
1543
|
+
kind = "const"
|
|
1544
|
+
}
|
|
1545
|
+
sig := kind + " " + name
|
|
1546
|
+
if s.Type != nil {
|
|
1547
|
+
sig += " " + formatType(s.Type)
|
|
1548
|
+
}
|
|
1549
|
+
syms = append(syms, Sym{Name: name, Kind: kind, Line: pos.Line, Col: pos.Column, Signature: sig, Scope: pkgScope})
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
refs := []Ref{}
|
|
1557
|
+
ast.Inspect(node, func(n ast.Node) bool {
|
|
1558
|
+
switch expr := n.(type) {
|
|
1559
|
+
case *ast.CallExpr:
|
|
1560
|
+
line := fset.Position(expr.Pos()).Line
|
|
1561
|
+
switch fun := expr.Fun.(type) {
|
|
1562
|
+
case *ast.Ident:
|
|
1563
|
+
refs = append(refs, Ref{ToName: fun.Name, CallType: "call", Line: line})
|
|
1564
|
+
case *ast.SelectorExpr:
|
|
1565
|
+
// Record the selected name (\`Join\` of \`filepath.Join\`): it is the
|
|
1566
|
+
// declared symbol name, so it resolves the same way the TypeScript
|
|
1567
|
+
// and Python extractors' call refs do.
|
|
1568
|
+
refs = append(refs, Ref{ToName: fun.Sel.Name, CallType: "call", Line: line})
|
|
1569
|
+
}
|
|
1570
|
+
case *ast.ImportSpec:
|
|
1571
|
+
if expr.Path != nil {
|
|
1572
|
+
if importPath, uerr := strconv.Unquote(expr.Path.Value); uerr == nil {
|
|
1573
|
+
line := fset.Position(expr.Pos()).Line
|
|
1574
|
+
// A Go import names a package, not a symbol; the package's
|
|
1575
|
+
// last path segment is the name it is referenced by.
|
|
1576
|
+
name := importPath
|
|
1577
|
+
if idx := strings.LastIndex(importPath, "/"); idx >= 0 {
|
|
1578
|
+
name = importPath[idx+1:]
|
|
1579
|
+
}
|
|
1580
|
+
refs = append(refs, Ref{ToName: name, CallType: "import", Line: line, Module: importPath})
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
return true
|
|
1585
|
+
})
|
|
1586
|
+
|
|
1587
|
+
if syms == nil {
|
|
1588
|
+
syms = []Sym{}
|
|
1589
|
+
}
|
|
1590
|
+
data, err := json.Marshal(Result{Symbols: syms, Refs: refs})
|
|
1591
|
+
if err != nil {
|
|
1592
|
+
fmt.Print(emptyResult())
|
|
1593
|
+
return
|
|
1594
|
+
}
|
|
1595
|
+
fmt.Print(string(data))
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
func recvTypeName(t ast.Expr) string {
|
|
1599
|
+
switch v := t.(type) {
|
|
1600
|
+
case *ast.Ident:
|
|
1601
|
+
return v.Name
|
|
1602
|
+
case *ast.StarExpr:
|
|
1603
|
+
return recvTypeName(v.X)
|
|
1604
|
+
default:
|
|
1605
|
+
return "?"
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
func formatFuncSig(d *ast.FuncDecl) string {
|
|
1610
|
+
scope := ""
|
|
1611
|
+
if d.Recv != nil && len(d.Recv.List) > 0 {
|
|
1612
|
+
scope = "(" + formatFieldList(d.Recv.List) + ") "
|
|
1613
|
+
}
|
|
1614
|
+
scope += formatFuncType(d.Type)
|
|
1615
|
+
return "func " + scope
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
func formatFuncType(f *ast.FuncType) string {
|
|
1619
|
+
params := formatFieldList(f.Params.List)
|
|
1620
|
+
results := ""
|
|
1621
|
+
if f.Results != nil {
|
|
1622
|
+
results = " -> " + formatFieldList(f.Results.List)
|
|
1623
|
+
}
|
|
1624
|
+
return params + results
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
func formatFieldList(fields []*ast.Field) string {
|
|
1628
|
+
if len(fields) == 0 {
|
|
1629
|
+
return "()"
|
|
1630
|
+
}
|
|
1631
|
+
names := make([]string, 0, len(fields))
|
|
1632
|
+
for _, f := range fields {
|
|
1633
|
+
name := ""
|
|
1634
|
+
if len(f.Names) > 0 {
|
|
1635
|
+
name = f.Names[0].Name
|
|
1636
|
+
}
|
|
1637
|
+
t := formatType(f.Type)
|
|
1638
|
+
if name != "" {
|
|
1639
|
+
names = append(names, name+" "+t)
|
|
1640
|
+
} else {
|
|
1641
|
+
names = append(names, t)
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
return "(" + strings.Join(names, ", ") + ")"
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
func formatFields(fields []*ast.Field) string {
|
|
1648
|
+
lines := make([]string, 0)
|
|
1649
|
+
for _, f := range fields {
|
|
1650
|
+
name := ""
|
|
1651
|
+
if len(f.Names) > 0 {
|
|
1652
|
+
name = f.Names[0].Name
|
|
1653
|
+
}
|
|
1654
|
+
t := formatType(f.Type)
|
|
1655
|
+
if name != "" {
|
|
1656
|
+
lines = append(lines, name+" "+t)
|
|
1657
|
+
} else {
|
|
1658
|
+
lines = append(lines, t)
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
return strings.Join(lines, "; ")
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
func formatMethods(fields []*ast.Field) string {
|
|
1665
|
+
return formatFields(fields)
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
func formatTypeParams(tp *ast.FieldList) string {
|
|
1669
|
+
if tp == nil || len(tp.List) == 0 {
|
|
1670
|
+
return ""
|
|
1671
|
+
}
|
|
1672
|
+
params := make([]string, len(tp.List))
|
|
1673
|
+
for i, p := range tp.List {
|
|
1674
|
+
if len(p.Names) > 0 {
|
|
1675
|
+
params[i] = p.Names[0].Name
|
|
1676
|
+
} else {
|
|
1677
|
+
params[i] = "T"
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
return "[" + strings.Join(params, ", ") + "]"
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
func formatType(t ast.Expr) string {
|
|
1684
|
+
if t == nil {
|
|
1685
|
+
return "?"
|
|
1686
|
+
}
|
|
1687
|
+
switch v := t.(type) {
|
|
1688
|
+
case *ast.Ident:
|
|
1689
|
+
return v.Name
|
|
1690
|
+
case *ast.SelectorExpr:
|
|
1691
|
+
return formatType(v.X) + "." + v.Sel.Name
|
|
1692
|
+
case *ast.StarExpr:
|
|
1693
|
+
return "*" + formatType(v.X)
|
|
1694
|
+
case *ast.ArrayType:
|
|
1695
|
+
if v.Len == nil {
|
|
1696
|
+
return "[]" + formatType(v.Elt)
|
|
1697
|
+
}
|
|
1698
|
+
return "[...]" + formatType(v.Elt)
|
|
1699
|
+
case *ast.MapType:
|
|
1700
|
+
return "map[" + formatType(v.Key) + "]" + formatType(v.Value)
|
|
1701
|
+
case *ast.InterfaceType:
|
|
1702
|
+
return "interface{}"
|
|
1703
|
+
case *ast.StructType:
|
|
1704
|
+
return "struct{}"
|
|
1705
|
+
case *ast.FuncType:
|
|
1706
|
+
return formatFuncType(v)
|
|
1707
|
+
case *ast.ChanType:
|
|
1708
|
+
return "chan " + formatType(v.Value)
|
|
1709
|
+
case *ast.BasicLit:
|
|
1710
|
+
return v.Value
|
|
1711
|
+
case *ast.IndexExpr:
|
|
1712
|
+
// Generic instantiation with one type arg, e.g. Logger[int].
|
|
1713
|
+
return formatType(v.X) + "[" + formatType(v.Index) + "]"
|
|
1714
|
+
case *ast.IndexListExpr:
|
|
1715
|
+
// Generic instantiation with multiple type args, e.g. Map[K, V].
|
|
1716
|
+
args := make([]string, len(v.Indices))
|
|
1717
|
+
for i, idx := range v.Indices {
|
|
1718
|
+
args[i] = formatType(idx)
|
|
1719
|
+
}
|
|
1720
|
+
return formatType(v.X) + "[" + strings.Join(args, ", ") + "]"
|
|
1721
|
+
default:
|
|
1722
|
+
return "?"
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
`;
|
|
1726
|
+
_cachedGoScriptPath = null;
|
|
1727
|
+
}
|
|
1728
|
+
});
|
|
1729
|
+
|
|
1730
|
+
// src/codebase-index/rs-parser.ts
|
|
1731
|
+
var rs_parser_exports = {};
|
|
1732
|
+
__export(rs_parser_exports, {
|
|
1733
|
+
detectLang: () => detectLang,
|
|
1734
|
+
parseSymbols: () => parseSymbols5
|
|
1735
|
+
});
|
|
1736
|
+
import { expectDefined } from "@wrongstack/core/utils";
|
|
1737
|
+
async function parseSymbols5(opts) {
|
|
1738
|
+
const { file, content, lang } = opts;
|
|
1739
|
+
return regexParse({ file, content, lang });
|
|
1740
|
+
}
|
|
1741
|
+
function regexParse(opts) {
|
|
1742
|
+
const { file, content, lang } = opts;
|
|
1743
|
+
const symbols = [];
|
|
1744
|
+
const lines = content.split("\n");
|
|
1745
|
+
const lineOffsets = [0];
|
|
1746
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1747
|
+
lineOffsets.push((lineOffsets[i] ?? 0) + (lines[i]?.length ?? 0) + 1);
|
|
1748
|
+
}
|
|
1749
|
+
function lineFromOffset(offset) {
|
|
1750
|
+
let lo = 0;
|
|
1751
|
+
let hi = lineOffsets.length - 1;
|
|
1752
|
+
while (lo < hi) {
|
|
1753
|
+
const mid = lo + hi + 1 >>> 1;
|
|
1754
|
+
if (expectDefined(lineOffsets[mid]) <= offset) lo = mid;
|
|
1755
|
+
else hi = mid - 1;
|
|
1756
|
+
}
|
|
1757
|
+
return lo + 1;
|
|
1758
|
+
}
|
|
1759
|
+
function extractDeclaration(lineIdx, _match) {
|
|
1760
|
+
const line = lines[lineIdx] ?? "";
|
|
1761
|
+
return line.trim().slice(0, 500);
|
|
1762
|
+
}
|
|
1763
|
+
for (const pattern of RS_PATTERNS) {
|
|
1764
|
+
pattern.regex.lastIndex = 0;
|
|
1765
|
+
for (let match = pattern.regex.exec(content); match !== null; match = pattern.regex.exec(content)) {
|
|
1766
|
+
const name = expectDefined(match[1]);
|
|
1767
|
+
const offset = match.index ?? 0;
|
|
1768
|
+
const line = lineFromOffset(offset);
|
|
1769
|
+
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
1770
|
+
const lineIdx = line - 1;
|
|
1771
|
+
const signature = extractDeclaration(lineIdx, match);
|
|
1772
|
+
symbols.push({
|
|
1773
|
+
id: 0,
|
|
1774
|
+
lang,
|
|
1775
|
+
kind: pattern.kind,
|
|
1776
|
+
name,
|
|
1777
|
+
file,
|
|
1778
|
+
line,
|
|
1779
|
+
col,
|
|
1780
|
+
signature,
|
|
1781
|
+
docComment: "",
|
|
1782
|
+
scope: "",
|
|
1783
|
+
text: `${name} ${signature}`.trim()
|
|
1784
|
+
});
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1788
|
+
const deduped = symbols.filter((s) => {
|
|
1789
|
+
const key = `${s.name}:${s.line}`;
|
|
1790
|
+
if (seen.has(key)) return false;
|
|
1791
|
+
seen.add(key);
|
|
1792
|
+
return true;
|
|
1793
|
+
});
|
|
1794
|
+
return { file, lang, symbols: deduped, mtimeMs: Date.now() };
|
|
1795
|
+
}
|
|
1796
|
+
var RS_PATTERNS;
|
|
1797
|
+
var init_rs_parser = __esm({
|
|
1798
|
+
"src/codebase-index/rs-parser.ts"() {
|
|
1799
|
+
"use strict";
|
|
1800
|
+
init_languages();
|
|
1801
|
+
RS_PATTERNS = [
|
|
1802
|
+
{ regex: /fn\s+(\w+)\s*\([^)]*\)/g, kind: "function" },
|
|
1803
|
+
{ regex: /struct\s+(\w+)/g, kind: "struct" },
|
|
1804
|
+
{ regex: /enum\s+(\w+)/g, kind: "enum" },
|
|
1805
|
+
{ regex: /trait\s+(\w+)/g, kind: "trait" },
|
|
1806
|
+
{ regex: /impl\s+(?:<[^>]+>)?(\w+)/g, kind: "impl" },
|
|
1807
|
+
{ regex: /type\s+(\w+)\s*=/g, kind: "type" },
|
|
1808
|
+
{ regex: /const\s+(\w+)/g, kind: "const" },
|
|
1809
|
+
{ regex: /static\s+(\w+)/g, kind: "static" },
|
|
1810
|
+
{ regex: /mod\s+(\w+)/g, kind: "mod" }
|
|
1811
|
+
];
|
|
1812
|
+
}
|
|
1813
|
+
});
|
|
1814
|
+
|
|
1815
|
+
// src/codebase-index/json-parser.ts
|
|
1816
|
+
var json_parser_exports = {};
|
|
1817
|
+
__export(json_parser_exports, {
|
|
1818
|
+
detectLang: () => detectLang,
|
|
1819
|
+
parseSymbols: () => parseSymbols6
|
|
1820
|
+
});
|
|
1821
|
+
import { expectDefined as expectDefined2 } from "@wrongstack/core/utils";
|
|
1822
|
+
import * as path6 from "node:path";
|
|
1823
|
+
function parseSymbols6(opts) {
|
|
1824
|
+
const { file, content, lang } = opts;
|
|
1825
|
+
try {
|
|
1826
|
+
return regexParse2({ file, content, lang });
|
|
1827
|
+
} catch {
|
|
1828
|
+
return { file, lang, symbols: [], mtimeMs: Date.now() };
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
function regexParse2(opts) {
|
|
1832
|
+
const { file, content, lang } = opts;
|
|
1833
|
+
const symbols = [];
|
|
1834
|
+
const basename3 = path6.basename(file).toLowerCase();
|
|
1835
|
+
const isPackageJson = basename3 === "package.json";
|
|
1836
|
+
const isTsconfig = basename3 === "tsconfig.json" || basename3 === "tsconfig.build.json";
|
|
1837
|
+
const isJsonSchema = content.includes("$schema") || content.includes("$id") || content.includes("$ref");
|
|
1838
|
+
const isOpenApi = content.includes("openapi") || content.includes("swagger");
|
|
1839
|
+
const lines = content.split("\n");
|
|
1840
|
+
const lineOffsets = [0];
|
|
1841
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1842
|
+
lineOffsets.push((lineOffsets[i] ?? 0) + (lines[i]?.length ?? 0) + 1);
|
|
1843
|
+
}
|
|
1844
|
+
function lineFromOffset(offset) {
|
|
1845
|
+
let lo = 0;
|
|
1846
|
+
let hi = lineOffsets.length - 1;
|
|
1847
|
+
while (lo < hi) {
|
|
1848
|
+
const mid = lo + hi + 1 >>> 1;
|
|
1849
|
+
if (expectDefined2(lineOffsets[mid]) <= offset) lo = mid;
|
|
1850
|
+
else hi = mid - 1;
|
|
1851
|
+
}
|
|
1852
|
+
return lo + 1;
|
|
1853
|
+
}
|
|
1854
|
+
const rootMatch = content.match(/^\s*\{/m);
|
|
1855
|
+
if (rootMatch) {
|
|
1856
|
+
const offset = expectDefined2(rootMatch.index);
|
|
1857
|
+
const line = lineFromOffset(offset);
|
|
1858
|
+
symbols.push(
|
|
1859
|
+
makeSymbol({
|
|
1860
|
+
name: path6.basename(file),
|
|
1861
|
+
kind: "object",
|
|
1862
|
+
line,
|
|
1863
|
+
col: 0,
|
|
1864
|
+
signature: `"${path6.basename(file)}" = { ... }`,
|
|
1865
|
+
file,
|
|
1866
|
+
lang
|
|
1867
|
+
})
|
|
1868
|
+
);
|
|
1869
|
+
}
|
|
1870
|
+
const topLevelKeyRegex = /^\s*"([^"]+)"\s*:/gm;
|
|
1871
|
+
for (let match = topLevelKeyRegex.exec(content); match !== null; match = topLevelKeyRegex.exec(content)) {
|
|
1872
|
+
const key = expectDefined2(match[1]);
|
|
1873
|
+
const offset = match.index ?? 0;
|
|
1874
|
+
const line = lineFromOffset(offset);
|
|
1875
|
+
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
1876
|
+
let kind = "property";
|
|
1877
|
+
let signature = `"${key}": ..."`;
|
|
1878
|
+
if (isPackageJson) {
|
|
1879
|
+
if (key === "scripts" || key === "dependencies" || key === "devDependencies" || key === "peerDependencies" || key === "optionalDependencies") {
|
|
1880
|
+
kind = "const";
|
|
1881
|
+
signature = `"${key}": { ... }`;
|
|
1882
|
+
}
|
|
1883
|
+
} else if (isTsconfig) {
|
|
1884
|
+
if (key === "compilerOptions") {
|
|
1885
|
+
kind = "property";
|
|
1886
|
+
signature = `"compilerOptions": { ... }`;
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
if (isJsonSchema || isOpenApi) {
|
|
1890
|
+
if (key === "$schema" || key === "$id") {
|
|
1891
|
+
kind = "schema";
|
|
1892
|
+
signature = `"${key}": "..."`;
|
|
1893
|
+
} else if (key === "$ref") {
|
|
1894
|
+
kind = "schema";
|
|
1895
|
+
signature = `"$ref": "..."`;
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
symbols.push(
|
|
1899
|
+
makeSymbol({
|
|
1900
|
+
name: key,
|
|
1901
|
+
kind,
|
|
1902
|
+
line,
|
|
1903
|
+
col,
|
|
1904
|
+
signature,
|
|
1905
|
+
file,
|
|
1906
|
+
lang
|
|
1907
|
+
})
|
|
1908
|
+
);
|
|
1909
|
+
if (isPackageJson && key === "scripts") {
|
|
1910
|
+
extractPackageScripts(content, symbols, file, lang, lineOffsets, lineFromOffset);
|
|
1911
|
+
}
|
|
1912
|
+
if (isTsconfig && key === "compilerOptions") {
|
|
1913
|
+
extractCompilerOptions(content, symbols, file, lang, lineOffsets, line, lineFromOffset);
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
const defsRegex = /"\$defs"\s*:|"\$defs"\s*:/g;
|
|
1917
|
+
const defsMatch = defsRegex.exec(content);
|
|
1918
|
+
if (defsMatch !== null) {
|
|
1919
|
+
const offset = expectDefined2(defsMatch.index);
|
|
1920
|
+
const line = lineFromOffset(offset);
|
|
1921
|
+
symbols.push(
|
|
1922
|
+
makeSymbol({
|
|
1923
|
+
name: "$defs",
|
|
1924
|
+
kind: "property",
|
|
1925
|
+
line,
|
|
1926
|
+
col: offset - (lineOffsets[line - 1] ?? 0),
|
|
1927
|
+
signature: '"$defs": { ... }',
|
|
1928
|
+
file,
|
|
1929
|
+
lang
|
|
1930
|
+
})
|
|
1931
|
+
);
|
|
1932
|
+
}
|
|
1933
|
+
const defsPatterns = [
|
|
1934
|
+
/"\$defs"\s*:/g,
|
|
1935
|
+
/"definitions"\s*:/g,
|
|
1936
|
+
/"components"\s*:/g,
|
|
1937
|
+
/"schemas"\s*:/g
|
|
1938
|
+
];
|
|
1939
|
+
for (const pat of defsPatterns) {
|
|
1940
|
+
pat.lastIndex = 0;
|
|
1941
|
+
for (let match = pat.exec(content); match !== null; match = pat.exec(content)) {
|
|
1942
|
+
const offset = match.index ?? 0;
|
|
1943
|
+
const line = lineFromOffset(offset);
|
|
1944
|
+
const key = match[0]?.match(/"([^"]+)"/)?.[1] ?? expectDefined2(match[0]);
|
|
1945
|
+
symbols.push(
|
|
1946
|
+
makeSymbol({
|
|
1947
|
+
name: key,
|
|
1948
|
+
kind: "property",
|
|
1949
|
+
line,
|
|
1950
|
+
col: offset - (lineOffsets[line - 1] ?? 0),
|
|
1951
|
+
signature: `"${key}": { ... }`,
|
|
1952
|
+
file,
|
|
1953
|
+
lang
|
|
1954
|
+
})
|
|
1955
|
+
);
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
return { file, lang, symbols, mtimeMs: Date.now() };
|
|
1959
|
+
}
|
|
1960
|
+
function extractPackageScripts(content, symbols, file, lang, lineOffsets, lineFromOffset) {
|
|
1961
|
+
const scriptsBlockRegex = /"scripts"\s*:\s*\{([^}]+)\}/g;
|
|
1962
|
+
for (let match = scriptsBlockRegex.exec(content); match !== null; match = scriptsBlockRegex.exec(content)) {
|
|
1963
|
+
const blockContent = expectDefined2(match[0]);
|
|
1964
|
+
const blockOffset = match.index ?? 0;
|
|
1965
|
+
const scriptKeyRegex = /"(\w[\w-]*)"\s*:/g;
|
|
1966
|
+
for (let scriptMatch = scriptKeyRegex.exec(blockContent); scriptMatch !== null; scriptMatch = scriptKeyRegex.exec(blockContent)) {
|
|
1967
|
+
const key = expectDefined2(scriptMatch[1]);
|
|
1968
|
+
const keyOffset = blockOffset + expectDefined2(scriptMatch.index);
|
|
1969
|
+
const line = lineFromOffset(keyOffset);
|
|
1970
|
+
symbols.push(
|
|
1971
|
+
makeSymbol({
|
|
1972
|
+
name: key,
|
|
1973
|
+
kind: "function",
|
|
1974
|
+
line,
|
|
1975
|
+
col: keyOffset - (lineOffsets[line - 1] ?? 0),
|
|
1976
|
+
signature: `"${key}": "..."`,
|
|
1977
|
+
file,
|
|
1978
|
+
lang
|
|
1979
|
+
})
|
|
1980
|
+
);
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
function extractCompilerOptions(content, symbols, file, lang, lineOffsets, parentLine, lineFromOffset) {
|
|
1985
|
+
const optsBlockRegex = /"compilerOptions"\s*:\s*\{([^}]+)\}/g;
|
|
1986
|
+
for (let match = optsBlockRegex.exec(content); match !== null; match = optsBlockRegex.exec(content)) {
|
|
1987
|
+
const blockContent = expectDefined2(match[0]);
|
|
1988
|
+
const blockOffset = match.index ?? 0;
|
|
1989
|
+
const optKeyRegex = /"(\w[\w]*)"\s*:/g;
|
|
1990
|
+
for (let optMatch = optKeyRegex.exec(blockContent); optMatch !== null; optMatch = optKeyRegex.exec(blockContent)) {
|
|
1991
|
+
const key = expectDefined2(optMatch[1]);
|
|
1992
|
+
const keyOffset = blockOffset + expectDefined2(optMatch.index);
|
|
1993
|
+
const line = lineFromOffset(keyOffset);
|
|
1994
|
+
if (line <= parentLine) continue;
|
|
1995
|
+
symbols.push(
|
|
1996
|
+
makeSymbol({
|
|
1997
|
+
name: key,
|
|
1998
|
+
kind: "property",
|
|
1999
|
+
line,
|
|
2000
|
+
col: keyOffset - (lineOffsets[line - 1] ?? 0),
|
|
2001
|
+
signature: `"${key}": ...`,
|
|
2002
|
+
file,
|
|
2003
|
+
lang
|
|
2004
|
+
})
|
|
2005
|
+
);
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
function makeSymbol(opts) {
|
|
2010
|
+
return {
|
|
2011
|
+
id: 0,
|
|
2012
|
+
lang: opts.lang,
|
|
2013
|
+
kind: opts.kind,
|
|
2014
|
+
name: opts.name,
|
|
2015
|
+
file: opts.file,
|
|
2016
|
+
line: opts.line,
|
|
2017
|
+
col: opts.col,
|
|
2018
|
+
signature: opts.signature,
|
|
2019
|
+
docComment: "",
|
|
2020
|
+
scope: "",
|
|
2021
|
+
text: `${opts.name} ${opts.signature}`.trim()
|
|
2022
|
+
};
|
|
2023
|
+
}
|
|
2024
|
+
var init_json_parser = __esm({
|
|
2025
|
+
"src/codebase-index/json-parser.ts"() {
|
|
2026
|
+
"use strict";
|
|
2027
|
+
init_languages();
|
|
2028
|
+
}
|
|
2029
|
+
});
|
|
2030
|
+
|
|
2031
|
+
// src/codebase-index/yaml-parser.ts
|
|
2032
|
+
var yaml_parser_exports = {};
|
|
2033
|
+
__export(yaml_parser_exports, {
|
|
2034
|
+
detectLang: () => detectLang,
|
|
2035
|
+
parseSymbols: () => parseSymbols7
|
|
2036
|
+
});
|
|
2037
|
+
import { expectDefined as expectDefined3, truncate } from "@wrongstack/core/utils";
|
|
2038
|
+
function parseSymbols7(opts) {
|
|
2039
|
+
const { file, content, lang } = opts;
|
|
2040
|
+
try {
|
|
2041
|
+
return regexParse3({ file, content, lang });
|
|
2042
|
+
} catch {
|
|
2043
|
+
return { file, lang, symbols: [], mtimeMs: Date.now() };
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
function regexParse3(opts) {
|
|
2047
|
+
const { file, content, lang } = opts;
|
|
2048
|
+
const symbols = [];
|
|
2049
|
+
const lines = content.split("\n");
|
|
2050
|
+
const lineOffsets = [0];
|
|
2051
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2052
|
+
lineOffsets.push((lineOffsets[i] ?? 0) + (lines[i]?.length ?? 0) + 1);
|
|
2053
|
+
}
|
|
2054
|
+
function lineFromOffset(offset) {
|
|
2055
|
+
let lo = 0;
|
|
2056
|
+
let hi = lineOffsets.length - 1;
|
|
2057
|
+
while (lo < hi) {
|
|
2058
|
+
const mid = lo + hi + 1 >>> 1;
|
|
2059
|
+
if (expectDefined3(lineOffsets[mid]) <= offset) lo = mid;
|
|
2060
|
+
else hi = mid - 1;
|
|
2061
|
+
}
|
|
2062
|
+
return lo + 1;
|
|
2063
|
+
}
|
|
2064
|
+
const anchorRegex = /&(\w[\w-]*)/g;
|
|
2065
|
+
for (let match = anchorRegex.exec(content); match !== null; match = anchorRegex.exec(content)) {
|
|
2066
|
+
const name = expectDefined3(match[1]);
|
|
2067
|
+
const offset = match.index ?? 0;
|
|
2068
|
+
const line = lineFromOffset(offset);
|
|
2069
|
+
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
2070
|
+
symbols.push(
|
|
2071
|
+
makeSymbol2({
|
|
2072
|
+
name,
|
|
2073
|
+
kind: "const",
|
|
2074
|
+
line,
|
|
2075
|
+
col,
|
|
2076
|
+
signature: `&${name}`,
|
|
2077
|
+
file,
|
|
2078
|
+
lang
|
|
2079
|
+
})
|
|
2080
|
+
);
|
|
2081
|
+
}
|
|
2082
|
+
const aliasRegex = /\*(\w[\w-]*)/g;
|
|
2083
|
+
for (let match = aliasRegex.exec(content); match !== null; match = aliasRegex.exec(content)) {
|
|
2084
|
+
const name = expectDefined3(match[1]);
|
|
2085
|
+
const offset = match.index ?? 0;
|
|
2086
|
+
const line = lineFromOffset(offset);
|
|
2087
|
+
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
2088
|
+
symbols.push(
|
|
2089
|
+
makeSymbol2({
|
|
2090
|
+
name,
|
|
2091
|
+
kind: "const",
|
|
2092
|
+
line,
|
|
2093
|
+
col,
|
|
2094
|
+
signature: `*${name}`,
|
|
2095
|
+
file,
|
|
2096
|
+
lang
|
|
2097
|
+
})
|
|
2098
|
+
);
|
|
2099
|
+
}
|
|
2100
|
+
const kvRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:/gm;
|
|
2101
|
+
for (let match = kvRegex.exec(content); match !== null; match = kvRegex.exec(content)) {
|
|
2102
|
+
const indent = match[1]?.length ?? 0;
|
|
2103
|
+
const key = match[2];
|
|
2104
|
+
if (!key) continue;
|
|
2105
|
+
const offset = match.index ?? 0;
|
|
2106
|
+
const line = lineFromOffset(offset);
|
|
2107
|
+
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
2108
|
+
const lineContent = lines[line - 1] ?? "";
|
|
2109
|
+
if (/^[|&>]/.test(lineContent.trim())) continue;
|
|
2110
|
+
if (key === "---" || key === "...") continue;
|
|
2111
|
+
if (indent > 12) continue;
|
|
2112
|
+
const value = extractValue(content, match.index ?? 0);
|
|
2113
|
+
const kind = isScalar(value) ? "literal" : "property";
|
|
2114
|
+
const signature = `${key}: ${truncate(value, 60)}`;
|
|
2115
|
+
symbols.push(makeSymbol2({ name: key, kind, line, col, signature, file, lang }));
|
|
2116
|
+
}
|
|
2117
|
+
const listItemRegex = /^-(\s+)([^:#\s][^:#\s]*)\s*:/gm;
|
|
2118
|
+
for (let match = listItemRegex.exec(content); match !== null; match = listItemRegex.exec(content)) {
|
|
2119
|
+
const key = expectDefined3(match[2]);
|
|
2120
|
+
const offset = match.index ?? 0;
|
|
2121
|
+
const line = lineFromOffset(offset);
|
|
2122
|
+
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
2123
|
+
const value = extractValue(content, offset + match[0]?.length);
|
|
2124
|
+
const kind = isScalar(value) ? "literal" : "property";
|
|
2125
|
+
symbols.push(
|
|
2126
|
+
makeSymbol2({
|
|
2127
|
+
name: key,
|
|
2128
|
+
kind,
|
|
2129
|
+
line,
|
|
2130
|
+
col,
|
|
2131
|
+
signature: `- ${key}: ${truncate(value, 60)}`,
|
|
2132
|
+
file,
|
|
2133
|
+
lang
|
|
2134
|
+
})
|
|
2135
|
+
);
|
|
2136
|
+
}
|
|
2137
|
+
const blockScalarRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:\s*[|>](\s|$)/gm;
|
|
2138
|
+
for (let match = blockScalarRegex.exec(content); match !== null; match = blockScalarRegex.exec(content)) {
|
|
2139
|
+
const key = expectDefined3(match[2]);
|
|
2140
|
+
const offset = match.index ?? 0;
|
|
2141
|
+
const line = lineFromOffset(offset);
|
|
2142
|
+
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
2143
|
+
symbols.push(
|
|
2144
|
+
makeSymbol2({
|
|
2145
|
+
name: key,
|
|
2146
|
+
kind: "property",
|
|
2147
|
+
line,
|
|
2148
|
+
col,
|
|
2149
|
+
signature: `${key}: | ...`,
|
|
2150
|
+
file,
|
|
2151
|
+
lang
|
|
2152
|
+
})
|
|
2153
|
+
);
|
|
2154
|
+
}
|
|
2155
|
+
return { file, lang, symbols, mtimeMs: Date.now() };
|
|
2156
|
+
}
|
|
2157
|
+
function extractValue(content, afterColonOffset) {
|
|
2158
|
+
const lineEnd = content.indexOf("\n", afterColonOffset);
|
|
2159
|
+
const rest = content.slice(afterColonOffset, lineEnd < 0 ? void 0 : lineEnd);
|
|
2160
|
+
return rest.trim();
|
|
2161
|
+
}
|
|
2162
|
+
function isScalar(value) {
|
|
2163
|
+
if (!value) return false;
|
|
2164
|
+
if (/^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(value)) return true;
|
|
2165
|
+
if (/^(true|false|null|undefined)$/i.test(value)) return true;
|
|
2166
|
+
if (/^'[^']*'$/.test(value) || /^"[^"]*"$/.test(value)) return true;
|
|
2167
|
+
return false;
|
|
2168
|
+
}
|
|
2169
|
+
function makeSymbol2(opts) {
|
|
2170
|
+
return {
|
|
2171
|
+
id: 0,
|
|
2172
|
+
lang: opts.lang,
|
|
2173
|
+
kind: opts.kind,
|
|
2174
|
+
name: opts.name,
|
|
2175
|
+
file: opts.file,
|
|
2176
|
+
line: opts.line,
|
|
2177
|
+
col: opts.col,
|
|
2178
|
+
signature: opts.signature,
|
|
2179
|
+
docComment: "",
|
|
2180
|
+
scope: "",
|
|
2181
|
+
text: `${opts.name} ${opts.signature}`.trim()
|
|
2182
|
+
};
|
|
2183
|
+
}
|
|
2184
|
+
var init_yaml_parser = __esm({
|
|
2185
|
+
"src/codebase-index/yaml-parser.ts"() {
|
|
2186
|
+
"use strict";
|
|
2187
|
+
init_languages();
|
|
2188
|
+
}
|
|
2189
|
+
});
|
|
2190
|
+
|
|
2191
|
+
// src/codebase-index/tree-sitter/queries.ts
|
|
2192
|
+
function parseGroupedUse(text) {
|
|
2193
|
+
const open = text.indexOf("{");
|
|
2194
|
+
const close = text.lastIndexOf("}");
|
|
2195
|
+
if (open < 0 || close <= open) return null;
|
|
2196
|
+
const prefix = text.slice(0, open).replace(/[\\/]+$/, "");
|
|
2197
|
+
const out = [];
|
|
2198
|
+
for (const rawMember of text.slice(open + 1, close).split(",")) {
|
|
2199
|
+
let member = rawMember.trim();
|
|
2200
|
+
if (!member) continue;
|
|
2201
|
+
member = member.replace(
|
|
2202
|
+
/^(static|final|type|class|struct|enum|protocol|var|func|let|typealias|function|const)\s+/i,
|
|
2203
|
+
""
|
|
2204
|
+
).trim();
|
|
2205
|
+
if (!member) continue;
|
|
2206
|
+
const aliasMatch = /\s+as\s+([A-Za-z_]\w*)\s*$/i.exec(member);
|
|
2207
|
+
if (aliasMatch) member = member.slice(0, aliasMatch.index).trim();
|
|
2208
|
+
if (!member) continue;
|
|
2209
|
+
const module = prefix ? `${prefix}\\${member}` : member;
|
|
2210
|
+
const toName = member.split(/[\\/]/).filter(Boolean).pop();
|
|
2211
|
+
if (toName) out.push({ toName, callType: "import", module });
|
|
2212
|
+
}
|
|
2213
|
+
return out.length ? out : null;
|
|
2214
|
+
}
|
|
2215
|
+
function importFromText(prefixes) {
|
|
2216
|
+
return (node) => {
|
|
2217
|
+
let text = node.text.replace(/\s+/g, " ").trim();
|
|
2218
|
+
for (const prefix of prefixes) {
|
|
2219
|
+
if (text.startsWith(prefix)) text = text.slice(prefix.length).trim();
|
|
2220
|
+
}
|
|
2221
|
+
text = text.replace(
|
|
2222
|
+
/^(static|final|type|class|struct|enum|protocol|var|func|let|typealias|function|const)\s+/i,
|
|
2223
|
+
""
|
|
2224
|
+
).trim();
|
|
2225
|
+
if (text.includes("{")) return parseGroupedUse(text);
|
|
2226
|
+
if (text.includes(",") && !text.includes("<") && !text.includes("=")) {
|
|
2227
|
+
const out = [];
|
|
2228
|
+
for (const clause of text.split(",")) {
|
|
2229
|
+
const one = oneImportClause(clause.trim());
|
|
2230
|
+
if (one) out.push(one);
|
|
2231
|
+
}
|
|
2232
|
+
return out.length ? out : null;
|
|
2233
|
+
}
|
|
2234
|
+
const single = oneImportClause(text);
|
|
2235
|
+
return single ? [single] : null;
|
|
2236
|
+
};
|
|
2237
|
+
}
|
|
2238
|
+
function oneImportClause(rawClause) {
|
|
2239
|
+
let text = rawClause;
|
|
2240
|
+
text = text.replace(
|
|
2241
|
+
/^(static|final|type|class|struct|enum|protocol|var|func|let|typealias|function|const)\s+/i,
|
|
2242
|
+
""
|
|
2243
|
+
).trim();
|
|
2244
|
+
text = text.replace(/[;}]+$/g, "").trim();
|
|
2245
|
+
const aliasMatch = /\s+as\s+([A-Za-z_]\w*)\s*$/i.exec(text);
|
|
2246
|
+
if (aliasMatch) text = text.slice(0, aliasMatch.index).trim();
|
|
2247
|
+
const eqMatch = /^([A-Za-z_]\w*)\s*=\s*(.+)$/.exec(text);
|
|
2248
|
+
if (eqMatch) text = eqMatch[2].trim();
|
|
2249
|
+
if (!text) return null;
|
|
2250
|
+
if (text.endsWith("*")) text = text.slice(0, -1).replace(/[.]$/, "");
|
|
2251
|
+
if (!text) return null;
|
|
2252
|
+
const module = text;
|
|
2253
|
+
const toName = module.split(/[.\\/]/).filter(Boolean).pop()?.replace(/<.*>$/s, "");
|
|
2254
|
+
if (!toName) return null;
|
|
2255
|
+
return { toName, callType: "import", module };
|
|
2256
|
+
}
|
|
2257
|
+
function heritageLeaf(node, depth) {
|
|
2258
|
+
if (depth > 6) return null;
|
|
2259
|
+
const named = node.childForFieldName("name");
|
|
2260
|
+
if (named) {
|
|
2261
|
+
if (named.type === "scoped_type_identifier" || named.type === "qualified_name" || named.type === "scope_resolution" || named.type === "user_type") {
|
|
2262
|
+
return heritageLeaf(named, depth + 1);
|
|
2263
|
+
}
|
|
2264
|
+
return named.text;
|
|
2265
|
+
}
|
|
2266
|
+
const children = [];
|
|
2267
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
2268
|
+
const c = node.namedChild(i);
|
|
2269
|
+
if (c) children.push(c);
|
|
2270
|
+
}
|
|
2271
|
+
for (let i = children.length - 1; i >= 0; i--) {
|
|
2272
|
+
const c = children[i];
|
|
2273
|
+
if (c.type === "type_arguments" || c.type === "type_argument_list" || // cpp: (template_type arguments: (template_argument_list …)) — the
|
|
2274
|
+
// descriptor's type_identifier inside it is never the declared base.
|
|
2275
|
+
c.type === "template_argument_list" || c.type === "type_parameter_list" || c.type === "type_projection" || c.type === "value_arguments") {
|
|
2276
|
+
continue;
|
|
2277
|
+
}
|
|
2278
|
+
if (c.type === "type_identifier" || c.type === "identifier" || c.type === "constant" || c.type === "name") {
|
|
2279
|
+
return c.text;
|
|
2280
|
+
}
|
|
2281
|
+
if (c.type === "scoped_type_identifier" || c.type === "qualified_name" || c.type === "scope_resolution" || c.type === "user_type") {
|
|
2282
|
+
return heritageLeaf(c, depth + 1);
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
return leafSegment(
|
|
2286
|
+
node.text.replace(/\\/g, ".").replace(/::/g, ".").replace(/<[^<>]*>$/, "")
|
|
2287
|
+
);
|
|
2288
|
+
}
|
|
2289
|
+
function leafSegment(text) {
|
|
2290
|
+
return text.split(".").filter(Boolean).pop() ?? text;
|
|
2291
|
+
}
|
|
2292
|
+
function getQueries(lang) {
|
|
2293
|
+
return LANG_QUERIES[lang] ?? DEFAULT_QUERIES;
|
|
2294
|
+
}
|
|
2295
|
+
function readFirstString(node) {
|
|
2296
|
+
if (!node) return null;
|
|
2297
|
+
if (node.type === "string_literal" || node.type === "alias") {
|
|
2298
|
+
return node.text.replace(/^"|"$/g, "");
|
|
2299
|
+
}
|
|
2300
|
+
const child = node.namedChild(0);
|
|
2301
|
+
return child ? readFirstString(child) : null;
|
|
2302
|
+
}
|
|
2303
|
+
var heritageExtractor, cCallExtractor, rubyCallExtractor, firstIdentifierCallExtractor, cIncludeExtractor, phpConstructorExtractor, DEFAULT_QUERIES, LANG_QUERIES;
|
|
2304
|
+
var init_queries = __esm({
|
|
2305
|
+
"src/codebase-index/tree-sitter/queries.ts"() {
|
|
2306
|
+
"use strict";
|
|
2307
|
+
heritageExtractor = (node) => {
|
|
2308
|
+
const out = [];
|
|
2309
|
+
const SKIP_SUBTREES = /* @__PURE__ */ new Set([
|
|
2310
|
+
"type_arguments",
|
|
2311
|
+
"type_argument_list",
|
|
2312
|
+
// tree-sitter-cpp names its argument subtree template_argument_list —
|
|
2313
|
+
// verified AST: (base_class_clause (template_type name:
|
|
2314
|
+
// (type_identifier) arguments: (template_argument_list
|
|
2315
|
+
// (type_descriptor type: (type_identifier))))). Without this entry
|
|
2316
|
+
// `class D : Base<Foo>` recurses into the descriptor and emits Foo as a
|
|
2317
|
+
// phantom inherit ref.
|
|
2318
|
+
"template_argument_list",
|
|
2319
|
+
"type_parameter_list",
|
|
2320
|
+
"type_projection",
|
|
2321
|
+
"value_arguments"
|
|
2322
|
+
]);
|
|
2323
|
+
const collect = (current, depth) => {
|
|
2324
|
+
if (depth > 4) return;
|
|
2325
|
+
for (let i = 0; i < current.namedChildCount; i++) {
|
|
2326
|
+
const child = current.namedChild(i);
|
|
2327
|
+
if (!child) continue;
|
|
2328
|
+
if (SKIP_SUBTREES.has(child.type)) continue;
|
|
2329
|
+
if (child.type === "type_identifier" || child.type === "identifier" || child.type === "named_type" || child.type === "type" || // PHP heritage carries `name`; Ruby a `constant`.
|
|
2330
|
+
child.type === "constant" || child.type === "name") {
|
|
2331
|
+
const name = child.type === "named_type" ? leafSegment(child.text) : child.text;
|
|
2332
|
+
if (name) out.push({ toName: name });
|
|
2333
|
+
continue;
|
|
2334
|
+
}
|
|
2335
|
+
if (child.type === "generic_type" || child.type === "generic_name") {
|
|
2336
|
+
for (let j = 0; j < child.namedChildCount; j++) {
|
|
2337
|
+
const inner = child.namedChild(j);
|
|
2338
|
+
if (inner && !SKIP_SUBTREES.has(inner.type) && (inner.type === "type_identifier" || inner.type === "identifier" || inner.type === "name")) {
|
|
2339
|
+
out.push({ toName: inner.text });
|
|
2340
|
+
break;
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
continue;
|
|
2344
|
+
}
|
|
2345
|
+
if (child.type === "qualified_name" || child.type === "scoped_type_identifier" || child.type === "user_type" || child.type === "scope_resolution") {
|
|
2346
|
+
const leaf = heritageLeaf(child, 0);
|
|
2347
|
+
if (leaf) out.push({ toName: leaf });
|
|
2348
|
+
continue;
|
|
2349
|
+
}
|
|
2350
|
+
collect(child, depth + 1);
|
|
2351
|
+
}
|
|
2352
|
+
};
|
|
2353
|
+
collect(node, 0);
|
|
2354
|
+
return out;
|
|
2355
|
+
};
|
|
2356
|
+
cCallExtractor = (node) => {
|
|
2357
|
+
const fn = node.childForFieldName("function");
|
|
2358
|
+
if (!fn) return null;
|
|
2359
|
+
if (fn.type === "field_expression") {
|
|
2360
|
+
const field = fn.childForFieldName("field");
|
|
2361
|
+
if (field) return [{ toName: field.text, callType: "call" }];
|
|
2362
|
+
const seg = fn.text.split("->").filter(Boolean).pop();
|
|
2363
|
+
if (seg) return [{ toName: leafSegment(seg.split(".")[0] ?? seg), callType: "call" }];
|
|
2364
|
+
return null;
|
|
2365
|
+
}
|
|
2366
|
+
if (fn.type === "qualified_identifier") {
|
|
2367
|
+
const name = fn.childForFieldName("name");
|
|
2368
|
+
if (name) return [{ toName: name.text, callType: "call" }];
|
|
2369
|
+
const seg = fn.text.split("::").filter(Boolean).pop();
|
|
2370
|
+
if (seg) return [{ toName: seg.split(/[<(]/)[0].trim(), callType: "call" }];
|
|
2371
|
+
return null;
|
|
2372
|
+
}
|
|
2373
|
+
return [{ toName: fn.text.split(/[<(]/)[0].trim(), callType: "call" }];
|
|
2374
|
+
};
|
|
2375
|
+
rubyCallExtractor = (node) => {
|
|
2376
|
+
const emissions = [];
|
|
2377
|
+
const method = node.childForFieldName("method");
|
|
2378
|
+
if (method) {
|
|
2379
|
+
const name = method.text;
|
|
2380
|
+
if (name && !name.includes(" ")) emissions.push({ toName: name, callType: "call" });
|
|
2381
|
+
if (name === "require" || name === "require_relative") {
|
|
2382
|
+
const args = node.childForFieldName("arguments");
|
|
2383
|
+
const first = args?.namedChild(0);
|
|
2384
|
+
if (first) {
|
|
2385
|
+
const raw = first.text.replace(/^['"]|['"]$/g, "");
|
|
2386
|
+
const toName = raw.split("/").filter(Boolean).pop();
|
|
2387
|
+
if (toName) emissions.push({ toName, callType: "import", module: raw });
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
return emissions;
|
|
2392
|
+
};
|
|
2393
|
+
firstIdentifierCallExtractor = (node) => {
|
|
2394
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
2395
|
+
const child = node.namedChild(i);
|
|
2396
|
+
if (child && (child.type === "simple_identifier" || child.type === "identifier")) {
|
|
2397
|
+
return [{ toName: child.text, callType: "call" }];
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
const first = node.namedChild(0);
|
|
2401
|
+
if (!first) return null;
|
|
2402
|
+
const leaf = leafSegment(first.text.split(/[<(]/)[0] ?? first.text);
|
|
2403
|
+
if (!leaf) return null;
|
|
2404
|
+
return [{ toName: leaf, callType: "call" }];
|
|
2405
|
+
};
|
|
2406
|
+
cIncludeExtractor = (node) => {
|
|
2407
|
+
const raw = node.text.replace(/^#\s*include\s*/i, "").trim();
|
|
2408
|
+
const module = raw.replace(/^["'<]|["'>]$/g, "");
|
|
2409
|
+
if (!module) return null;
|
|
2410
|
+
const toName = module.split("/").pop()?.replace(/\.h$/, "");
|
|
2411
|
+
if (!toName) return null;
|
|
2412
|
+
return [{ toName, callType: "import", module }];
|
|
2413
|
+
};
|
|
2414
|
+
phpConstructorExtractor = (node) => {
|
|
2415
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
2416
|
+
const child = node.namedChild(i);
|
|
2417
|
+
if (child && (child.type === "qualified_name" || child.type === "name")) {
|
|
2418
|
+
const leaf = child.text.split(/[\\]/).filter(Boolean).pop();
|
|
2419
|
+
if (leaf) return [{ toName: leaf, callType: "call" }];
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
return null;
|
|
2423
|
+
};
|
|
2424
|
+
DEFAULT_QUERIES = {
|
|
2425
|
+
declKinds: {}
|
|
2426
|
+
};
|
|
2427
|
+
LANG_QUERIES = {
|
|
2428
|
+
// ─── C family ──────────────────────────────────────────────────────────────
|
|
2429
|
+
c: {
|
|
2430
|
+
declKinds: {
|
|
2431
|
+
function_definition: "function",
|
|
2432
|
+
declaration: "function",
|
|
2433
|
+
// K&R-style `int foo(...)` ambiguous w/ local var; the visitor prefers the function branch when the declarator field is present
|
|
2434
|
+
struct_specifier: "struct",
|
|
2435
|
+
union_specifier: "struct",
|
|
2436
|
+
enum_specifier: "enum",
|
|
2437
|
+
type_definition: "type",
|
|
2438
|
+
// `typedef … X;`
|
|
2439
|
+
preproc_def: "const"
|
|
2440
|
+
// `#define NAME …`
|
|
2441
|
+
},
|
|
2442
|
+
nameField: {
|
|
2443
|
+
function_definition: "declarator",
|
|
2444
|
+
declaration: "declarator",
|
|
2445
|
+
struct_specifier: "name",
|
|
2446
|
+
enum_specifier: "name",
|
|
2447
|
+
type_definition: "declarator",
|
|
2448
|
+
preproc_def: "name"
|
|
2449
|
+
},
|
|
2450
|
+
scopeNodes: /* @__PURE__ */ new Set([
|
|
2451
|
+
"translation_unit",
|
|
2452
|
+
"function_definition",
|
|
2453
|
+
"struct_specifier",
|
|
2454
|
+
"union_specifier",
|
|
2455
|
+
"enum_specifier"
|
|
2456
|
+
]),
|
|
2457
|
+
refRules: {
|
|
2458
|
+
// `obj->run()` and `Cls::stat()` carry structured function fields —
|
|
2459
|
+
// cCallExtractor handles all three AST shapes.
|
|
2460
|
+
call_expression: { callType: "call", nameExtractor: cCallExtractor },
|
|
2461
|
+
preproc_include: { callType: "import", nameExtractor: cIncludeExtractor }
|
|
2462
|
+
}
|
|
2463
|
+
},
|
|
2464
|
+
cpp: {
|
|
2465
|
+
declKinds: {
|
|
2466
|
+
function_definition: "function",
|
|
2467
|
+
template_declaration: "function",
|
|
2468
|
+
// `template<typename T> …`
|
|
2469
|
+
class_specifier: "class",
|
|
2470
|
+
struct_specifier: "struct",
|
|
2471
|
+
union_specifier: "struct",
|
|
2472
|
+
enum_specifier: "enum",
|
|
2473
|
+
namespace_definition: "namespace",
|
|
2474
|
+
type_definition: "type"
|
|
2475
|
+
},
|
|
2476
|
+
nameField: {
|
|
2477
|
+
function_definition: "declarator",
|
|
2478
|
+
template_declaration: "name",
|
|
2479
|
+
class_specifier: "name",
|
|
2480
|
+
struct_specifier: "name",
|
|
2481
|
+
enum_specifier: "name",
|
|
2482
|
+
namespace_definition: "name",
|
|
2483
|
+
type_definition: "declarator"
|
|
2484
|
+
},
|
|
2485
|
+
scopeNodes: /* @__PURE__ */ new Set([
|
|
2486
|
+
"translation_unit",
|
|
2487
|
+
"function_definition",
|
|
2488
|
+
"class_specifier",
|
|
2489
|
+
"struct_specifier",
|
|
2490
|
+
"union_specifier",
|
|
2491
|
+
"enum_specifier",
|
|
2492
|
+
"namespace_definition"
|
|
2493
|
+
]),
|
|
2494
|
+
refRules: {
|
|
2495
|
+
call_expression: { callType: "call", nameExtractor: cCallExtractor },
|
|
2496
|
+
preproc_include: { callType: "import", nameExtractor: cIncludeExtractor },
|
|
2497
|
+
// `class Foo : public Bar, private Baz` — the base-class clause.
|
|
2498
|
+
base_class_clause: { callType: "inherit", nameExtractor: heritageExtractor }
|
|
2499
|
+
}
|
|
2500
|
+
},
|
|
2501
|
+
java: {
|
|
2502
|
+
declKinds: {
|
|
2503
|
+
class_declaration: "class",
|
|
2504
|
+
interface_declaration: "interface",
|
|
2505
|
+
enum_declaration: "enum",
|
|
2506
|
+
record_declaration: "class",
|
|
2507
|
+
annotation_type_declaration: "interface",
|
|
2508
|
+
method_declaration: "method",
|
|
2509
|
+
constructor_declaration: "method",
|
|
2510
|
+
field_declaration: "property"
|
|
2511
|
+
},
|
|
2512
|
+
nameField: {
|
|
2513
|
+
class_declaration: "name",
|
|
2514
|
+
interface_declaration: "name",
|
|
2515
|
+
enum_declaration: "name",
|
|
2516
|
+
record_declaration: "name",
|
|
2517
|
+
annotation_type_declaration: "name",
|
|
2518
|
+
method_declaration: "name",
|
|
2519
|
+
constructor_declaration: "name"
|
|
2520
|
+
},
|
|
2521
|
+
// `field_declaration` has no single `name` field — it carries a list of
|
|
2522
|
+
// variable declarators. We emit one Symbol per node using the first
|
|
2523
|
+
// identifier-shaped named child (see `extractName` fallback in
|
|
2524
|
+
// `visitor.ts`). `int a, b, c;` therefore indexes only `a` — splitting
|
|
2525
|
+
// multi-declarator fields into separate Symbols is a separate refactor
|
|
2526
|
+
// that needs the visitor to know it has multiple names per node, and no
|
|
2527
|
+
// current test relies on it.
|
|
2528
|
+
scopeNodes: /* @__PURE__ */ new Set([
|
|
2529
|
+
"program",
|
|
2530
|
+
"class_declaration",
|
|
2531
|
+
"interface_declaration",
|
|
2532
|
+
"enum_declaration",
|
|
2533
|
+
"record_declaration"
|
|
2534
|
+
]),
|
|
2535
|
+
refRules: {
|
|
2536
|
+
method_invocation: { callType: "call", field: "name" },
|
|
2537
|
+
object_creation_expression: { callType: "call", field: "type" },
|
|
2538
|
+
// Verified AST: `superclass: (superclass (type_identifier))` and
|
|
2539
|
+
// `interfaces: (super_interfaces (type_list ...))` — no underscores.
|
|
2540
|
+
superclass: { callType: "inherit", nameExtractor: heritageExtractor },
|
|
2541
|
+
super_interfaces: {
|
|
2542
|
+
callType: "implement",
|
|
2543
|
+
nameExtractor: heritageExtractor
|
|
2544
|
+
},
|
|
2545
|
+
import_declaration: {
|
|
2546
|
+
callType: "import",
|
|
2547
|
+
nameExtractor: importFromText(["import "])
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
},
|
|
2551
|
+
csharp: {
|
|
2552
|
+
// C# 10+ `namespace Foo.Bar;` produces this node type. The legacy block
|
|
2553
|
+
// form `namespace Foo.Bar { ... }` produces `namespace_declaration`. Both
|
|
2554
|
+
// carry a `qualified_name` child whose text already includes the dots.
|
|
2555
|
+
// `using_directive` is intentionally not a declaration. Imports are
|
|
2556
|
+
// extracted separately; indexing a using directive as a namespace makes
|
|
2557
|
+
// the resolver bind it to its own source file before the real declaration.
|
|
2558
|
+
declKinds: {
|
|
2559
|
+
file_scoped_namespace_declaration: "namespace",
|
|
2560
|
+
class_declaration: "class",
|
|
2561
|
+
interface_declaration: "interface",
|
|
2562
|
+
struct_declaration: "struct",
|
|
2563
|
+
enum_declaration: "enum",
|
|
2564
|
+
record_declaration: "class",
|
|
2565
|
+
method_declaration: "method",
|
|
2566
|
+
constructor_declaration: "method",
|
|
2567
|
+
property_declaration: "property",
|
|
2568
|
+
field_declaration: "property",
|
|
2569
|
+
namespace_declaration: "namespace"
|
|
2570
|
+
},
|
|
2571
|
+
// Custom name extractor: take the full dotted name verbatim.
|
|
2572
|
+
nameExtractor: (node) => {
|
|
2573
|
+
const inner = node.namedChild(0);
|
|
2574
|
+
if (inner && (inner.type === "qualified_name" || inner.type === "name")) {
|
|
2575
|
+
return inner.text;
|
|
2576
|
+
}
|
|
2577
|
+
return null;
|
|
2578
|
+
},
|
|
2579
|
+
scopeNodes: /* @__PURE__ */ new Set([
|
|
2580
|
+
"compilation_unit",
|
|
2581
|
+
"namespace_declaration",
|
|
2582
|
+
"class_declaration",
|
|
2583
|
+
"interface_declaration",
|
|
2584
|
+
"struct_declaration",
|
|
2585
|
+
"enum_declaration",
|
|
2586
|
+
"record_declaration"
|
|
2587
|
+
]),
|
|
2588
|
+
refRules: {
|
|
2589
|
+
// Verified AST: `invocation_expression function: (identifier)` — the
|
|
2590
|
+
// callee field is `function` (C-style), not `name`.
|
|
2591
|
+
invocation_expression: { callType: "call", field: "function" },
|
|
2592
|
+
object_creation_expression: { callType: "call", field: "type" },
|
|
2593
|
+
base_list: { callType: "inherit", nameExtractor: heritageExtractor },
|
|
2594
|
+
using_directive: {
|
|
2595
|
+
callType: "import",
|
|
2596
|
+
nameExtractor: importFromText(["using "])
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
},
|
|
2600
|
+
php: {
|
|
2601
|
+
declKinds: {
|
|
2602
|
+
function_definition: "function",
|
|
2603
|
+
method_declaration: "method",
|
|
2604
|
+
class_declaration: "class",
|
|
2605
|
+
interface_declaration: "interface",
|
|
2606
|
+
trait_declaration: "class",
|
|
2607
|
+
enum_declaration: "enum",
|
|
2608
|
+
namespace_definition: "namespace"
|
|
2609
|
+
},
|
|
2610
|
+
nameField: {
|
|
2611
|
+
function_definition: "name",
|
|
2612
|
+
method_declaration: "name",
|
|
2613
|
+
class_declaration: "name",
|
|
2614
|
+
interface_declaration: "name",
|
|
2615
|
+
trait_declaration: "name",
|
|
2616
|
+
enum_declaration: "name",
|
|
2617
|
+
namespace_definition: "name"
|
|
2618
|
+
},
|
|
2619
|
+
scopeNodes: /* @__PURE__ */ new Set([
|
|
2620
|
+
"program",
|
|
2621
|
+
"namespace_definition",
|
|
2622
|
+
"class_declaration",
|
|
2623
|
+
"interface_declaration",
|
|
2624
|
+
"trait_declaration",
|
|
2625
|
+
"enum_declaration"
|
|
2626
|
+
]),
|
|
2627
|
+
refRules: {
|
|
2628
|
+
function_call_expression: { callType: "call", field: "function" },
|
|
2629
|
+
// Verified AST: `new App\Model\User()` carries a BARE qualified_name
|
|
2630
|
+
// child (no `name:` field), so the field default never fires.
|
|
2631
|
+
object_creation_expression: { callType: "call", nameExtractor: phpConstructorExtractor },
|
|
2632
|
+
base_clause: { callType: "inherit", nameExtractor: heritageExtractor },
|
|
2633
|
+
class_interface_clause: {
|
|
2634
|
+
callType: "implement",
|
|
2635
|
+
nameExtractor: heritageExtractor
|
|
2636
|
+
},
|
|
2637
|
+
// Verified AST: `namespace_use_declaration (namespace_use_clause
|
|
2638
|
+
// (qualified_name ...))` — not `use_declaration`.
|
|
2639
|
+
namespace_use_declaration: {
|
|
2640
|
+
callType: "import",
|
|
2641
|
+
nameExtractor: importFromText(["use "])
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
},
|
|
2645
|
+
// ─── Scripting / mobile ────────────────────────────────────────────────────
|
|
2646
|
+
ruby: {
|
|
2647
|
+
declKinds: {
|
|
2648
|
+
method: "function",
|
|
2649
|
+
singleton_method: "method",
|
|
2650
|
+
class: "class",
|
|
2651
|
+
module: "namespace",
|
|
2652
|
+
constant: "const"
|
|
2653
|
+
},
|
|
2654
|
+
nameField: {
|
|
2655
|
+
method: "name",
|
|
2656
|
+
singleton_method: "name",
|
|
2657
|
+
class: "name",
|
|
2658
|
+
module: "name",
|
|
2659
|
+
constant: "name"
|
|
2660
|
+
},
|
|
2661
|
+
scopeNodes: /* @__PURE__ */ new Set(["program", "class", "module", "singleton_method", "method"]),
|
|
2662
|
+
refRules: {
|
|
2663
|
+
// `call` covers both `foo(...)` and `obj.foo(...)` — the extractor
|
|
2664
|
+
// records the method leaf, plus `require`/`require_relative` imports.
|
|
2665
|
+
call: { callType: "call", nameExtractor: rubyCallExtractor },
|
|
2666
|
+
superclass: { callType: "inherit", nameExtractor: heritageExtractor }
|
|
2667
|
+
}
|
|
2668
|
+
},
|
|
2669
|
+
swift: {
|
|
2670
|
+
declKinds: {
|
|
2671
|
+
function_declaration: "function",
|
|
2672
|
+
class_declaration: "class",
|
|
2673
|
+
struct_declaration: "struct",
|
|
2674
|
+
enum_declaration: "enum",
|
|
2675
|
+
protocol_declaration: "interface",
|
|
2676
|
+
actor_declaration: "class",
|
|
2677
|
+
extension_declaration: "class",
|
|
2678
|
+
initializer: "method",
|
|
2679
|
+
property_declaration: "property"
|
|
2680
|
+
},
|
|
2681
|
+
nameField: {
|
|
2682
|
+
function_declaration: "name",
|
|
2683
|
+
class_declaration: "name",
|
|
2684
|
+
struct_declaration: "name",
|
|
2685
|
+
enum_declaration: "name",
|
|
2686
|
+
protocol_declaration: "name",
|
|
2687
|
+
actor_declaration: "name",
|
|
2688
|
+
extension_declaration: "name",
|
|
2689
|
+
initializer: "name",
|
|
2690
|
+
property_declaration: "name"
|
|
2691
|
+
},
|
|
2692
|
+
scopeNodes: /* @__PURE__ */ new Set([
|
|
2693
|
+
"source_file",
|
|
2694
|
+
"class_declaration",
|
|
2695
|
+
"struct_declaration",
|
|
2696
|
+
"enum_declaration",
|
|
2697
|
+
"protocol_declaration",
|
|
2698
|
+
"actor_declaration",
|
|
2699
|
+
"extension_declaration"
|
|
2700
|
+
]),
|
|
2701
|
+
refRules: {
|
|
2702
|
+
// Verified AST: `call_expression (simple_identifier) (call_suffix …)` —
|
|
2703
|
+
// the callee is a bare first child, no field name.
|
|
2704
|
+
call_expression: { callType: "call", nameExtractor: firstIdentifierCallExtractor },
|
|
2705
|
+
// Verified AST: `inheritance_specifier inherits_from: (user_type …)`.
|
|
2706
|
+
inheritance_specifier: { callType: "inherit", nameExtractor: heritageExtractor },
|
|
2707
|
+
import_declaration: {
|
|
2708
|
+
callType: "import",
|
|
2709
|
+
nameExtractor: importFromText(["import ", "import type ", "@testable import "])
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
},
|
|
2713
|
+
kotlin: {
|
|
2714
|
+
declKinds: {
|
|
2715
|
+
class_declaration: "class",
|
|
2716
|
+
object_declaration: "class",
|
|
2717
|
+
interface_declaration: "interface",
|
|
2718
|
+
function_declaration: "function",
|
|
2719
|
+
property_declaration: "property",
|
|
2720
|
+
type_alias: "type"
|
|
2721
|
+
},
|
|
2722
|
+
nameField: {
|
|
2723
|
+
class_declaration: "name",
|
|
2724
|
+
object_declaration: "name",
|
|
2725
|
+
interface_declaration: "name",
|
|
2726
|
+
function_declaration: "name",
|
|
2727
|
+
property_declaration: "name",
|
|
2728
|
+
type_alias: "name"
|
|
2729
|
+
},
|
|
2730
|
+
scopeNodes: /* @__PURE__ */ new Set([
|
|
2731
|
+
"source_file",
|
|
2732
|
+
"class_declaration",
|
|
2733
|
+
"object_declaration",
|
|
2734
|
+
"interface_declaration",
|
|
2735
|
+
"function_declaration"
|
|
2736
|
+
]),
|
|
2737
|
+
refRules: {
|
|
2738
|
+
call_expression: { callType: "call", nameExtractor: firstIdentifierCallExtractor },
|
|
2739
|
+
// Verified AST: `delegation_specifier (user_type (type_identifier))` —
|
|
2740
|
+
// the `: Handler` / `: Base()` clause.
|
|
2741
|
+
delegation_specifier: { callType: "inherit", nameExtractor: heritageExtractor },
|
|
2742
|
+
import_header: {
|
|
2743
|
+
callType: "import",
|
|
2744
|
+
nameExtractor: importFromText(["import "])
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
},
|
|
2748
|
+
elixir: {
|
|
2749
|
+
declKinds: {
|
|
2750
|
+
// `def foo`, `defp foo`, `defmacro foo`, `macrop foo` all surface as
|
|
2751
|
+
// `call` nodes in the tree-sitter grammar — there is no
|
|
2752
|
+
// `function_definition`. The `nameExtractor` walks the call's
|
|
2753
|
+
// children to pick the right sibling identifier.
|
|
2754
|
+
call: "function",
|
|
2755
|
+
module: "namespace"
|
|
2756
|
+
},
|
|
2757
|
+
nameExtractor: (node) => {
|
|
2758
|
+
if (node.type === "module") {
|
|
2759
|
+
const aliasNode = node.childForFieldName("alias");
|
|
2760
|
+
return readFirstString(aliasNode) ?? null;
|
|
2761
|
+
}
|
|
2762
|
+
if (node.type !== "call") return null;
|
|
2763
|
+
const first = node.namedChild(0);
|
|
2764
|
+
if (!first) return null;
|
|
2765
|
+
const target = first.text;
|
|
2766
|
+
if (target !== "def" && target !== "defp" && target !== "defmacro" && target !== "defp_macro" && target !== "macrop" && target !== "defprotocol" && target !== "defguard" && target !== "defguardp") {
|
|
2767
|
+
return null;
|
|
2768
|
+
}
|
|
2769
|
+
const nameNode = node.namedChild(1);
|
|
2770
|
+
return nameNode?.text ?? null;
|
|
2771
|
+
},
|
|
2772
|
+
scopeNodes: /* @__PURE__ */ new Set(["source", "module"])
|
|
2773
|
+
},
|
|
2774
|
+
shell: {
|
|
2775
|
+
declKinds: {
|
|
2776
|
+
function_definition: "function"
|
|
2777
|
+
},
|
|
2778
|
+
nameField: { function_definition: "name" },
|
|
2779
|
+
scopeNodes: /* @__PURE__ */ new Set(["program", "function_definition"])
|
|
2780
|
+
}
|
|
2781
|
+
};
|
|
2782
|
+
}
|
|
2783
|
+
});
|
|
2784
|
+
|
|
2785
|
+
// src/codebase-index/tree-sitter/util.ts
|
|
2786
|
+
function lineColAt2(offsets, index) {
|
|
2787
|
+
let low = 0;
|
|
2788
|
+
let high = offsets.length;
|
|
2789
|
+
while (low < high) {
|
|
2790
|
+
const mid = low + high >>> 1;
|
|
2791
|
+
if ((offsets[mid] ?? 0) < index) low = mid + 1;
|
|
2792
|
+
else high = mid;
|
|
2793
|
+
}
|
|
2794
|
+
const lastNl = low > 0 ? offsets[low - 1] ?? -1 : -1;
|
|
2795
|
+
return { line: low + 1, col: index - lastNl };
|
|
2796
|
+
}
|
|
2797
|
+
function newlineOffsets3(content) {
|
|
2798
|
+
const offsets = [];
|
|
2799
|
+
for (let i = 0; i < content.length; i++) {
|
|
2800
|
+
if (content.charCodeAt(i) === 10) offsets.push(i);
|
|
2801
|
+
}
|
|
2802
|
+
return offsets;
|
|
2803
|
+
}
|
|
2804
|
+
var TREE_SITTER_MAX_FILE_CHARS, TREE_SITTER_MAX_SYMBOLS;
|
|
2805
|
+
var init_util = __esm({
|
|
2806
|
+
"src/codebase-index/tree-sitter/util.ts"() {
|
|
2807
|
+
"use strict";
|
|
2808
|
+
TREE_SITTER_MAX_FILE_CHARS = 512 * 1024;
|
|
2809
|
+
TREE_SITTER_MAX_SYMBOLS = 500;
|
|
2810
|
+
}
|
|
2811
|
+
});
|
|
2812
|
+
|
|
2813
|
+
// src/codebase-index/tree-sitter/visitor.ts
|
|
2814
|
+
function visitTree(tree, content, file, lang, queries) {
|
|
2815
|
+
const boundedContent = content.length > TREE_SITTER_MAX_FILE_CHARS ? content.slice(0, TREE_SITTER_MAX_FILE_CHARS) : content;
|
|
2816
|
+
const nlOffsets = newlineOffsets3(boundedContent);
|
|
2817
|
+
const symbols = [];
|
|
2818
|
+
const refs = [];
|
|
2819
|
+
const seenRefs = /* @__PURE__ */ new Set();
|
|
2820
|
+
const scopeStack = [];
|
|
2821
|
+
function emitRefsForNode(node, rule) {
|
|
2822
|
+
const emissions = rule.nameExtractor?.(node) ?? defaultRefTarget(node, rule);
|
|
2823
|
+
if (!emissions) return;
|
|
2824
|
+
const { line } = lineColAt2(nlOffsets, node.startIndex);
|
|
2825
|
+
for (const emission of emissions) {
|
|
2826
|
+
if (!emission.toName) continue;
|
|
2827
|
+
const callType = emission.callType ?? rule.callType;
|
|
2828
|
+
const key = `${emission.toName}:${callType}:${line}:${emission.module ?? ""}:${node.startIndex}`;
|
|
2829
|
+
if (seenRefs.has(key)) continue;
|
|
2830
|
+
seenRefs.add(key);
|
|
2831
|
+
refs.push({
|
|
2832
|
+
fromId: 0,
|
|
2833
|
+
// assignRefsToSymbols attaches owners after insertion
|
|
2834
|
+
toName: emission.toName.slice(0, 200),
|
|
2835
|
+
callType,
|
|
2836
|
+
line,
|
|
2837
|
+
lang,
|
|
2838
|
+
module: emission.module
|
|
2839
|
+
});
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
function defaultRefTarget(node, rule) {
|
|
2843
|
+
if (!rule.field) return null;
|
|
2844
|
+
const field = node.childForFieldName(rule.field);
|
|
2845
|
+
if (!field) return null;
|
|
2846
|
+
const leaf = field.text.split(/[.:\\]/).filter(Boolean).pop()?.split(/[<(]/)[0];
|
|
2847
|
+
if (!leaf) return null;
|
|
2848
|
+
return [{ toName: leaf.trim() }];
|
|
2849
|
+
}
|
|
2850
|
+
function visit(node, depth) {
|
|
2851
|
+
if (symbols.length >= TREE_SITTER_MAX_SYMBOLS) return;
|
|
2852
|
+
if (node.isMissing || node.isError) {
|
|
2853
|
+
} else {
|
|
2854
|
+
const kind = queries.declKinds[node.type];
|
|
2855
|
+
if (kind) {
|
|
2856
|
+
const emitted = emitSymbol(
|
|
2857
|
+
node,
|
|
2858
|
+
kind,
|
|
2859
|
+
file,
|
|
2860
|
+
lang,
|
|
2861
|
+
scopeStack,
|
|
2862
|
+
boundedContent,
|
|
2863
|
+
nlOffsets,
|
|
2864
|
+
queries
|
|
2865
|
+
);
|
|
2866
|
+
if (emitted) symbols.push(emitted);
|
|
2867
|
+
}
|
|
2868
|
+
const refRule = queries.refRules?.[node.type];
|
|
2869
|
+
if (refRule) emitRefsForNode(node, refRule);
|
|
2870
|
+
}
|
|
2871
|
+
const pushesScope = queries.scopeNodes?.has(node.type) ?? false;
|
|
2872
|
+
const pushIdx = pushesScope ? pushScope(scopeStack, node, queries) : -1;
|
|
2873
|
+
if (queries.skipNamedChildren) {
|
|
2874
|
+
if (pushIdx !== -1) scopeStack.pop();
|
|
2875
|
+
return;
|
|
2876
|
+
}
|
|
2877
|
+
for (const child of node.namedChildren) {
|
|
2878
|
+
visit(child, depth + 1);
|
|
2879
|
+
if (symbols.length >= TREE_SITTER_MAX_SYMBOLS) {
|
|
2880
|
+
if (pushIdx !== -1) scopeStack.pop();
|
|
2881
|
+
return;
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
if (pushIdx !== -1) scopeStack.pop();
|
|
2885
|
+
}
|
|
2886
|
+
visit(tree.rootNode, 0);
|
|
2887
|
+
return { symbols, refs };
|
|
2888
|
+
}
|
|
2889
|
+
function pushScope(scopeStack, node, queries) {
|
|
2890
|
+
const name = extractName(node, queries);
|
|
2891
|
+
if (!name) return -1;
|
|
2892
|
+
scopeStack.push(name);
|
|
2893
|
+
return scopeStack.length - 1;
|
|
2894
|
+
}
|
|
2895
|
+
function extractName(node, queries) {
|
|
2896
|
+
if (queries.nameExtractor) {
|
|
2897
|
+
const extracted = queries.nameExtractor(node);
|
|
2898
|
+
if (extracted) return extracted;
|
|
2899
|
+
}
|
|
2900
|
+
const fieldName = queries.nameField?.[node.type] ?? "name";
|
|
2901
|
+
const field = node.childForFieldName(fieldName);
|
|
2902
|
+
if (field) {
|
|
2903
|
+
if (IDENTIFIER_NODE_TYPES.has(field.type)) {
|
|
2904
|
+
return field.text;
|
|
2905
|
+
}
|
|
2906
|
+
const inner = field.childForFieldName("name") ?? field.namedChild(0);
|
|
2907
|
+
if (inner && IDENTIFIER_NODE_TYPES.has(inner.type)) {
|
|
2908
|
+
return inner.text;
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
2912
|
+
const child = node.namedChild(i);
|
|
2913
|
+
if (child && IDENTIFIER_NODE_TYPES.has(child.type)) {
|
|
2914
|
+
return child.text;
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
return null;
|
|
2918
|
+
}
|
|
2919
|
+
function emitSymbol(node, kind, file, lang, scopeStack, content, nlOffsets, queries) {
|
|
2920
|
+
const name = extractName(node, queries);
|
|
2921
|
+
if (!name) return null;
|
|
2922
|
+
const pos = node.startIndex;
|
|
2923
|
+
const { line, col } = lineColAt2(nlOffsets, pos);
|
|
2924
|
+
const end = Math.min(node.endIndex, content.length);
|
|
2925
|
+
const signature = content.slice(pos, end).replace(/\s+/g, " ").trim().slice(0, 500);
|
|
2926
|
+
const scope = scopeStack.join(".");
|
|
2927
|
+
const text = [name, signature].filter(Boolean).join(" | ").trim().slice(0, 1e3);
|
|
2928
|
+
return {
|
|
2929
|
+
id: 0,
|
|
2930
|
+
// caller assigns during bulk insertion
|
|
2931
|
+
lang,
|
|
2932
|
+
kind,
|
|
2933
|
+
name: name.slice(0, 200),
|
|
2934
|
+
file,
|
|
2935
|
+
line,
|
|
2936
|
+
col,
|
|
2937
|
+
signature,
|
|
2938
|
+
docComment: "",
|
|
2939
|
+
// doc-comment extraction lands with ref emission on Day 4
|
|
2940
|
+
scope,
|
|
2941
|
+
text
|
|
2942
|
+
};
|
|
2943
|
+
}
|
|
2944
|
+
var IDENTIFIER_NODE_TYPES;
|
|
2945
|
+
var init_visitor = __esm({
|
|
2946
|
+
"src/codebase-index/tree-sitter/visitor.ts"() {
|
|
2947
|
+
"use strict";
|
|
2948
|
+
init_util();
|
|
2949
|
+
IDENTIFIER_NODE_TYPES = /* @__PURE__ */ new Set([
|
|
2950
|
+
"identifier",
|
|
2951
|
+
"simple_identifier",
|
|
2952
|
+
"type_identifier",
|
|
2953
|
+
"field_identifier",
|
|
2954
|
+
"property_identifier",
|
|
2955
|
+
"name",
|
|
2956
|
+
"word",
|
|
2957
|
+
"variable_name",
|
|
2958
|
+
"constant",
|
|
2959
|
+
"sym"
|
|
2960
|
+
]);
|
|
2961
|
+
}
|
|
2962
|
+
});
|
|
2963
|
+
|
|
2964
|
+
// src/codebase-index/tree-sitter-parser.ts
|
|
2965
|
+
var tree_sitter_parser_exports = {};
|
|
2966
|
+
__export(tree_sitter_parser_exports, {
|
|
2967
|
+
__smokeRootType: () => __smokeRootType,
|
|
2968
|
+
getGrammarWasmPath: () => getGrammarWasmPath,
|
|
2969
|
+
isTreeSitterSupported: () => isTreeSitterSupported,
|
|
2970
|
+
loadTreeSitterLanguage: () => loadTreeSitterLanguage,
|
|
2971
|
+
parseSymbols: () => parseSymbols8,
|
|
2972
|
+
parseTreeSitterAst: () => parseTreeSitterAst
|
|
2973
|
+
});
|
|
2974
|
+
import * as path7 from "node:path";
|
|
2975
|
+
import { fileURLToPath } from "node:url";
|
|
2976
|
+
function optInEnabled(env) {
|
|
2977
|
+
return process.env[env] === "1" || process.env[env] === "true";
|
|
2978
|
+
}
|
|
2979
|
+
function getRuntime() {
|
|
2980
|
+
if (!runtimePromise) {
|
|
2981
|
+
runtimePromise = (async () => {
|
|
2982
|
+
const mod = await import("web-tree-sitter");
|
|
2983
|
+
const init = () => mod.Parser.init({ locateFile: () => RUNTIME_WASM });
|
|
2984
|
+
return { Parser: mod.Parser, Language: mod.Language, init };
|
|
2985
|
+
})();
|
|
2986
|
+
}
|
|
2987
|
+
return runtimePromise;
|
|
2988
|
+
}
|
|
2989
|
+
async function loadLanguage(lang) {
|
|
2990
|
+
const existing = languageCache.get(lang);
|
|
2991
|
+
if (existing) return existing;
|
|
2992
|
+
const promise = (async () => {
|
|
2993
|
+
const grammarName = resolveGrammarName(lang);
|
|
2994
|
+
if (!grammarName) {
|
|
2995
|
+
throw new Error(`tree-sitter: no grammar registered for lang "${lang}"`);
|
|
2996
|
+
}
|
|
2997
|
+
const wasmPath = path7.join(WASM_DIR, grammarName, `tree-sitter-${grammarName}.wasm`);
|
|
2998
|
+
const { Language, init } = await getRuntime();
|
|
2999
|
+
await init();
|
|
3000
|
+
const languageObj = await Language.load(wasmPath);
|
|
3001
|
+
return { lang, Language: languageObj };
|
|
3002
|
+
})();
|
|
3003
|
+
languageCache.set(lang, promise);
|
|
3004
|
+
return promise;
|
|
3005
|
+
}
|
|
3006
|
+
function resolveGrammarName(lang) {
|
|
3007
|
+
if (lang === "go" && optInEnabled(GO_OPT_IN)) return "go";
|
|
3008
|
+
if (lang === "py" && optInEnabled(PY_OPT_IN)) return "python";
|
|
3009
|
+
if (lang === "rs" && optInEnabled(RS_OPT_IN)) return "rust";
|
|
3010
|
+
return LANG_TO_GRAMMAR[lang];
|
|
3011
|
+
}
|
|
3012
|
+
function isTreeSitterSupported(lang) {
|
|
3013
|
+
return resolveGrammarName(lang) !== void 0;
|
|
3014
|
+
}
|
|
3015
|
+
function getGrammarWasmPath(lang) {
|
|
3016
|
+
const name = resolveGrammarName(lang);
|
|
3017
|
+
if (!name) return void 0;
|
|
3018
|
+
return path7.join(WASM_DIR, name, `tree-sitter-${name}.wasm`);
|
|
3019
|
+
}
|
|
3020
|
+
async function parseSymbols8(opts) {
|
|
3021
|
+
const { file, content, lang } = opts;
|
|
3022
|
+
if (!isTreeSitterSupported(lang)) {
|
|
3023
|
+
return { file, lang, symbols: [], mtimeMs: Date.now() };
|
|
3024
|
+
}
|
|
3025
|
+
try {
|
|
3026
|
+
const { Parser } = await getRuntime();
|
|
3027
|
+
const cached = await loadLanguage(lang);
|
|
3028
|
+
const parser = new Parser();
|
|
3029
|
+
parser.setLanguage(cached.Language);
|
|
3030
|
+
const tree = parser.parse(content);
|
|
3031
|
+
if (!tree) {
|
|
3032
|
+
return { file, lang, symbols: [], mtimeMs: Date.now() };
|
|
3033
|
+
}
|
|
3034
|
+
const { symbols, refs } = visitTree(tree, content, file, lang, getQueries(lang));
|
|
3035
|
+
parser.delete();
|
|
3036
|
+
tree.delete();
|
|
3037
|
+
return { file, lang, symbols, refs, mtimeMs: Date.now() };
|
|
3038
|
+
} catch {
|
|
3039
|
+
return { file, lang, symbols: [], mtimeMs: Date.now() };
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
async function loadTreeSitterLanguage(lang) {
|
|
3043
|
+
const cached = await loadLanguage(lang);
|
|
3044
|
+
return cached.Language;
|
|
3045
|
+
}
|
|
3046
|
+
async function __smokeRootType(opts) {
|
|
3047
|
+
if (!isTreeSitterSupported(opts.lang)) {
|
|
3048
|
+
throw new Error(`tree-sitter: no grammar registered for lang "${opts.lang}"`);
|
|
3049
|
+
}
|
|
3050
|
+
const { Parser } = await getRuntime();
|
|
3051
|
+
const cached = await loadLanguage(opts.lang);
|
|
3052
|
+
const parser = new Parser();
|
|
3053
|
+
parser.setLanguage(cached.Language);
|
|
3054
|
+
let tree = null;
|
|
3055
|
+
try {
|
|
3056
|
+
tree = parser.parse(opts.content);
|
|
3057
|
+
if (!tree) throw new Error("tree-sitter: parser.parse returned null");
|
|
3058
|
+
return tree.rootNode.type;
|
|
3059
|
+
} finally {
|
|
3060
|
+
tree?.delete();
|
|
3061
|
+
parser.delete();
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
async function parseTreeSitterAst(opts) {
|
|
3065
|
+
const grammar = resolveGrammarName(opts.lang) ?? (opts.lang === "go" ? "go" : opts.lang === "py" ? "python" : opts.lang === "rs" ? "rust" : void 0);
|
|
3066
|
+
if (!grammar) return null;
|
|
3067
|
+
try {
|
|
3068
|
+
const { Parser, Language, init } = await getRuntime();
|
|
3069
|
+
await init();
|
|
3070
|
+
const wasmPath = path7.join(WASM_DIR, grammar, `tree-sitter-${grammar}.wasm`);
|
|
3071
|
+
const languageObj = await Language.load(wasmPath);
|
|
3072
|
+
const parser = new Parser();
|
|
3073
|
+
parser.setLanguage(languageObj);
|
|
3074
|
+
const tree = parser.parse(opts.content);
|
|
3075
|
+
if (!tree) {
|
|
3076
|
+
parser.delete();
|
|
3077
|
+
return null;
|
|
3078
|
+
}
|
|
3079
|
+
return { tree, parser };
|
|
3080
|
+
} catch {
|
|
3081
|
+
return null;
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
var WASM_DIR, RUNTIME_WASM, LANG_TO_GRAMMAR, GO_OPT_IN, PY_OPT_IN, RS_OPT_IN, runtimePromise, languageCache;
|
|
3085
|
+
var init_tree_sitter_parser = __esm({
|
|
3086
|
+
"src/codebase-index/tree-sitter-parser.ts"() {
|
|
3087
|
+
"use strict";
|
|
3088
|
+
init_queries();
|
|
3089
|
+
init_visitor();
|
|
3090
|
+
WASM_DIR = fileURLToPath(new URL("./wasm/", import.meta.url));
|
|
3091
|
+
RUNTIME_WASM = path7.join(WASM_DIR, "tree-sitter-runtime.wasm");
|
|
3092
|
+
LANG_TO_GRAMMAR = {
|
|
3093
|
+
c: "c",
|
|
3094
|
+
cpp: "cpp",
|
|
3095
|
+
java: "java",
|
|
3096
|
+
csharp: "c_sharp",
|
|
3097
|
+
// tree-sitter directory uses underscore
|
|
3098
|
+
php: "php",
|
|
3099
|
+
ruby: "ruby",
|
|
3100
|
+
swift: "swift",
|
|
3101
|
+
kotlin: "kotlin",
|
|
3102
|
+
shell: "bash",
|
|
3103
|
+
// we treat `.sh` / `.bash` / `.zsh` via the bash grammar
|
|
3104
|
+
// Go/Python/Rust are opt-in only (see goOptIn / pyOptIn / rsOptIn below)
|
|
3105
|
+
elixir: "elixir"
|
|
3106
|
+
};
|
|
3107
|
+
GO_OPT_IN = "WRONGSTACK_USE_TS_GO";
|
|
3108
|
+
PY_OPT_IN = "WRONGSTACK_USE_TS_PY";
|
|
3109
|
+
RS_OPT_IN = "WRONGSTACK_USE_TS_RS";
|
|
3110
|
+
runtimePromise = null;
|
|
3111
|
+
languageCache = /* @__PURE__ */ new Map();
|
|
3112
|
+
}
|
|
3113
|
+
});
|
|
3114
|
+
|
|
3115
|
+
// src/codebase-index/parser-worker-script.ts
|
|
3116
|
+
import { parentPort, threadId } from "node:worker_threads";
|
|
3117
|
+
|
|
3118
|
+
// src/codebase-index/import-extractor.ts
|
|
3119
|
+
var IMPORT_MAX_FILE_CHARS = 512 * 1024;
|
|
3120
|
+
var IMPORT_MAX_PER_FILE = 400;
|
|
3121
|
+
var DOTTED_IMPORT = [
|
|
3122
|
+
{ re: /^[ \t]*import\s+(?:static\s+)?([\w.]+(?:\.\*)?)\s*;?\s*$/gm }
|
|
3123
|
+
];
|
|
3124
|
+
var LANG_IMPORTS = {
|
|
3125
|
+
// Go and Python have real AST extractors; these patterns are the fallback for
|
|
3126
|
+
// machines with no Go toolchain or Python interpreter installed, where the
|
|
3127
|
+
// parser degrades to regex symbols and would otherwise contribute no edges.
|
|
3128
|
+
go: [
|
|
3129
|
+
{ re: /^[ \t]*import\s+(?:[\w.]+\s+)?"([^"]+)"/gm },
|
|
3130
|
+
// Grouped form: inside `import ( … )` each line is an optional alias plus a
|
|
3131
|
+
// quoted path. A stray match elsewhere resolves to no file and is dropped.
|
|
3132
|
+
{ re: /^[ \t]*(?:[A-Za-z_.]\w*\s+)?"([^"]+)"\s*$/gm }
|
|
3133
|
+
],
|
|
3134
|
+
py: [{ re: /^[ \t]*import\s+([\w.]+)/gm }, { re: /^[ \t]*from\s+([.\w]+)\s+import\b/gm }],
|
|
3135
|
+
rs: [
|
|
3136
|
+
// use a::b::C; | use a::b::{C, D}; → the path before any brace
|
|
3137
|
+
{ re: /^[ \t]*(?:pub\s+)?use\s+([\w:]+?)(?:::\{|\s*;|\s+as\b)/gm },
|
|
3138
|
+
// mod foo; (a declaration *and* a dependency on foo.rs / foo/mod.rs)
|
|
3139
|
+
{ re: /^[ \t]*(?:pub\s+)?mod\s+(\w+)\s*;/gm }
|
|
3140
|
+
],
|
|
3141
|
+
java: DOTTED_IMPORT,
|
|
3142
|
+
kotlin: DOTTED_IMPORT,
|
|
3143
|
+
scala: [{ re: /^[ \t]*import\s+([\w.]+(?:\.[_*])?)\s*$/gm }],
|
|
3144
|
+
csharp: [
|
|
3145
|
+
// using Foo.Bar; | using static Foo.Bar; | using Alias = Foo.Bar;
|
|
3146
|
+
{ re: /^[ \t]*global\s+using\s+(?:static\s+)?([\w.]+)\s*;/gm, name: "full" },
|
|
3147
|
+
{ re: /^[ \t]*using\s+(?:static\s+)?(?:\w+\s*=\s*)?([\w.]+)\s*;/gm, name: "full" }
|
|
3148
|
+
],
|
|
3149
|
+
// Quoted includes only: <stdio.h> is a system header with no indexed file.
|
|
3150
|
+
c: [{ re: /^[ \t]*#\s*include\s+"([^"]+)"/gm }],
|
|
3151
|
+
cpp: [{ re: /^[ \t]*#\s*include\s+"([^"]+)"/gm }],
|
|
3152
|
+
ruby: [{ re: /^[ \t]*(?:require|require_relative|load)\s*\(?\s*['"]([^'"]+)['"]/gm }],
|
|
3153
|
+
php: [
|
|
3154
|
+
// `use A\B\C` imports the class C, which is what the index has a symbol
|
|
3155
|
+
// for — the namespace symbol only covers the `A\B` prefix.
|
|
3156
|
+
{ re: /^[ \t]*use\s+(?:function\s+|const\s+)?([\w\\]+)/gm },
|
|
3157
|
+
{ re: /^[ \t]*(?:require|require_once|include|include_once)\s*\(?\s*['"]([^'"]+)['"]/gm }
|
|
3158
|
+
],
|
|
3159
|
+
swift: [{ re: /^[ \t]*import\s+(?:struct\s+|class\s+|func\s+)?([\w.]+)\s*$/gm }],
|
|
3160
|
+
dart: [{ re: /^[ \t]*(?:import|export|part)\s+['"]([^'"]+)['"]/gm }],
|
|
3161
|
+
lua: [{ re: /\brequire\s*\(?\s*['"]([^'"]+)['"]/g }],
|
|
3162
|
+
elixir: [{ re: /^[ \t]*(?:alias|import|require|use)\s+([A-Z][\w.]*)/gm, name: "full" }],
|
|
3163
|
+
haskell: [{ re: /^[ \t]*import\s+(?:qualified\s+)?([\w.]+)/gm, name: "full" }],
|
|
3164
|
+
zig: [{ re: /@import\s*\(\s*"([^"]+)"\s*\)/g }],
|
|
3165
|
+
proto: [{ re: /^[ \t]*import\s+(?:public\s+|weak\s+)?"([^"]+)"\s*;/gm }],
|
|
3166
|
+
// `@import "x"`, `@use "x"`, `@forward "x"` — Sass and plain CSS alike.
|
|
3167
|
+
css: [{ re: /^[ \t]*@(?:import|use|forward)\s+(?:url\()?\s*['"]([^'"]+)['"]/gm }],
|
|
3168
|
+
// A .vue/.svelte file's <script> block is JS; the same ESM syntax applies.
|
|
3169
|
+
vue: [{ re: /^[ \t]*import\s[^'"]*from\s*['"]([^'"]+)['"]/gm }],
|
|
3170
|
+
svelte: [{ re: /^[ \t]*import\s[^'"]*from\s*['"]([^'"]+)['"]/gm }],
|
|
3171
|
+
html: [
|
|
3172
|
+
{ re: /<script[^>]+src\s*=\s*['"]([^'"]+)['"]/g },
|
|
3173
|
+
{ re: /<link[^>]+href\s*=\s*['"]([^'"]+)['"]/g }
|
|
3174
|
+
],
|
|
3175
|
+
shell: [{ re: /^[ \t]*(?:source|\.)\s+(\S+)/gm }],
|
|
3176
|
+
r: [{ re: /\b(?:source|library|require)\s*\(\s*['"]?([\w./-]+)['"]?\s*\)/g }]
|
|
3177
|
+
};
|
|
3178
|
+
function lastSegment(specifier) {
|
|
3179
|
+
const pathLike = /[/\\]|::/.test(specifier);
|
|
3180
|
+
const segments = specifier.split(/[/\\]|::/).filter(Boolean);
|
|
3181
|
+
let last = segments[segments.length - 1] ?? specifier;
|
|
3182
|
+
if (last === "*" || last === "_") {
|
|
3183
|
+
last = segments[segments.length - 2] ?? specifier;
|
|
3184
|
+
}
|
|
3185
|
+
if (pathLike) return last.replace(/\.[A-Za-z0-9]{1,8}$/, "") || last;
|
|
3186
|
+
const dotted = last.split(".").filter((part) => part && part !== "*" && part !== "_");
|
|
3187
|
+
return dotted[dotted.length - 1] ?? last;
|
|
3188
|
+
}
|
|
3189
|
+
function newlineOffsets(content) {
|
|
3190
|
+
const offsets = [];
|
|
3191
|
+
for (let i = 0; i < content.length; i++) {
|
|
3192
|
+
if (content.charCodeAt(i) === 10) offsets.push(i);
|
|
3193
|
+
}
|
|
3194
|
+
return offsets;
|
|
3195
|
+
}
|
|
3196
|
+
function lineAt(offsets, index) {
|
|
3197
|
+
let low = 0;
|
|
3198
|
+
let high = offsets.length;
|
|
3199
|
+
while (low < high) {
|
|
3200
|
+
const mid = low + high >>> 1;
|
|
3201
|
+
if ((offsets[mid] ?? 0) < index) low = mid + 1;
|
|
3202
|
+
else high = mid;
|
|
3203
|
+
}
|
|
3204
|
+
return low + 1;
|
|
3205
|
+
}
|
|
3206
|
+
function hasImportPatterns(lang) {
|
|
3207
|
+
return LANG_IMPORTS[lang] !== void 0;
|
|
3208
|
+
}
|
|
3209
|
+
function extractImports(opts) {
|
|
3210
|
+
const patterns = LANG_IMPORTS[opts.lang];
|
|
3211
|
+
if (!patterns || !opts.content) return [];
|
|
3212
|
+
const limit = opts.maxImports ?? IMPORT_MAX_PER_FILE;
|
|
3213
|
+
const content = opts.content.length > IMPORT_MAX_FILE_CHARS ? opts.content.slice(0, IMPORT_MAX_FILE_CHARS) : opts.content;
|
|
3214
|
+
const refs = [];
|
|
3215
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3216
|
+
const offsets = newlineOffsets(content);
|
|
3217
|
+
for (const pattern of patterns) {
|
|
3218
|
+
const re = new RegExp(pattern.re.source, pattern.re.flags);
|
|
3219
|
+
for (const match of content.matchAll(re)) {
|
|
3220
|
+
if (refs.length >= limit) return refs;
|
|
3221
|
+
const specifier = match[1]?.trim();
|
|
3222
|
+
if (!specifier) continue;
|
|
3223
|
+
const module = specifier;
|
|
3224
|
+
const toName = pattern.name === "full" ? module : lastSegment(module);
|
|
3225
|
+
if (!toName) continue;
|
|
3226
|
+
const key = `${module}\0${toName}`;
|
|
3227
|
+
if (seen.has(key)) continue;
|
|
3228
|
+
seen.add(key);
|
|
3229
|
+
refs.push({
|
|
3230
|
+
fromId: 0,
|
|
3231
|
+
toName,
|
|
3232
|
+
callType: "import",
|
|
3233
|
+
line: lineAt(offsets, match.index ?? 0),
|
|
3234
|
+
lang: opts.lang,
|
|
3235
|
+
module
|
|
3236
|
+
});
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
return refs;
|
|
3240
|
+
}
|
|
3241
|
+
|
|
3242
|
+
// src/codebase-index/parser-batch.ts
|
|
3243
|
+
init_win32_resolve();
|
|
3244
|
+
init_parser_output();
|
|
3245
|
+
init_spawn_gate();
|
|
3246
|
+
import { spawn } from "node:child_process";
|
|
3247
|
+
import * as fsSync from "node:fs";
|
|
3248
|
+
import * as fs2 from "node:fs/promises";
|
|
3249
|
+
import * as os from "node:os";
|
|
3250
|
+
import * as path2 from "node:path";
|
|
3251
|
+
var MAX_BATCH_FILES = 100;
|
|
3252
|
+
var MAX_BATCH_BYTES = 8 * 1024 * 1024;
|
|
3253
|
+
function chunkBatchFiles(files) {
|
|
3254
|
+
const chunks = [];
|
|
3255
|
+
let current = [];
|
|
3256
|
+
let bytes = 0;
|
|
3257
|
+
for (const file of files) {
|
|
3258
|
+
const size = Buffer.byteLength(file.content, "utf8");
|
|
3259
|
+
if (current.length > 0 && (current.length >= MAX_BATCH_FILES || bytes + size > MAX_BATCH_BYTES)) {
|
|
3260
|
+
chunks.push(current);
|
|
3261
|
+
current = [];
|
|
3262
|
+
bytes = 0;
|
|
3263
|
+
}
|
|
3264
|
+
current.push(file);
|
|
3265
|
+
bytes += size;
|
|
3266
|
+
}
|
|
3267
|
+
if (current.length > 0) chunks.push(current);
|
|
3268
|
+
return chunks;
|
|
3269
|
+
}
|
|
3270
|
+
function batchTimeoutMs(fileCount) {
|
|
3271
|
+
return Math.min(12e4, 15e3 + fileCount * 1500);
|
|
3272
|
+
}
|
|
3273
|
+
var GO_BATCH_SCRIPT = `
|
|
3274
|
+
package main
|
|
3275
|
+
|
|
3276
|
+
import (
|
|
3277
|
+
"encoding/json"
|
|
3278
|
+
"fmt"
|
|
3279
|
+
"go/ast"
|
|
3280
|
+
"go/parser"
|
|
3281
|
+
"go/token"
|
|
3282
|
+
"io"
|
|
3283
|
+
"os"
|
|
3284
|
+
"strconv"
|
|
3285
|
+
"strings"
|
|
3286
|
+
)
|
|
3287
|
+
|
|
3288
|
+
type Sym struct {
|
|
3289
|
+
Name string \`json:"name"\`
|
|
3290
|
+
Kind string \`json:"kind"\`
|
|
3291
|
+
Line int \`json:"line"\`
|
|
3292
|
+
Col int \`json:"col"\`
|
|
3293
|
+
Signature string \`json:"signature"\`
|
|
3294
|
+
Scope string \`json:"scope"\`
|
|
3295
|
+
}
|
|
3296
|
+
|
|
3297
|
+
type Ref struct {
|
|
3298
|
+
ToName string \`json:"toName"\`
|
|
3299
|
+
CallType string \`json:"callType"\`
|
|
3300
|
+
Line int \`json:"line"\`
|
|
3301
|
+
Module string \`json:"module"\`
|
|
3302
|
+
}
|
|
3303
|
+
|
|
3304
|
+
type FileResult struct {
|
|
3305
|
+
File string \`json:"file"\`
|
|
3306
|
+
Error string \`json:"error,omitempty"\`
|
|
3307
|
+
Symbols []Sym \`json:"symbols"\`
|
|
3308
|
+
Refs []Ref \`json:"refs"\`
|
|
3309
|
+
}
|
|
3310
|
+
|
|
3311
|
+
type BatchResult struct {
|
|
3312
|
+
Version int \`json:"version"\`
|
|
3313
|
+
Results []FileResult \`json:"results"\`
|
|
3314
|
+
}
|
|
3315
|
+
|
|
3316
|
+
type inputFile struct {
|
|
3317
|
+
File string \`json:"file"\`
|
|
3318
|
+
Content string \`json:"content"\`
|
|
3319
|
+
}
|
|
3320
|
+
|
|
3321
|
+
func parseOne(name string, src []byte) FileResult {
|
|
3322
|
+
res := FileResult{File: name, Symbols: []Sym{}, Refs: []Ref{}}
|
|
3323
|
+
fset := token.NewFileSet()
|
|
3324
|
+
node, err := parser.ParseFile(fset, "src.go", src, 0)
|
|
3325
|
+
if err != nil {
|
|
3326
|
+
res.Error = err.Error()
|
|
3327
|
+
return res
|
|
3328
|
+
}
|
|
3329
|
+
pkgScope := node.Name.Name
|
|
3330
|
+
for _, decl := range node.Decls {
|
|
3331
|
+
switch d := decl.(type) {
|
|
3332
|
+
case *ast.FuncDecl:
|
|
3333
|
+
symName := d.Name.Name
|
|
3334
|
+
kind := "function"
|
|
3335
|
+
scope := pkgScope
|
|
3336
|
+
if d.Recv != nil && len(d.Recv.List) > 0 {
|
|
3337
|
+
scope = pkgScope + "." + recvTypeName(d.Recv.List[0].Type) + "." + symName
|
|
3338
|
+
kind = "method"
|
|
3339
|
+
} else {
|
|
3340
|
+
scope = pkgScope + "." + symName
|
|
3341
|
+
}
|
|
3342
|
+
pos := fset.Position(d.Pos())
|
|
3343
|
+
res.Symbols = append(res.Symbols, Sym{Name: symName, Kind: kind, Line: pos.Line, Col: pos.Column, Signature: formatFuncSig(d), Scope: scope})
|
|
3344
|
+
case *ast.GenDecl:
|
|
3345
|
+
for _, spec := range d.Specs {
|
|
3346
|
+
switch s := spec.(type) {
|
|
3347
|
+
case *ast.TypeSpec:
|
|
3348
|
+
typeName := s.Name.Name
|
|
3349
|
+
pos := fset.Position(s.Pos())
|
|
3350
|
+
sig := "type " + typeName
|
|
3351
|
+
if s.TypeParams != nil {
|
|
3352
|
+
sig += formatTypeParams(s.TypeParams)
|
|
3353
|
+
}
|
|
3354
|
+
if st, ok := s.Type.(*ast.StructType); ok {
|
|
3355
|
+
sig += " = struct { " + formatFields(st.Fields.List) + " }"
|
|
3356
|
+
} else if it, ok := s.Type.(*ast.InterfaceType); ok {
|
|
3357
|
+
sig += " = interface { " + formatMethods(it.Methods.List) + " }"
|
|
3358
|
+
} else {
|
|
3359
|
+
sig += " = " + formatType(s.Type)
|
|
3360
|
+
}
|
|
3361
|
+
res.Symbols = append(res.Symbols, Sym{Name: typeName, Kind: "type", Line: pos.Line, Col: pos.Column, Signature: sig, Scope: pkgScope})
|
|
3362
|
+
case *ast.ValueSpec:
|
|
3363
|
+
for _, n := range s.Names {
|
|
3364
|
+
valueName := n.Name
|
|
3365
|
+
pos := fset.Position(n.Pos())
|
|
3366
|
+
kind := "var"
|
|
3367
|
+
if d.Tok == token.CONST {
|
|
3368
|
+
kind = "const"
|
|
3369
|
+
}
|
|
3370
|
+
sig := kind + " " + valueName
|
|
3371
|
+
if s.Type != nil {
|
|
3372
|
+
sig += " " + formatType(s.Type)
|
|
3373
|
+
}
|
|
3374
|
+
res.Symbols = append(res.Symbols, Sym{Name: valueName, Kind: kind, Line: pos.Line, Col: pos.Column, Signature: sig, Scope: pkgScope})
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
ast.Inspect(node, func(n ast.Node) bool {
|
|
3381
|
+
switch expr := n.(type) {
|
|
3382
|
+
case *ast.CallExpr:
|
|
3383
|
+
line := fset.Position(expr.Pos()).Line
|
|
3384
|
+
switch fun := expr.Fun.(type) {
|
|
3385
|
+
case *ast.Ident:
|
|
3386
|
+
res.Refs = append(res.Refs, Ref{ToName: fun.Name, CallType: "call", Line: line})
|
|
3387
|
+
case *ast.SelectorExpr:
|
|
3388
|
+
res.Refs = append(res.Refs, Ref{ToName: fun.Sel.Name, CallType: "call", Line: line})
|
|
3389
|
+
}
|
|
3390
|
+
case *ast.ImportSpec:
|
|
3391
|
+
if expr.Path != nil {
|
|
3392
|
+
if importPath, uerr := strconv.Unquote(expr.Path.Value); uerr == nil {
|
|
3393
|
+
line := fset.Position(expr.Pos()).Line
|
|
3394
|
+
name := importPath
|
|
3395
|
+
if idx := strings.LastIndex(importPath, "/"); idx >= 0 {
|
|
3396
|
+
name = importPath[idx+1:]
|
|
3397
|
+
}
|
|
3398
|
+
res.Refs = append(res.Refs, Ref{ToName: name, CallType: "import", Line: line, Module: importPath})
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
return true
|
|
3403
|
+
})
|
|
3404
|
+
return res
|
|
3405
|
+
}
|
|
3406
|
+
|
|
3407
|
+
func main() {
|
|
3408
|
+
raw, err := io.ReadAll(os.Stdin)
|
|
3409
|
+
if err != nil {
|
|
3410
|
+
out, _ := json.Marshal(BatchResult{Version: 1, Results: []FileResult{}})
|
|
3411
|
+
fmt.Print(string(out))
|
|
3412
|
+
return
|
|
3413
|
+
}
|
|
3414
|
+
var inputs []inputFile
|
|
3415
|
+
if err := json.Unmarshal(raw, &inputs); err != nil {
|
|
3416
|
+
out, _ := json.Marshal(BatchResult{Version: 1, Results: []FileResult{}})
|
|
3417
|
+
fmt.Print(string(out))
|
|
3418
|
+
return
|
|
3419
|
+
}
|
|
3420
|
+
results := make([]FileResult, 0, len(inputs))
|
|
3421
|
+
for _, in := range inputs {
|
|
3422
|
+
results = append(results, parseOne(in.File, []byte(in.Content)))
|
|
3423
|
+
}
|
|
3424
|
+
data, err := json.Marshal(BatchResult{Version: 1, Results: results})
|
|
3425
|
+
if err != nil {
|
|
3426
|
+
fmt.Print("{\\"version\\":1,\\"results\\":[]}")
|
|
3427
|
+
return
|
|
3428
|
+
}
|
|
3429
|
+
fmt.Print(string(data))
|
|
3430
|
+
}
|
|
3431
|
+
|
|
3432
|
+
func recvTypeName(t ast.Expr) string {
|
|
3433
|
+
switch v := t.(type) {
|
|
3434
|
+
case *ast.Ident:
|
|
3435
|
+
return v.Name
|
|
3436
|
+
case *ast.StarExpr:
|
|
3437
|
+
return recvTypeName(v.X)
|
|
3438
|
+
default:
|
|
3439
|
+
return "?"
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
|
|
3443
|
+
func formatFuncSig(d *ast.FuncDecl) string {
|
|
3444
|
+
scope := ""
|
|
3445
|
+
if d.Recv != nil && len(d.Recv.List) > 0 {
|
|
3446
|
+
scope = "(" + formatFieldList(d.Recv.List) + ") "
|
|
3447
|
+
}
|
|
3448
|
+
scope += formatFuncType(d.Type)
|
|
3449
|
+
return "func " + scope
|
|
3450
|
+
}
|
|
3451
|
+
|
|
3452
|
+
func formatFuncType(f *ast.FuncType) string {
|
|
3453
|
+
params := formatFieldList(f.Params.List)
|
|
3454
|
+
results := ""
|
|
3455
|
+
if f.Results != nil {
|
|
3456
|
+
results = " -> " + formatFieldList(f.Results.List)
|
|
3457
|
+
}
|
|
3458
|
+
return params + results
|
|
3459
|
+
}
|
|
3460
|
+
|
|
3461
|
+
func formatFieldList(fields []*ast.Field) string {
|
|
3462
|
+
if len(fields) == 0 {
|
|
3463
|
+
return "()"
|
|
3464
|
+
}
|
|
3465
|
+
names := make([]string, 0, len(fields))
|
|
3466
|
+
for _, f := range fields {
|
|
3467
|
+
name := ""
|
|
3468
|
+
if len(f.Names) > 0 {
|
|
3469
|
+
name = f.Names[0].Name
|
|
3470
|
+
}
|
|
3471
|
+
t := formatType(f.Type)
|
|
3472
|
+
if name != "" {
|
|
3473
|
+
names = append(names, name+" "+t)
|
|
3474
|
+
} else {
|
|
3475
|
+
names = append(names, t)
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3478
|
+
return "(" + strings.Join(names, ", ") + ")"
|
|
3479
|
+
}
|
|
3480
|
+
|
|
3481
|
+
func formatFields(fields []*ast.Field) string {
|
|
3482
|
+
lines := make([]string, 0)
|
|
3483
|
+
for _, f := range fields {
|
|
3484
|
+
name := ""
|
|
3485
|
+
if len(f.Names) > 0 {
|
|
3486
|
+
name = f.Names[0].Name
|
|
3487
|
+
}
|
|
3488
|
+
t := formatType(f.Type)
|
|
3489
|
+
if name != "" {
|
|
3490
|
+
lines = append(lines, name+" "+t)
|
|
3491
|
+
} else {
|
|
3492
|
+
lines = append(lines, t)
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
return strings.Join(lines, "; ")
|
|
3496
|
+
}
|
|
3497
|
+
|
|
3498
|
+
func formatMethods(fields []*ast.Field) string {
|
|
3499
|
+
return formatFields(fields)
|
|
3500
|
+
}
|
|
3501
|
+
|
|
3502
|
+
func formatTypeParams(tp *ast.FieldList) string {
|
|
3503
|
+
if tp == nil || len(tp.List) == 0 {
|
|
3504
|
+
return ""
|
|
3505
|
+
}
|
|
3506
|
+
params := make([]string, len(tp.List))
|
|
3507
|
+
for i, p := range tp.List {
|
|
3508
|
+
if len(p.Names) > 0 {
|
|
3509
|
+
params[i] = p.Names[0].Name
|
|
3510
|
+
} else {
|
|
3511
|
+
params[i] = "T"
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
return "[" + strings.Join(params, ", ") + "]"
|
|
3515
|
+
}
|
|
3516
|
+
|
|
3517
|
+
func formatType(t ast.Expr) string {
|
|
3518
|
+
if t == nil {
|
|
3519
|
+
return "?"
|
|
3520
|
+
}
|
|
3521
|
+
switch v := t.(type) {
|
|
3522
|
+
case *ast.Ident:
|
|
3523
|
+
return v.Name
|
|
3524
|
+
case *ast.SelectorExpr:
|
|
3525
|
+
return formatType(v.X) + "." + v.Sel.Name
|
|
3526
|
+
case *ast.StarExpr:
|
|
3527
|
+
return "*" + formatType(v.X)
|
|
3528
|
+
case *ast.ArrayType:
|
|
3529
|
+
if v.Len == nil {
|
|
3530
|
+
return "[]" + formatType(v.Elt)
|
|
3531
|
+
}
|
|
3532
|
+
return "[...]" + formatType(v.Elt)
|
|
3533
|
+
case *ast.MapType:
|
|
3534
|
+
return "map[" + formatType(v.Key) + "]" + formatType(v.Value)
|
|
3535
|
+
case *ast.InterfaceType:
|
|
3536
|
+
return "interface{}"
|
|
3537
|
+
case *ast.StructType:
|
|
3538
|
+
return "struct{}"
|
|
3539
|
+
case *ast.FuncType:
|
|
3540
|
+
return formatFuncType(v)
|
|
3541
|
+
case *ast.ChanType:
|
|
3542
|
+
return "chan " + formatType(v.Value)
|
|
3543
|
+
case *ast.BasicLit:
|
|
3544
|
+
return v.Value
|
|
3545
|
+
case *ast.IndexExpr:
|
|
3546
|
+
return formatType(v.X) + "[" + formatType(v.Index) + "]"
|
|
3547
|
+
case *ast.IndexListExpr:
|
|
3548
|
+
args := make([]string, len(v.Indices))
|
|
3549
|
+
for i, idx := range v.Indices {
|
|
3550
|
+
args[i] = formatType(idx)
|
|
3551
|
+
}
|
|
3552
|
+
return formatType(v.X) + "[" + strings.Join(args, ", ") + "]"
|
|
3553
|
+
default:
|
|
3554
|
+
return "?"
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3557
|
+
`;
|
|
3558
|
+
var PY_BATCH_SCRIPT = `import ast, json, sys
|
|
3559
|
+
|
|
3560
|
+
def get_name(node):
|
|
3561
|
+
if isinstance(node, ast.Name):
|
|
3562
|
+
return node.id
|
|
3563
|
+
elif isinstance(node, ast.Attribute):
|
|
3564
|
+
return get_name(node.value) + "." + node.attr
|
|
3565
|
+
elif isinstance(node, ast.Subscript):
|
|
3566
|
+
return get_name(node.value)
|
|
3567
|
+
elif isinstance(node, ast.Call):
|
|
3568
|
+
return get_name(node.func)
|
|
3569
|
+
elif isinstance(node, ast.Constant):
|
|
3570
|
+
return str(node.value)
|
|
3571
|
+
return ""
|
|
3572
|
+
|
|
3573
|
+
def leaf_name(node):
|
|
3574
|
+
if isinstance(node, ast.Attribute):
|
|
3575
|
+
return node.attr
|
|
3576
|
+
if isinstance(node, ast.Name):
|
|
3577
|
+
return node.id
|
|
3578
|
+
return get_name(node).split(".")[-1]
|
|
3579
|
+
|
|
3580
|
+
def is_private(name):
|
|
3581
|
+
return name.startswith("__") and not name.endswith("__")
|
|
3582
|
+
|
|
3583
|
+
def parse_one(name, source, module_name):
|
|
3584
|
+
result = {"file": name, "symbols": [], "refs": []}
|
|
3585
|
+
try:
|
|
3586
|
+
tree = ast.parse(source, filename=name)
|
|
3587
|
+
except Exception as e:
|
|
3588
|
+
result["error"] = str(e)
|
|
3589
|
+
return result
|
|
3590
|
+
syms = []
|
|
3591
|
+
refs = []
|
|
3592
|
+
scope_stack = [module_name]
|
|
3593
|
+
|
|
3594
|
+
def sym(d):
|
|
3595
|
+
return {
|
|
3596
|
+
"name": d["name"], "kind": d["kind"], "line": d["line"], "col": d["col"],
|
|
3597
|
+
"signature": d["signature"], "scope": d["scope"],
|
|
3598
|
+
}
|
|
3599
|
+
|
|
3600
|
+
class Visitor(ast.NodeVisitor):
|
|
3601
|
+
def visit_ClassDef(self, node):
|
|
3602
|
+
bases = [get_name(b) for b in node.bases]
|
|
3603
|
+
sig = "class " + node.name
|
|
3604
|
+
if bases:
|
|
3605
|
+
sig += "(" + ", ".join(bases) + ")"
|
|
3606
|
+
sig += ": ..."
|
|
3607
|
+
syms.append(sym({
|
|
3608
|
+
"name": node.name, "kind": "class", "line": node.lineno,
|
|
3609
|
+
"col": node.col_offset, "signature": sig,
|
|
3610
|
+
"scope": ".".join(scope_stack) + "." + node.name,
|
|
3611
|
+
}))
|
|
3612
|
+
scope_stack.append(node.name)
|
|
3613
|
+
self.generic_visit(node)
|
|
3614
|
+
scope_stack.pop()
|
|
3615
|
+
|
|
3616
|
+
def visit_FunctionDef(self, node):
|
|
3617
|
+
args = ", ".join(a.arg for a in node.args.args)
|
|
3618
|
+
returns = get_name(node.returns) if node.returns is not None else ""
|
|
3619
|
+
is_async = isinstance(node, ast.AsyncFunctionDef)
|
|
3620
|
+
kind = "function"
|
|
3621
|
+
prefix = "def "
|
|
3622
|
+
for dec in node.decorator_list:
|
|
3623
|
+
d = get_name(dec)
|
|
3624
|
+
if d.endswith(".staticmethod"):
|
|
3625
|
+
kind = "staticmethod"
|
|
3626
|
+
elif d.endswith(".classmethod"):
|
|
3627
|
+
kind = "classmethod"
|
|
3628
|
+
elif d == "property":
|
|
3629
|
+
kind = "property"
|
|
3630
|
+
if is_async:
|
|
3631
|
+
kind = "async_" + kind
|
|
3632
|
+
sig = f"{prefix}{node.name}({args})"
|
|
3633
|
+
if returns:
|
|
3634
|
+
sig += f" -> {returns}"
|
|
3635
|
+
syms.append(sym({
|
|
3636
|
+
"name": node.name, "kind": kind, "line": node.lineno,
|
|
3637
|
+
"col": node.col_offset, "signature": sig,
|
|
3638
|
+
"scope": ".".join(scope_stack) + "." + node.name,
|
|
3639
|
+
}))
|
|
3640
|
+
|
|
3641
|
+
def visit_AsyncFunctionDef(self, node):
|
|
3642
|
+
self.visit_FunctionDef(node)
|
|
3643
|
+
|
|
3644
|
+
def visit_Assign(self, node):
|
|
3645
|
+
for target in node.targets:
|
|
3646
|
+
if isinstance(target, ast.Name):
|
|
3647
|
+
tname = target.id
|
|
3648
|
+
if is_private(tname):
|
|
3649
|
+
continue
|
|
3650
|
+
kind = "const" if tname.isupper() else "var"
|
|
3651
|
+
col = target.col_offset if hasattr(target, "col_offset") else 0
|
|
3652
|
+
syms.append(sym({
|
|
3653
|
+
"name": tname, "kind": kind, "line": node.lineno, "col": col,
|
|
3654
|
+
"signature": f"{tname} = ...", "scope": ".".join(scope_stack),
|
|
3655
|
+
}))
|
|
3656
|
+
|
|
3657
|
+
def visit_AnnAssign(self, node):
|
|
3658
|
+
if isinstance(node.target, ast.Name):
|
|
3659
|
+
tname = node.target.id
|
|
3660
|
+
if is_private(tname):
|
|
3661
|
+
return
|
|
3662
|
+
kind = "const" if tname.isupper() else "var"
|
|
3663
|
+
col = node.target.col_offset if hasattr(node.target, "col_offset") else 0
|
|
3664
|
+
sig = f"{tname}: {get_name(node.annotation)}"
|
|
3665
|
+
if node.value:
|
|
3666
|
+
sig += " = ..."
|
|
3667
|
+
syms.append(sym({
|
|
3668
|
+
"name": tname, "kind": kind, "line": node.lineno, "col": col,
|
|
3669
|
+
"signature": sig, "scope": ".".join(scope_stack),
|
|
3670
|
+
}))
|
|
3671
|
+
|
|
3672
|
+
def visit_Import(self, node):
|
|
3673
|
+
# Parity with the single-file parser: imports are symbols too.
|
|
3674
|
+
for alias in node.names:
|
|
3675
|
+
name = alias.asname or alias.name
|
|
3676
|
+
syms.append(sym({
|
|
3677
|
+
"name": name, "kind": "import", "line": node.lineno,
|
|
3678
|
+
"col": node.col_offset, "signature": f"import {alias.name}",
|
|
3679
|
+
"scope": ".".join(scope_stack),
|
|
3680
|
+
}))
|
|
3681
|
+
|
|
3682
|
+
def visit_ImportFrom(self, node):
|
|
3683
|
+
module = node.module or ""
|
|
3684
|
+
for alias in node.names:
|
|
3685
|
+
name = alias.asname or alias.name
|
|
3686
|
+
syms.append(sym({
|
|
3687
|
+
"name": name, "kind": "import", "line": node.lineno,
|
|
3688
|
+
"col": node.col_offset, "signature": f"from {module} import {alias.name}",
|
|
3689
|
+
"scope": ".".join(scope_stack),
|
|
3690
|
+
}))
|
|
3691
|
+
|
|
3692
|
+
Visitor().visit(tree)
|
|
3693
|
+
|
|
3694
|
+
for node in ast.walk(tree):
|
|
3695
|
+
if isinstance(node, ast.Call):
|
|
3696
|
+
cname = leaf_name(node.func)
|
|
3697
|
+
if cname:
|
|
3698
|
+
refs.append({"toName": cname, "callType": "call", "line": node.lineno})
|
|
3699
|
+
elif isinstance(node, ast.Import):
|
|
3700
|
+
for alias in node.names:
|
|
3701
|
+
refs.append({
|
|
3702
|
+
"toName": alias.name.split(".")[-1], "callType": "import",
|
|
3703
|
+
"line": node.lineno, "module": alias.name,
|
|
3704
|
+
})
|
|
3705
|
+
elif isinstance(node, ast.ImportFrom):
|
|
3706
|
+
module = ("." * (node.level or 0)) + (node.module or "")
|
|
3707
|
+
for alias in node.names:
|
|
3708
|
+
refs.append({
|
|
3709
|
+
"toName": alias.name, "callType": "import",
|
|
3710
|
+
"line": node.lineno, "module": module,
|
|
3711
|
+
})
|
|
3712
|
+
elif isinstance(node, ast.ClassDef):
|
|
3713
|
+
for base in node.bases:
|
|
3714
|
+
bname = leaf_name(base)
|
|
3715
|
+
if bname:
|
|
3716
|
+
refs.append({"toName": bname, "callType": "inherit", "line": node.lineno})
|
|
3717
|
+
|
|
3718
|
+
result["symbols"] = syms
|
|
3719
|
+
result["refs"] = refs
|
|
3720
|
+
return result
|
|
3721
|
+
|
|
3722
|
+
def main():
|
|
3723
|
+
try:
|
|
3724
|
+
inputs = json.loads(sys.stdin.read())
|
|
3725
|
+
except Exception:
|
|
3726
|
+
print(json.dumps({"version": 1, "results": []}))
|
|
3727
|
+
return
|
|
3728
|
+
results = []
|
|
3729
|
+
for entry in inputs:
|
|
3730
|
+
name = entry.get("file", "")
|
|
3731
|
+
module_name = name.rsplit("/", 1)[-1].rsplit("\\\\", 1)[-1][:-3]
|
|
3732
|
+
results.append(parse_one(name, entry.get("content", ""), module_name))
|
|
3733
|
+
print(json.dumps({"version": 1, "results": results}))
|
|
3734
|
+
|
|
3735
|
+
main()
|
|
3736
|
+
`;
|
|
3737
|
+
var _goBatchScriptPath = null;
|
|
3738
|
+
var _pyBatchScriptPath = null;
|
|
3739
|
+
async function ensureScriptPath(cached, prefix, fileName, script) {
|
|
3740
|
+
if (cached) return { path: cached, wrote: false };
|
|
3741
|
+
const dir = await fs2.mkdtemp(path2.join(os.tmpdir(), prefix));
|
|
3742
|
+
const scriptPath = path2.join(dir, fileName);
|
|
3743
|
+
await fs2.writeFile(scriptPath, script, { encoding: "utf8", flag: "wx" });
|
|
3744
|
+
process.once("exit", () => {
|
|
3745
|
+
try {
|
|
3746
|
+
fsSync.rmSync(dir, { recursive: true, force: true });
|
|
3747
|
+
} catch {
|
|
3748
|
+
}
|
|
3749
|
+
});
|
|
3750
|
+
return { path: scriptPath, wrote: true };
|
|
3751
|
+
}
|
|
3752
|
+
function runToolchainChild(binary, args, stdinPayload, timeoutMs) {
|
|
3753
|
+
return new Promise((resolve) => {
|
|
3754
|
+
let settled = false;
|
|
3755
|
+
let stdout = "";
|
|
3756
|
+
let proc;
|
|
3757
|
+
try {
|
|
3758
|
+
proc = spawn(binary, args, { stdio: ["pipe", "pipe", "pipe"], windowsHide: true });
|
|
3759
|
+
} catch {
|
|
3760
|
+
resolve(null);
|
|
3761
|
+
return;
|
|
3762
|
+
}
|
|
3763
|
+
const finish = (value) => {
|
|
3764
|
+
if (settled) return;
|
|
3765
|
+
settled = true;
|
|
3766
|
+
clearTimeout(timer);
|
|
3767
|
+
resolve(value);
|
|
3768
|
+
};
|
|
3769
|
+
const timer = setTimeout(() => {
|
|
3770
|
+
proc.kill("SIGKILL");
|
|
3771
|
+
finish(null);
|
|
3772
|
+
}, timeoutMs);
|
|
3773
|
+
timer.unref?.();
|
|
3774
|
+
proc.on("error", () => finish(null));
|
|
3775
|
+
proc.stdout?.on("data", (chunk) => {
|
|
3776
|
+
stdout += chunk.toString();
|
|
3777
|
+
});
|
|
3778
|
+
proc.stderr?.resume();
|
|
3779
|
+
proc.stdin?.on("error", () => {
|
|
3780
|
+
});
|
|
3781
|
+
proc.stdin?.write(stdinPayload);
|
|
3782
|
+
proc.stdin?.end();
|
|
3783
|
+
proc.on("close", (code) => finish({ code, stdout }));
|
|
3784
|
+
});
|
|
3785
|
+
}
|
|
3786
|
+
async function runGoBatch(files, goBinary) {
|
|
3787
|
+
const out = /* @__PURE__ */ new Map();
|
|
3788
|
+
if (files.length === 0) return out;
|
|
3789
|
+
const { path: scriptPath } = await ensureScriptPath(
|
|
3790
|
+
_goBatchScriptPath,
|
|
3791
|
+
"ws-go-parse",
|
|
3792
|
+
"batch.go",
|
|
3793
|
+
GO_BATCH_SCRIPT
|
|
3794
|
+
);
|
|
3795
|
+
_goBatchScriptPath = scriptPath;
|
|
3796
|
+
const payload = JSON.stringify(files.map((f) => ({ file: f.file, content: f.content })));
|
|
3797
|
+
const result = await withSpawnGate(
|
|
3798
|
+
() => runToolchainChild(
|
|
3799
|
+
goBinary ?? resolveWin32Command("go"),
|
|
3800
|
+
["run", scriptPath],
|
|
3801
|
+
payload,
|
|
3802
|
+
batchTimeoutMs(files.length)
|
|
3803
|
+
)
|
|
3804
|
+
);
|
|
3805
|
+
if (result?.code !== 0 || !result.stdout.trim()) return out;
|
|
3806
|
+
for (const entry of parseParserBatchOutput(result.stdout, "go")) {
|
|
3807
|
+
if (entry.error !== void 0) continue;
|
|
3808
|
+
out.set(entry.file, {
|
|
3809
|
+
file: entry.file,
|
|
3810
|
+
lang: "go",
|
|
3811
|
+
symbols: entry.symbols.map((s) => ({
|
|
3812
|
+
id: 0,
|
|
3813
|
+
lang: "go",
|
|
3814
|
+
kind: s.kind,
|
|
3815
|
+
name: s.name,
|
|
3816
|
+
file: entry.file,
|
|
3817
|
+
line: s.line,
|
|
3818
|
+
col: s.col,
|
|
3819
|
+
signature: s.signature ?? "",
|
|
3820
|
+
docComment: "",
|
|
3821
|
+
scope: s.scope ?? "",
|
|
3822
|
+
text: `${s.name} ${s.signature ?? ""}`.trim()
|
|
3823
|
+
})),
|
|
3824
|
+
refs: entry.refs,
|
|
3825
|
+
mtimeMs: Date.now()
|
|
3826
|
+
});
|
|
3827
|
+
}
|
|
3828
|
+
return out;
|
|
3829
|
+
}
|
|
3830
|
+
async function runPyBatch(files, pythonBinary) {
|
|
3831
|
+
const out = /* @__PURE__ */ new Map();
|
|
3832
|
+
if (files.length === 0) return out;
|
|
3833
|
+
const { path: scriptPath } = await ensureScriptPath(
|
|
3834
|
+
_pyBatchScriptPath,
|
|
3835
|
+
"ws-py-parse",
|
|
3836
|
+
"batch.py",
|
|
3837
|
+
PY_BATCH_SCRIPT
|
|
3838
|
+
);
|
|
3839
|
+
_pyBatchScriptPath = scriptPath;
|
|
3840
|
+
const payload = JSON.stringify(files.map((f) => ({ file: f.file, content: f.content })));
|
|
3841
|
+
const result = await withSpawnGate(
|
|
3842
|
+
() => runToolchainChild(pythonBinary, [scriptPath], payload, batchTimeoutMs(files.length))
|
|
3843
|
+
);
|
|
3844
|
+
if (result?.code !== 0 || !result.stdout.trim()) return out;
|
|
3845
|
+
for (const entry of parseParserBatchOutput(result.stdout, "py")) {
|
|
3846
|
+
if (entry.error !== void 0) continue;
|
|
3847
|
+
out.set(entry.file, {
|
|
3848
|
+
file: entry.file,
|
|
3849
|
+
lang: "py",
|
|
3850
|
+
symbols: entry.symbols.map((s) => ({
|
|
3851
|
+
id: 0,
|
|
3852
|
+
lang: "py",
|
|
3853
|
+
kind: s.kind,
|
|
3854
|
+
name: s.name,
|
|
3855
|
+
file: entry.file,
|
|
3856
|
+
line: s.line,
|
|
3857
|
+
col: s.col,
|
|
3858
|
+
signature: s.signature ?? "",
|
|
3859
|
+
docComment: "",
|
|
3860
|
+
scope: s.scope ?? "",
|
|
3861
|
+
text: `${s.name} ${s.signature ?? ""}`.trim()
|
|
3862
|
+
})),
|
|
3863
|
+
refs: entry.refs,
|
|
3864
|
+
mtimeMs: Date.now()
|
|
3865
|
+
});
|
|
3866
|
+
}
|
|
3867
|
+
return out;
|
|
3868
|
+
}
|
|
3869
|
+
|
|
3870
|
+
// src/codebase-index/parser-dispatch.ts
|
|
3871
|
+
init_py_parser();
|
|
3872
|
+
async function parseFileContent(file, content, lang) {
|
|
3873
|
+
const parsed = await dispatch(file, content, lang);
|
|
3874
|
+
return withRelations(parsed, content, lang);
|
|
3875
|
+
}
|
|
3876
|
+
async function parseFilesContent(files) {
|
|
3877
|
+
if (files.length === 0) return [];
|
|
3878
|
+
const slots = files.map(() => ({ result: null }));
|
|
3879
|
+
const batchingEnabled = process.env["WRONGSTACK_TOOLCHAIN_BATCH"] !== "0";
|
|
3880
|
+
if (batchingEnabled) {
|
|
3881
|
+
const goFiles = [];
|
|
3882
|
+
const pyFiles = [];
|
|
3883
|
+
files.forEach((f, index) => {
|
|
3884
|
+
if (f.lang === "go") goFiles.push({ ...f, index });
|
|
3885
|
+
else if (f.lang === "py") pyFiles.push({ ...f, index });
|
|
3886
|
+
});
|
|
3887
|
+
if (goFiles.length > 0) {
|
|
3888
|
+
await applyBatchResults(slots, goFiles, (chunks) => runGoBatch(chunks), "go");
|
|
3889
|
+
}
|
|
3890
|
+
if (pyFiles.length > 0) {
|
|
3891
|
+
const pyBinary = await resolvePythonBinary();
|
|
3892
|
+
if (pyBinary) {
|
|
3893
|
+
await applyBatchResults(slots, pyFiles, (chunks) => runPyBatch(chunks, pyBinary), "py");
|
|
3894
|
+
}
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
const jobs = [];
|
|
3898
|
+
for (let i = 0; i < files.length; i++) {
|
|
3899
|
+
if (slots[i].result !== null) continue;
|
|
3900
|
+
const { file, content, lang } = files[i];
|
|
3901
|
+
const slot = slots[i];
|
|
3902
|
+
jobs.push(
|
|
3903
|
+
(async () => {
|
|
3904
|
+
try {
|
|
3905
|
+
slot.result = await parseFileContent(file, content, lang);
|
|
3906
|
+
} catch (err) {
|
|
3907
|
+
slot.error = err instanceof Error ? err.message : String(err);
|
|
3908
|
+
}
|
|
3909
|
+
})()
|
|
3910
|
+
);
|
|
3911
|
+
}
|
|
3912
|
+
await Promise.all(jobs);
|
|
3913
|
+
return slots;
|
|
3914
|
+
}
|
|
3915
|
+
async function applyBatchResults(slots, batchFiles, runBatch, lang) {
|
|
3916
|
+
for (const chunk of chunkBatchFiles(batchFiles)) {
|
|
3917
|
+
let byFile = null;
|
|
3918
|
+
try {
|
|
3919
|
+
byFile = await runBatch(chunk);
|
|
3920
|
+
} catch {
|
|
3921
|
+
byFile = null;
|
|
3922
|
+
}
|
|
3923
|
+
if (!byFile) continue;
|
|
3924
|
+
for (const item of chunk) {
|
|
3925
|
+
const parsed = byFile.get(item.file);
|
|
3926
|
+
if (!parsed) continue;
|
|
3927
|
+
slots[item.index] = { result: withRelations(parsed, item.content, lang) };
|
|
3928
|
+
}
|
|
3929
|
+
}
|
|
3930
|
+
}
|
|
3931
|
+
async function dispatch(file, content, lang) {
|
|
3932
|
+
switch (lang) {
|
|
3933
|
+
case "ts":
|
|
3934
|
+
case "tsx":
|
|
3935
|
+
case "js":
|
|
3936
|
+
case "jsx": {
|
|
3937
|
+
const { parseSymbols: parseSymbols9 } = await Promise.resolve().then(() => (init_ts_parser(), ts_parser_exports));
|
|
3938
|
+
return parseSymbols9({ file, content, lang });
|
|
3939
|
+
}
|
|
3940
|
+
case "go": {
|
|
3941
|
+
const { parseSymbols: parseSymbols9 } = await Promise.resolve().then(() => (init_go_parser(), go_parser_exports));
|
|
3942
|
+
return parseSymbols9({ file, content, lang: "go" });
|
|
3943
|
+
}
|
|
3944
|
+
case "py": {
|
|
3945
|
+
const { parseSymbols: parseSymbols9 } = await Promise.resolve().then(() => (init_py_parser(), py_parser_exports));
|
|
3946
|
+
return parseSymbols9({ file, content, lang: "py" });
|
|
3947
|
+
}
|
|
3948
|
+
case "rs": {
|
|
3949
|
+
const { parseSymbols: parseSymbols9 } = await Promise.resolve().then(() => (init_rs_parser(), rs_parser_exports));
|
|
3950
|
+
return parseSymbols9({ file, content, lang: "rs" });
|
|
3951
|
+
}
|
|
3952
|
+
case "json": {
|
|
3953
|
+
const { parseSymbols: parseSymbols9 } = await Promise.resolve().then(() => (init_json_parser(), json_parser_exports));
|
|
3954
|
+
return parseSymbols9({ file, content, lang: "json" });
|
|
3955
|
+
}
|
|
3956
|
+
case "yaml": {
|
|
3957
|
+
const { parseSymbols: parseSymbols9 } = await Promise.resolve().then(() => (init_yaml_parser(), yaml_parser_exports));
|
|
3958
|
+
return parseSymbols9({ file, content, lang: "yaml" });
|
|
3959
|
+
}
|
|
3960
|
+
// Phase 1: ten languages now route through the Tree-Sitter WASM
|
|
3961
|
+
// universal parser (`tree-sitter-parser.ts`). The dispatch falls back to
|
|
3962
|
+
// the regex extractor in `generic-parser.ts` whenever WASM loading fails
|
|
3963
|
+
// or the parser returns zero symbols — preserving the indexable-file
|
|
3964
|
+
// contract that "missing a parser must never mean skipping the file".
|
|
3965
|
+
case "c":
|
|
3966
|
+
case "cpp":
|
|
3967
|
+
case "java":
|
|
3968
|
+
case "csharp":
|
|
3969
|
+
case "php":
|
|
3970
|
+
case "ruby":
|
|
3971
|
+
case "swift":
|
|
3972
|
+
case "kotlin":
|
|
3973
|
+
case "shell":
|
|
3974
|
+
case "elixir": {
|
|
3975
|
+
try {
|
|
3976
|
+
const { parseSymbols: parseSymbols10 } = await Promise.resolve().then(() => (init_tree_sitter_parser(), tree_sitter_parser_exports));
|
|
3977
|
+
const parsed = await parseSymbols10({ file, content, lang });
|
|
3978
|
+
if (parsed.symbols.length > 0) return parsed;
|
|
3979
|
+
} catch {
|
|
3980
|
+
}
|
|
3981
|
+
const { parseSymbols: parseSymbols9 } = await Promise.resolve().then(() => (init_generic_parser(), generic_parser_exports));
|
|
3982
|
+
return parseSymbols9({ file, content, lang });
|
|
3983
|
+
}
|
|
3984
|
+
default: {
|
|
3985
|
+
const { parseSymbols: parseSymbols9 } = await Promise.resolve().then(() => (init_generic_parser(), generic_parser_exports));
|
|
3986
|
+
return parseSymbols9({ file, content, lang });
|
|
3987
|
+
}
|
|
3988
|
+
}
|
|
3989
|
+
}
|
|
3990
|
+
function withRelations(parsed, content, lang) {
|
|
3991
|
+
let refs = parsed.refs ?? [];
|
|
3992
|
+
if (refs.length === 0 && hasImportPatterns(lang)) {
|
|
3993
|
+
refs = extractImports({ content, lang });
|
|
3994
|
+
}
|
|
3995
|
+
return { ...parsed, refs: refs.map((ref) => ref.lang ? ref : { ...ref, lang }) };
|
|
3996
|
+
}
|
|
3997
|
+
|
|
3998
|
+
// src/codebase-index/parser-worker-script.ts
|
|
3999
|
+
if (!parentPort) {
|
|
4000
|
+
throw new Error("parser-worker-script must be started as a worker thread");
|
|
4001
|
+
}
|
|
4002
|
+
var port = parentPort;
|
|
4003
|
+
async function parseBatch(files) {
|
|
4004
|
+
const slots = await parseFilesContent(files);
|
|
4005
|
+
const results = [];
|
|
4006
|
+
const errors = [];
|
|
4007
|
+
slots.forEach((slot, i) => {
|
|
4008
|
+
if (slot.result) results.push(slot.result);
|
|
4009
|
+
else errors.push({ file: files[i].file, error: slot.error ?? "parse produced no result" });
|
|
4010
|
+
});
|
|
4011
|
+
return { results, errors };
|
|
4012
|
+
}
|
|
4013
|
+
port.on("message", async (msg) => {
|
|
4014
|
+
if (msg.type === "shutdown") {
|
|
4015
|
+
port.close();
|
|
4016
|
+
return;
|
|
4017
|
+
}
|
|
4018
|
+
try {
|
|
4019
|
+
const { results, errors } = await parseBatch(msg.files);
|
|
4020
|
+
const response = {
|
|
4021
|
+
type: "result",
|
|
4022
|
+
id: msg.id,
|
|
4023
|
+
workerId: threadId,
|
|
4024
|
+
results,
|
|
4025
|
+
errors
|
|
4026
|
+
};
|
|
4027
|
+
port.postMessage(response);
|
|
4028
|
+
} catch (err) {
|
|
4029
|
+
const response = {
|
|
4030
|
+
type: "result",
|
|
4031
|
+
id: msg.id,
|
|
4032
|
+
workerId: threadId,
|
|
4033
|
+
results: [],
|
|
4034
|
+
errors: msg.files.map((f) => ({
|
|
4035
|
+
file: f.file,
|
|
4036
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4037
|
+
}))
|
|
4038
|
+
};
|
|
4039
|
+
port.postMessage(response);
|
|
4040
|
+
}
|
|
4041
|
+
});
|
|
4042
|
+
//# sourceMappingURL=parser-worker-script.js.map
|