grepmax 0.26.4 → 0.26.6
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/.claude-plugin/marketplace.json +1 -1
- package/README.md +6 -2
- package/dist/commands/doctor.js +20 -5
- package/dist/commands/mcp.js +60 -15
- package/dist/commands/search-run.js +4 -3
- package/dist/commands/similar.js +2 -2
- package/dist/eval-graph-recovery-probe.js +2 -2
- package/dist/eval-graph-sanity.js +2 -2
- package/dist/lib/analysis/surprising-connections.js +2 -2
- package/dist/lib/daemon/daemon.js +1 -0
- package/dist/lib/index/batch-processor.js +49 -22
- package/dist/lib/index/chunker.js +633 -602
- package/dist/lib/index/syncer.js +1 -1
- package/dist/lib/index/watcher.js +14 -3
- package/dist/lib/search/searcher.js +2 -2
- package/dist/lib/skeleton/skeletonizer.js +9 -1
- package/dist/lib/store/ann-config.js +34 -0
- package/dist/lib/store/vector-db.js +184 -33
- package/dist/lib/workers/orchestrator.js +5 -35
- package/dist/lib/workers/pool.js +15 -32
- package/mlx-embed-server/server.py +11 -0
- package/package.json +3 -8
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
|
@@ -43,6 +43,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
43
43
|
};
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
exports.TreeSitterChunker = void 0;
|
|
46
|
+
exports.repairLoneSurrogates = repairLoneSurrogates;
|
|
46
47
|
exports.formatChunkText = formatChunkText;
|
|
47
48
|
exports.buildAnchorChunk = buildAnchorChunk;
|
|
48
49
|
const fs = __importStar(require("node:fs"));
|
|
@@ -71,6 +72,19 @@ function resolveTreeSitterWasmLocator() {
|
|
|
71
72
|
return path.join(__dirname, "..", "..", "..", "node_modules", "web-tree-sitter", "tree-sitter.wasm");
|
|
72
73
|
}
|
|
73
74
|
}
|
|
75
|
+
// Char-stride chunk splitting can slice through a UTF-16 surrogate pair
|
|
76
|
+
// (e.g. PDF-extracted math italics like 𝑃), leaving a lone surrogate. That
|
|
77
|
+
// poisons the whole embed batch — the HF fast tokenizer rejects non-well-
|
|
78
|
+
// formed input — and puts invalid UTF-8 into the vector table's content
|
|
79
|
+
// column. Repair to U+FFFD at the chunk emit boundary. (Regex stand-in for
|
|
80
|
+
// String.prototype.toWellFormed, which the ES6 lib target doesn't declare.)
|
|
81
|
+
const LONE_SURROGATE_TEST = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/;
|
|
82
|
+
const LONE_SURROGATE_ALL = new RegExp(LONE_SURROGATE_TEST.source, "g");
|
|
83
|
+
function repairLoneSurrogates(s) {
|
|
84
|
+
if (!LONE_SURROGATE_TEST.test(s))
|
|
85
|
+
return s;
|
|
86
|
+
return s.replace(LONE_SURROGATE_ALL, "�");
|
|
87
|
+
}
|
|
74
88
|
function isDocLikeFile(filePath) {
|
|
75
89
|
const ext = path.extname(filePath).toLowerCase();
|
|
76
90
|
return [".md", ".mdx", ".txt", ".rst", ".adoc"].includes(ext);
|
|
@@ -249,6 +263,9 @@ class TreeSitterChunker {
|
|
|
249
263
|
}
|
|
250
264
|
// Split chunks if too big
|
|
251
265
|
result.chunks = result.chunks.flatMap((c) => this.splitIfTooBig(c));
|
|
266
|
+
for (const c of result.chunks) {
|
|
267
|
+
c.content = repairLoneSurrogates(c.content);
|
|
268
|
+
}
|
|
252
269
|
// Post-process: extract property function names from chunks.
|
|
253
270
|
// Catches resolver patterns like `posTableTurnReport: withAuth(scope, async (args) => {`
|
|
254
271
|
const PROP_FN_RE = /^\s*(\w+)\s*:\s*(?:async\s+)?(?:function\b|\(|[A-Za-z_]\w*\()/gm;
|
|
@@ -299,7 +316,7 @@ class TreeSitterChunker {
|
|
|
299
316
|
}
|
|
300
317
|
chunkWithTreeSitter(filePath, content) {
|
|
301
318
|
return __awaiter(this, void 0, void 0, function* () {
|
|
302
|
-
var _a
|
|
319
|
+
var _a;
|
|
303
320
|
const ext = path.extname(filePath);
|
|
304
321
|
if (ext === ".json") {
|
|
305
322
|
return {
|
|
@@ -322,662 +339,676 @@ class TreeSitterChunker {
|
|
|
322
339
|
};
|
|
323
340
|
this.parser.setLanguage(language);
|
|
324
341
|
const tree = this.parser.parse(content);
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
let cursorIndex = 0;
|
|
330
|
-
let cursorRow = 0;
|
|
331
|
-
let sawDefinition = false;
|
|
332
|
-
const metadata = { imports: [], exports: [], comments: [] };
|
|
333
|
-
const definitionTypes = (langDef === null || langDef === void 0 ? void 0 : langDef.definitionTypes) || [];
|
|
334
|
-
const isDefType = (t) => definitionTypes.includes(t);
|
|
335
|
-
const classify = (node) => {
|
|
336
|
-
const t = node.type;
|
|
337
|
-
// Classify strictly by the node's own type; avoid language-wide keyword inference
|
|
338
|
-
if (t.includes("method"))
|
|
339
|
-
return "method";
|
|
340
|
-
if (t.includes("class"))
|
|
341
|
-
return "class";
|
|
342
|
-
if (t.includes("interface"))
|
|
343
|
-
return "interface";
|
|
344
|
-
if (t.includes("type_alias"))
|
|
345
|
-
return "type_alias";
|
|
346
|
-
if (isDefType(t) || isTopLevelValueDef(node))
|
|
347
|
-
return "function";
|
|
348
|
-
return "block";
|
|
349
|
-
};
|
|
350
|
-
const unwrapExport = (node) => {
|
|
351
|
-
if (node.type === "export_statement" &&
|
|
352
|
-
node.namedChildren &&
|
|
353
|
-
node.namedChildren.length > 0) {
|
|
354
|
-
return node.namedChildren[0];
|
|
355
|
-
}
|
|
356
|
-
return node;
|
|
357
|
-
};
|
|
358
|
-
const isTopLevelValueDef = (node) => {
|
|
359
|
-
var _a;
|
|
360
|
-
const t = node.type;
|
|
361
|
-
if (t !== "lexical_declaration" && t !== "variable_declaration")
|
|
362
|
-
return false;
|
|
363
|
-
const parentType = ((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) || "";
|
|
364
|
-
const allowedParents = [
|
|
365
|
-
"program",
|
|
366
|
-
"module",
|
|
367
|
-
"source_file",
|
|
368
|
-
"class_body",
|
|
369
|
-
"export_statement",
|
|
370
|
-
];
|
|
371
|
-
if (parentType && !allowedParents.includes(parentType))
|
|
372
|
-
return false;
|
|
373
|
-
// Exported declarations are part of the API surface, so always index
|
|
374
|
-
// them regardless of RHS shape. Without this, files whose only export
|
|
375
|
-
// is `export const typeDefs = gql\`...\`` (template literals, object
|
|
376
|
-
// literals, or non-PascalCase consts) had no defined_symbols entry
|
|
377
|
-
// and peek/extract returned "Symbol not found".
|
|
378
|
-
if (parentType === "export_statement" ||
|
|
379
|
-
parentType === "export_declaration") {
|
|
380
|
-
return true;
|
|
381
|
-
}
|
|
382
|
-
const text = node.text || "";
|
|
383
|
-
if (text.includes("=>"))
|
|
384
|
-
return true;
|
|
385
|
-
if (text.includes("function "))
|
|
386
|
-
return true;
|
|
387
|
-
if (text.includes("class "))
|
|
388
|
-
return true;
|
|
389
|
-
if (/(?:^|\n)\s*(?:export\s+)?const\s+[A-Z0-9_]+\s*=/.test(text))
|
|
390
|
-
return true;
|
|
391
|
-
return false;
|
|
392
|
-
};
|
|
393
|
-
const getNodeName = (node) => {
|
|
394
|
-
var _a, _b, _c;
|
|
395
|
-
const byField = (field) => {
|
|
396
|
-
const child = typeof node.childForFieldName === "function"
|
|
397
|
-
? node.childForFieldName(field)
|
|
398
|
-
: null;
|
|
399
|
-
if (child === null || child === void 0 ? void 0 : child.text)
|
|
400
|
-
return String(child.text);
|
|
401
|
-
return null;
|
|
342
|
+
if (!tree) {
|
|
343
|
+
return {
|
|
344
|
+
chunks: [],
|
|
345
|
+
metadata: { imports: [], exports: [], comments: [] },
|
|
402
346
|
};
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
347
|
+
}
|
|
348
|
+
try {
|
|
349
|
+
return this.buildChunksFromTree(filePath, content, (langDef === null || langDef === void 0 ? void 0 : langDef.definitionTypes) || [], lang, tree.rootNode);
|
|
350
|
+
}
|
|
351
|
+
finally {
|
|
352
|
+
// Nothing in the guarded block may retain a Node.
|
|
353
|
+
tree.delete();
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
buildChunksFromTree(filePath, content, definitionTypes, lang, root) {
|
|
358
|
+
var _a;
|
|
359
|
+
const fileContext = `File: ${filePath}`;
|
|
360
|
+
const chunks = [];
|
|
361
|
+
const blockChunks = [];
|
|
362
|
+
let cursorIndex = 0;
|
|
363
|
+
let cursorRow = 0;
|
|
364
|
+
let sawDefinition = false;
|
|
365
|
+
const metadata = { imports: [], exports: [], comments: [] };
|
|
366
|
+
const isDefType = (t) => definitionTypes.includes(t);
|
|
367
|
+
const classify = (node) => {
|
|
368
|
+
const t = node.type;
|
|
369
|
+
// Classify strictly by the node's own type; avoid language-wide keyword inference
|
|
370
|
+
if (t.includes("method"))
|
|
371
|
+
return "method";
|
|
372
|
+
if (t.includes("class"))
|
|
373
|
+
return "class";
|
|
374
|
+
if (t.includes("interface"))
|
|
375
|
+
return "interface";
|
|
376
|
+
if (t.includes("type_alias"))
|
|
377
|
+
return "type_alias";
|
|
378
|
+
if (isDefType(t) || isTopLevelValueDef(node))
|
|
379
|
+
return "function";
|
|
380
|
+
return "block";
|
|
381
|
+
};
|
|
382
|
+
const unwrapExport = (node) => {
|
|
383
|
+
if (node.type === "export_statement" &&
|
|
384
|
+
node.namedChildren &&
|
|
385
|
+
node.namedChildren.length > 0) {
|
|
386
|
+
return node.namedChildren[0];
|
|
387
|
+
}
|
|
388
|
+
return node;
|
|
389
|
+
};
|
|
390
|
+
const isTopLevelValueDef = (node) => {
|
|
391
|
+
var _a;
|
|
392
|
+
const t = node.type;
|
|
393
|
+
if (t !== "lexical_declaration" && t !== "variable_declaration")
|
|
394
|
+
return false;
|
|
395
|
+
const parentType = ((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) || "";
|
|
396
|
+
const allowedParents = [
|
|
397
|
+
"program",
|
|
398
|
+
"module",
|
|
399
|
+
"source_file",
|
|
400
|
+
"class_body",
|
|
401
|
+
"export_statement",
|
|
402
|
+
];
|
|
403
|
+
if (parentType && !allowedParents.includes(parentType))
|
|
404
|
+
return false;
|
|
405
|
+
// Exported declarations are part of the API surface, so always index
|
|
406
|
+
// them regardless of RHS shape. Without this, files whose only export
|
|
407
|
+
// is `export const typeDefs = gql\`...\`` (template literals, object
|
|
408
|
+
// literals, or non-PascalCase consts) had no defined_symbols entry
|
|
409
|
+
// and peek/extract returned "Symbol not found".
|
|
410
|
+
if (parentType === "export_statement" ||
|
|
411
|
+
parentType === "export_declaration") {
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
const text = node.text || "";
|
|
415
|
+
if (text.includes("=>"))
|
|
416
|
+
return true;
|
|
417
|
+
if (text.includes("function "))
|
|
418
|
+
return true;
|
|
419
|
+
if (text.includes("class "))
|
|
420
|
+
return true;
|
|
421
|
+
if (/(?:^|\n)\s*(?:export\s+)?const\s+[A-Z0-9_]+\s*=/.test(text))
|
|
422
|
+
return true;
|
|
423
|
+
return false;
|
|
424
|
+
};
|
|
425
|
+
const getNodeName = (node) => {
|
|
426
|
+
var _a, _b, _c;
|
|
427
|
+
const byField = (field) => {
|
|
428
|
+
const child = typeof node.childForFieldName === "function"
|
|
429
|
+
? node.childForFieldName(field)
|
|
430
|
+
: null;
|
|
431
|
+
if (child === null || child === void 0 ? void 0 : child.text)
|
|
432
|
+
return String(child.text);
|
|
432
433
|
return null;
|
|
433
434
|
};
|
|
434
|
-
const
|
|
435
|
-
|
|
436
|
-
const
|
|
437
|
-
if (
|
|
438
|
-
return
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
return
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
const t = current.type;
|
|
456
|
-
if (t.includes("comment")) {
|
|
457
|
-
comments.unshift(current.text);
|
|
458
|
-
current = current.previousSibling;
|
|
459
|
-
}
|
|
460
|
-
else if (current.text.trim().length === 0) {
|
|
461
|
-
// whitespace
|
|
462
|
-
current = current.previousSibling;
|
|
463
|
-
}
|
|
464
|
-
else {
|
|
465
|
-
break;
|
|
466
|
-
}
|
|
435
|
+
const fieldNames = ["name", "property", "identifier"];
|
|
436
|
+
for (const field of fieldNames) {
|
|
437
|
+
const name = byField(field);
|
|
438
|
+
if (name)
|
|
439
|
+
return name;
|
|
440
|
+
}
|
|
441
|
+
const identifierChild = ((_a = node.namedChildren) !== null && _a !== void 0 ? _a : []).find((c) => [
|
|
442
|
+
"identifier",
|
|
443
|
+
"property_identifier",
|
|
444
|
+
"type_identifier",
|
|
445
|
+
"field_identifier",
|
|
446
|
+
"simple_identifier", // Swift, Kotlin
|
|
447
|
+
"word", // Bash
|
|
448
|
+
].includes(c.type));
|
|
449
|
+
if (identifierChild === null || identifierChild === void 0 ? void 0 : identifierChild.text)
|
|
450
|
+
return String(identifierChild.text);
|
|
451
|
+
for (const child of (_b = node.namedChildren) !== null && _b !== void 0 ? _b : []) {
|
|
452
|
+
if (child.type === "variable_declarator") {
|
|
453
|
+
const idChild = ((_c = child.namedChildren) !== null && _c !== void 0 ? _c : []).find((c) => ["identifier", "property_identifier", "type_identifier"].includes(c.type));
|
|
454
|
+
if (idChild === null || idChild === void 0 ? void 0 : idChild.text)
|
|
455
|
+
return String(idChild.text);
|
|
467
456
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
457
|
+
}
|
|
458
|
+
const match = (node.text || "").match(/(?:class|function)\s+([A-Za-z0-9_$]+)/);
|
|
459
|
+
if (match === null || match === void 0 ? void 0 : match[1])
|
|
460
|
+
return match[1];
|
|
461
|
+
const varMatch = (node.text || "").match(/(?:const|let|var)\s+([A-Za-z0-9_$]+)/);
|
|
462
|
+
if (varMatch === null || varMatch === void 0 ? void 0 : varMatch[1])
|
|
463
|
+
return varMatch[1];
|
|
464
|
+
return null;
|
|
465
|
+
};
|
|
466
|
+
const labelForNode = (node) => {
|
|
467
|
+
const name = getNodeName(node);
|
|
468
|
+
const t = node.type;
|
|
469
|
+
if (t.includes("class"))
|
|
470
|
+
return `Class: ${name !== null && name !== void 0 ? name : "<anonymous class>"}`;
|
|
471
|
+
if (t.includes("method"))
|
|
472
|
+
return `Method: ${name !== null && name !== void 0 ? name : "<anonymous method>"}`;
|
|
473
|
+
if (t.includes("interface"))
|
|
474
|
+
return `Interface: ${name !== null && name !== void 0 ? name : "<anonymous interface>"}`;
|
|
475
|
+
if (t.includes("type_alias"))
|
|
476
|
+
return `Type: ${name !== null && name !== void 0 ? name : "<anonymous type>"}`;
|
|
477
|
+
if (t.includes("function"))
|
|
478
|
+
return `Function: ${name !== null && name !== void 0 ? name : "<anonymous function>"}`;
|
|
479
|
+
if (isTopLevelValueDef(node))
|
|
480
|
+
return `Function: ${name !== null && name !== void 0 ? name : "<anonymous function>"}`;
|
|
481
|
+
return name ? `Symbol: ${name}` : null;
|
|
482
|
+
};
|
|
483
|
+
const getDocstring = (node) => {
|
|
484
|
+
const comments = [];
|
|
485
|
+
let current = node.previousSibling;
|
|
486
|
+
while (current) {
|
|
487
|
+
const t = current.type;
|
|
488
|
+
if (t.includes("comment")) {
|
|
489
|
+
comments.unshift(current.text);
|
|
490
|
+
current = current.previousSibling;
|
|
478
491
|
}
|
|
479
|
-
else if (
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
const effective = unwrapExport(node);
|
|
483
|
-
const name = getNodeName(effective);
|
|
484
|
-
if (name) {
|
|
485
|
-
metadata.exports.push(name);
|
|
486
|
-
}
|
|
492
|
+
else if (current.text.trim().length === 0) {
|
|
493
|
+
// whitespace
|
|
494
|
+
current = current.previousSibling;
|
|
487
495
|
}
|
|
488
|
-
else
|
|
489
|
-
|
|
490
|
-
// Only top comments
|
|
491
|
-
metadata.comments.push(node.text.trim());
|
|
492
|
-
}
|
|
496
|
+
else {
|
|
497
|
+
break;
|
|
493
498
|
}
|
|
499
|
+
}
|
|
500
|
+
return comments.length > 0 ? comments.join("\n") : undefined;
|
|
501
|
+
};
|
|
502
|
+
const visit = (node, stack) => {
|
|
503
|
+
var _a;
|
|
504
|
+
// Metadata extraction
|
|
505
|
+
if (node.type === "import_statement" ||
|
|
506
|
+
node.type === "import_declaration" ||
|
|
507
|
+
node.type === "import_header" // Kotlin
|
|
508
|
+
) {
|
|
509
|
+
metadata.imports.push(node.text.trim());
|
|
510
|
+
}
|
|
511
|
+
else if (node.type === "export_statement" ||
|
|
512
|
+
node.type === "export_declaration") {
|
|
513
|
+
// Extract exported identifier
|
|
494
514
|
const effective = unwrapExport(node);
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
//
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
515
|
+
const name = getNodeName(effective);
|
|
516
|
+
if (name) {
|
|
517
|
+
metadata.exports.push(name);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
else if (node.type.includes("comment")) {
|
|
521
|
+
if (node.startPosition.row < 10) {
|
|
522
|
+
// Only top comments
|
|
523
|
+
metadata.comments.push(node.text.trim());
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
const effective = unwrapExport(node);
|
|
527
|
+
const isDefinition = isDefType(effective.type) || isTopLevelValueDef(effective);
|
|
528
|
+
let nextStack = stack;
|
|
529
|
+
if (isDefinition) {
|
|
530
|
+
sawDefinition = true;
|
|
531
|
+
const label = labelForNode(effective);
|
|
532
|
+
const context = [...stack, ...(label ? [label] : [])];
|
|
533
|
+
const docstring = getDocstring(node);
|
|
534
|
+
// Calculate complexity
|
|
535
|
+
const getComplexity = (n) => {
|
|
536
|
+
let complexity = 1;
|
|
537
|
+
const complexTypes = [
|
|
538
|
+
"if_statement",
|
|
539
|
+
"for_statement",
|
|
540
|
+
"while_statement",
|
|
541
|
+
"switch_statement",
|
|
542
|
+
"catch_clause",
|
|
543
|
+
"conditional_expression",
|
|
544
|
+
"binary_expression", // simplistic, but counts logical ops usually
|
|
545
|
+
];
|
|
546
|
+
const countComplexity = (curr) => {
|
|
547
|
+
var _a;
|
|
548
|
+
if (complexTypes.includes(curr.type)) {
|
|
549
|
+
if (curr.type === "binary_expression") {
|
|
550
|
+
const op = curr.childForFieldName
|
|
551
|
+
? curr.childForFieldName("operator")
|
|
552
|
+
: null;
|
|
553
|
+
if (["&&", "||", "??"].includes((op === null || op === void 0 ? void 0 : op.text) || "")) {
|
|
526
554
|
complexity++;
|
|
527
555
|
}
|
|
528
556
|
}
|
|
529
|
-
|
|
530
|
-
|
|
557
|
+
else {
|
|
558
|
+
complexity++;
|
|
531
559
|
}
|
|
532
|
-
};
|
|
533
|
-
countComplexity(n);
|
|
534
|
-
return complexity;
|
|
535
|
-
};
|
|
536
|
-
const complexity = getComplexity(effective);
|
|
537
|
-
// Check if exported
|
|
538
|
-
const name = getNodeName(effective);
|
|
539
|
-
const isExported = name ? metadata.exports.includes(name) : false;
|
|
540
|
-
// Extract symbols
|
|
541
|
-
const definedSymbols = [];
|
|
542
|
-
if (name)
|
|
543
|
-
definedSymbols.push(name);
|
|
544
|
-
const referencedSymbols = [];
|
|
545
|
-
// Reduce a constructor / instanceof operand to a simple symbol name:
|
|
546
|
-
// bare identifier -> its text; `ns.ClassName` member -> the rightmost
|
|
547
|
-
// property (`ClassName`). Returns null for shapes we can't name.
|
|
548
|
-
const simpleRefName = (node) => {
|
|
549
|
-
var _a, _b;
|
|
550
|
-
if (!node)
|
|
551
|
-
return null;
|
|
552
|
-
if (node.type === "member_expression") {
|
|
553
|
-
const prop = node.childForFieldName
|
|
554
|
-
? node.childForFieldName("property")
|
|
555
|
-
: null;
|
|
556
|
-
return (_a = prop === null || prop === void 0 ? void 0 : prop.text) !== null && _a !== void 0 ? _a : null;
|
|
557
560
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
const addRef = (name) => {
|
|
561
|
-
if (name && !referencedSymbols.includes(name)) {
|
|
562
|
-
referencedSymbols.push(name);
|
|
561
|
+
for (const child of (_a = curr.namedChildren) !== null && _a !== void 0 ? _a : []) {
|
|
562
|
+
countComplexity(child);
|
|
563
563
|
}
|
|
564
564
|
};
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
565
|
+
countComplexity(n);
|
|
566
|
+
return complexity;
|
|
567
|
+
};
|
|
568
|
+
const complexity = getComplexity(effective);
|
|
569
|
+
// Check if exported
|
|
570
|
+
const name = getNodeName(effective);
|
|
571
|
+
const isExported = name ? metadata.exports.includes(name) : false;
|
|
572
|
+
// Extract symbols
|
|
573
|
+
const definedSymbols = [];
|
|
574
|
+
if (name)
|
|
575
|
+
definedSymbols.push(name);
|
|
576
|
+
const referencedSymbols = [];
|
|
577
|
+
// Reduce a constructor / instanceof operand to a simple symbol name:
|
|
578
|
+
// bare identifier -> its text; `ns.ClassName` member -> the rightmost
|
|
579
|
+
// property (`ClassName`). Returns null for shapes we can't name.
|
|
580
|
+
const simpleRefName = (node) => {
|
|
581
|
+
var _a, _b;
|
|
582
|
+
if (!node)
|
|
583
|
+
return null;
|
|
584
|
+
if (node.type === "member_expression") {
|
|
585
|
+
const prop = node.childForFieldName
|
|
586
|
+
? node.childForFieldName("property")
|
|
587
|
+
: null;
|
|
588
|
+
return (_a = prop === null || prop === void 0 ? void 0 : prop.text) !== null && _a !== void 0 ? _a : null;
|
|
589
|
+
}
|
|
590
|
+
return (_b = node.text) !== null && _b !== void 0 ? _b : null;
|
|
591
|
+
};
|
|
592
|
+
const addRef = (name) => {
|
|
593
|
+
if (name && !referencedSymbols.includes(name)) {
|
|
594
|
+
referencedSymbols.push(name);
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
// Type-position references go in a SEPARATE list so they never inflate
|
|
598
|
+
// referencedSymbols.length (which drives role classification below and
|
|
599
|
+
// the search structural boost). Navigation consumers union the two.
|
|
600
|
+
const typeReferencedSymbols = [];
|
|
601
|
+
const addTypeRef = (n) => {
|
|
602
|
+
if (n && !typeReferencedSymbols.includes(n)) {
|
|
603
|
+
typeReferencedSymbols.push(n);
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
// Member-call names (`obj.method()`), recorded additively alongside
|
|
607
|
+
// referencedSymbols (see Chunk.memberReferencedSymbols). Deduped, like
|
|
608
|
+
// type refs — a future resolver queries membership, not occurrence count.
|
|
609
|
+
const memberReferencedSymbols = [];
|
|
610
|
+
const addMemberRef = (n) => {
|
|
611
|
+
if (n && !memberReferencedSymbols.includes(n)) {
|
|
612
|
+
memberReferencedSymbols.push(n);
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
// Leaf identifier node types across grammars (a bare name with no
|
|
616
|
+
// named children — `ErrorCodes`, not `a.ErrorCodes`).
|
|
617
|
+
const LEAF_ID_TYPES = new Set([
|
|
618
|
+
"identifier",
|
|
619
|
+
"type_identifier",
|
|
620
|
+
"constant", // Ruby
|
|
621
|
+
"name", // PHP
|
|
622
|
+
"simple_identifier", // Kotlin, Swift
|
|
623
|
+
"property_identifier",
|
|
624
|
+
"field_identifier", // Go
|
|
625
|
+
]);
|
|
626
|
+
const isLeafId = (n) => {
|
|
627
|
+
var _a, _b;
|
|
628
|
+
return !!n &&
|
|
629
|
+
LEAF_ID_TYPES.has(n.type) &&
|
|
630
|
+
((_b = (_a = n.namedChildren) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) === 0;
|
|
631
|
+
};
|
|
632
|
+
const firstNamed = (n) => { var _a, _b; return (_b = ((_a = n.namedChildren) !== null && _a !== void 0 ? _a : [])[0]) !== null && _b !== void 0 ? _b : null; };
|
|
633
|
+
// Resolve the type name out of an instantiation node (`new Foo`,
|
|
634
|
+
// `Foo{}`, `new Foo()`), preferring an explicit `type` field and
|
|
635
|
+
// falling back to the first named child (qualified names reduce to
|
|
636
|
+
// their rightmost segment via simpleRefName).
|
|
637
|
+
const instantiatedTypeName = (n) => {
|
|
638
|
+
var _a;
|
|
639
|
+
const typed = (_a = (n.childForFieldName ? n.childForFieldName("type") : null)) !== null && _a !== void 0 ? _a : firstNamed(n);
|
|
640
|
+
if (!typed)
|
|
641
|
+
return null;
|
|
642
|
+
if (isLeafId(typed))
|
|
643
|
+
return typed.text;
|
|
644
|
+
return simpleRefName(typed);
|
|
645
|
+
};
|
|
646
|
+
// First `type_identifier` reachable directly or via a `user_type`
|
|
647
|
+
// wrapper — covers Java `instanceof_expression` and Kotlin/Swift
|
|
648
|
+
// `check_expression` (`x is T`).
|
|
649
|
+
const firstTypeIdent = (n) => {
|
|
650
|
+
var _a, _b;
|
|
651
|
+
for (const c of (_a = n.namedChildren) !== null && _a !== void 0 ? _a : []) {
|
|
652
|
+
if (c.type === "type_identifier")
|
|
653
|
+
return c.text;
|
|
654
|
+
if (c.type === "user_type") {
|
|
655
|
+
const t = ((_b = c.namedChildren) !== null && _b !== void 0 ? _b : []).find((x) => x.type === "type_identifier");
|
|
656
|
+
if (t === null || t === void 0 ? void 0 : t.text)
|
|
657
|
+
return t.text;
|
|
572
658
|
}
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
659
|
+
}
|
|
660
|
+
return null;
|
|
661
|
+
};
|
|
662
|
+
// Member / scope access node types, one per grammar. We capture the
|
|
663
|
+
// head (object/scope) only when it is a Capitalized leaf identifier,
|
|
664
|
+
// so `ErrorCodes.VALIDATION` / `ErrorCodes::NOT_FOUND` yield an edge
|
|
665
|
+
// to `ErrorCodes` while `this.x` / `req.body` / lowercase locals do not.
|
|
666
|
+
const MEMBER_ACCESS_TYPES = new Set([
|
|
667
|
+
"member_expression", // TS/JS
|
|
668
|
+
"attribute", // Python
|
|
669
|
+
"selector_expression", // Go
|
|
670
|
+
"field_access", // Java
|
|
671
|
+
"member_access_expression", // C#
|
|
672
|
+
"scoped_identifier", // Rust
|
|
673
|
+
"scope_resolution", // Ruby
|
|
674
|
+
"navigation_expression", // Kotlin, Swift
|
|
675
|
+
"field_expression", // Scala
|
|
676
|
+
"class_constant_access_expression", // PHP
|
|
677
|
+
]);
|
|
678
|
+
// Instantiation node types whose first/`type` child names a type.
|
|
679
|
+
const INSTANTIATION_TYPES = new Set([
|
|
680
|
+
"object_creation_expression", // Java, C#, PHP
|
|
681
|
+
"composite_literal", // Go
|
|
682
|
+
"struct_expression", // Rust
|
|
683
|
+
"instance_expression", // Scala
|
|
684
|
+
]);
|
|
685
|
+
// Harvest Capitalized type names out of an annotation subtree, for the
|
|
686
|
+
// grammars whose type names are NOT `type_identifier` nodes (so Shape 4
|
|
687
|
+
// can't see them): Python/C# spell them `identifier`, PHP spells them
|
|
688
|
+
// `name`. We walk the WHOLE subtree and keep every Capitalized leaf of
|
|
689
|
+
// those kinds — safe because we only ever enter from a type position (a
|
|
690
|
+
// `type` / `return_type` / `returns` field, or a base/heritage list).
|
|
691
|
+
// Excludes the chunk's own name; lowercase builtins (`list`, `int`,
|
|
692
|
+
// `string`) and non-name leaves (`predefined_type`, `string_content`
|
|
693
|
+
// in `Literal["x"]`) fall out via the kind + Capitalized gates.
|
|
694
|
+
const TYPE_NAME_LEAF_TYPES = new Set(["identifier", "name"]);
|
|
695
|
+
const harvestTypeLeaves = (node) => {
|
|
696
|
+
var _a, _b, _c;
|
|
697
|
+
if (!node)
|
|
698
|
+
return;
|
|
699
|
+
if (((_b = (_a = node.namedChildren) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) === 0) {
|
|
700
|
+
if (TYPE_NAME_LEAF_TYPES.has(node.type) &&
|
|
701
|
+
/^[A-Z]/.test(node.text) &&
|
|
702
|
+
node.text !== name) {
|
|
703
|
+
addTypeRef(node.text);
|
|
581
704
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
"
|
|
592
|
-
"
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
return null;
|
|
610
|
-
if (isLeafId(typed))
|
|
611
|
-
return typed.text;
|
|
612
|
-
return simpleRefName(typed);
|
|
613
|
-
};
|
|
614
|
-
// First `type_identifier` reachable directly or via a `user_type`
|
|
615
|
-
// wrapper — covers Java `instanceof_expression` and Kotlin/Swift
|
|
616
|
-
// `check_expression` (`x is T`).
|
|
617
|
-
const firstTypeIdent = (n) => {
|
|
618
|
-
var _a, _b;
|
|
619
|
-
for (const c of (_a = n.namedChildren) !== null && _a !== void 0 ? _a : []) {
|
|
620
|
-
if (c.type === "type_identifier")
|
|
621
|
-
return c.text;
|
|
622
|
-
if (c.type === "user_type") {
|
|
623
|
-
const t = ((_b = c.namedChildren) !== null && _b !== void 0 ? _b : []).find((x) => x.type === "type_identifier");
|
|
624
|
-
if (t === null || t === void 0 ? void 0 : t.text)
|
|
625
|
-
return t.text;
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
for (const c of (_c = node.namedChildren) !== null && _c !== void 0 ? _c : [])
|
|
708
|
+
harvestTypeLeaves(c);
|
|
709
|
+
};
|
|
710
|
+
const extractRefs = (n) => {
|
|
711
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1;
|
|
712
|
+
// Handle JS/TS (call_expression), Python (call), Lua (function_call)
|
|
713
|
+
if (n.type === "call_expression" ||
|
|
714
|
+
n.type === "call" ||
|
|
715
|
+
n.type === "function_call") {
|
|
716
|
+
const func = n.childForFieldName
|
|
717
|
+
? n.childForFieldName("function")
|
|
718
|
+
: null;
|
|
719
|
+
if (func) {
|
|
720
|
+
let funcName = func.text;
|
|
721
|
+
let isMemberCall = false;
|
|
722
|
+
// Handle member access (obj.method) to extract just 'method'
|
|
723
|
+
if (func.type === "member_expression") {
|
|
724
|
+
// JS/TS: object.property
|
|
725
|
+
const prop = func.childForFieldName
|
|
726
|
+
? func.childForFieldName("property")
|
|
727
|
+
: null;
|
|
728
|
+
if (prop) {
|
|
729
|
+
funcName = prop.text;
|
|
730
|
+
isMemberCall = true;
|
|
731
|
+
}
|
|
626
732
|
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
"attribute", // Python
|
|
637
|
-
"selector_expression", // Go
|
|
638
|
-
"field_access", // Java
|
|
639
|
-
"member_access_expression", // C#
|
|
640
|
-
"scoped_identifier", // Rust
|
|
641
|
-
"scope_resolution", // Ruby
|
|
642
|
-
"navigation_expression", // Kotlin, Swift
|
|
643
|
-
"field_expression", // Scala
|
|
644
|
-
"class_constant_access_expression", // PHP
|
|
645
|
-
]);
|
|
646
|
-
// Instantiation node types whose first/`type` child names a type.
|
|
647
|
-
const INSTANTIATION_TYPES = new Set([
|
|
648
|
-
"object_creation_expression", // Java, C#, PHP
|
|
649
|
-
"composite_literal", // Go
|
|
650
|
-
"struct_expression", // Rust
|
|
651
|
-
"instance_expression", // Scala
|
|
652
|
-
]);
|
|
653
|
-
// Harvest Capitalized type names out of an annotation subtree, for the
|
|
654
|
-
// grammars whose type names are NOT `type_identifier` nodes (so Shape 4
|
|
655
|
-
// can't see them): Python/C# spell them `identifier`, PHP spells them
|
|
656
|
-
// `name`. We walk the WHOLE subtree and keep every Capitalized leaf of
|
|
657
|
-
// those kinds — safe because we only ever enter from a type position (a
|
|
658
|
-
// `type` / `return_type` / `returns` field, or a base/heritage list).
|
|
659
|
-
// Excludes the chunk's own name; lowercase builtins (`list`, `int`,
|
|
660
|
-
// `string`) and non-name leaves (`predefined_type`, `string_content`
|
|
661
|
-
// in `Literal["x"]`) fall out via the kind + Capitalized gates.
|
|
662
|
-
const TYPE_NAME_LEAF_TYPES = new Set(["identifier", "name"]);
|
|
663
|
-
const harvestTypeLeaves = (node) => {
|
|
664
|
-
var _a, _b, _c;
|
|
665
|
-
if (!node)
|
|
666
|
-
return;
|
|
667
|
-
if (((_b = (_a = node.namedChildren) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) === 0) {
|
|
668
|
-
if (TYPE_NAME_LEAF_TYPES.has(node.type) &&
|
|
669
|
-
/^[A-Z]/.test(node.text) &&
|
|
670
|
-
node.text !== name) {
|
|
671
|
-
addTypeRef(node.text);
|
|
733
|
+
else if (func.type === "attribute") {
|
|
734
|
+
// Python: object.attribute
|
|
735
|
+
const attr = func.childForFieldName
|
|
736
|
+
? func.childForFieldName("attribute")
|
|
737
|
+
: null;
|
|
738
|
+
if (attr) {
|
|
739
|
+
funcName = attr.text;
|
|
740
|
+
isMemberCall = true;
|
|
741
|
+
}
|
|
672
742
|
}
|
|
673
|
-
|
|
743
|
+
referencedSymbols.push(funcName);
|
|
744
|
+
// Additive: member calls ALSO go here. referencedSymbols is left
|
|
745
|
+
// untouched (no recall loss); this just tags which were members.
|
|
746
|
+
if (isMemberCall)
|
|
747
|
+
addMemberRef(funcName);
|
|
674
748
|
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
if (n.type === "call_expression" ||
|
|
682
|
-
n.type === "call" ||
|
|
683
|
-
n.type === "function_call") {
|
|
684
|
-
const func = n.childForFieldName
|
|
685
|
-
? n.childForFieldName("function")
|
|
686
|
-
: null;
|
|
687
|
-
if (func) {
|
|
688
|
-
let funcName = func.text;
|
|
749
|
+
else {
|
|
750
|
+
// Swift/Kotlin: call_expression has no "function" field;
|
|
751
|
+
// the callable is a positional child
|
|
752
|
+
const firstChild = ((_a = n.namedChildren) !== null && _a !== void 0 ? _a : [])[0];
|
|
753
|
+
if (firstChild) {
|
|
754
|
+
let funcName = firstChild.text;
|
|
689
755
|
let isMemberCall = false;
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
? func.childForFieldName("property")
|
|
756
|
+
if (firstChild.type === "navigation_expression") {
|
|
757
|
+
const suffix = ((_b = firstChild.namedChildren) !== null && _b !== void 0 ? _b : []).find((c) => c.type === "navigation_suffix");
|
|
758
|
+
const methodId = suffix
|
|
759
|
+
? ((_c = suffix.namedChildren) !== null && _c !== void 0 ? _c : []).find((c) => c.type === "simple_identifier")
|
|
695
760
|
: null;
|
|
696
|
-
if (
|
|
697
|
-
funcName =
|
|
698
|
-
isMemberCall = true;
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
else if (func.type === "attribute") {
|
|
702
|
-
// Python: object.attribute
|
|
703
|
-
const attr = func.childForFieldName
|
|
704
|
-
? func.childForFieldName("attribute")
|
|
705
|
-
: null;
|
|
706
|
-
if (attr) {
|
|
707
|
-
funcName = attr.text;
|
|
761
|
+
if (methodId) {
|
|
762
|
+
funcName = methodId.text;
|
|
708
763
|
isMemberCall = true;
|
|
709
764
|
}
|
|
710
765
|
}
|
|
711
766
|
referencedSymbols.push(funcName);
|
|
712
|
-
// Additive: member calls ALSO go here. referencedSymbols is left
|
|
713
|
-
// untouched (no recall loss); this just tags which were members.
|
|
714
767
|
if (isMemberCall)
|
|
715
768
|
addMemberRef(funcName);
|
|
716
769
|
}
|
|
717
|
-
else {
|
|
718
|
-
// Swift/Kotlin: call_expression has no "function" field;
|
|
719
|
-
// the callable is a positional child
|
|
720
|
-
const firstChild = ((_a = n.namedChildren) !== null && _a !== void 0 ? _a : [])[0];
|
|
721
|
-
if (firstChild) {
|
|
722
|
-
let funcName = firstChild.text;
|
|
723
|
-
let isMemberCall = false;
|
|
724
|
-
if (firstChild.type === "navigation_expression") {
|
|
725
|
-
const suffix = ((_b = firstChild.namedChildren) !== null && _b !== void 0 ? _b : []).find((c) => c.type === "navigation_suffix");
|
|
726
|
-
const methodId = suffix
|
|
727
|
-
? ((_c = suffix.namedChildren) !== null && _c !== void 0 ? _c : []).find((c) => c.type === "simple_identifier")
|
|
728
|
-
: null;
|
|
729
|
-
if (methodId) {
|
|
730
|
-
funcName = methodId.text;
|
|
731
|
-
isMemberCall = true;
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
referencedSymbols.push(funcName);
|
|
735
|
-
if (isMemberCall)
|
|
736
|
-
addMemberRef(funcName);
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
770
|
}
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
771
|
+
}
|
|
772
|
+
// JSX elements: <Component /> or <Component>
|
|
773
|
+
if (n.type === "jsx_self_closing_element" ||
|
|
774
|
+
n.type === "jsx_opening_element") {
|
|
775
|
+
const nameChild = ((_d = n.namedChildren) !== null && _d !== void 0 ? _d : []).find((c) => c.type === "identifier" ||
|
|
776
|
+
c.type === "member_expression" ||
|
|
777
|
+
c.type === "nested_identifier");
|
|
778
|
+
if (nameChild) {
|
|
779
|
+
// Only capture PascalCase components (skip HTML tags like div, span)
|
|
780
|
+
const name = nameChild.type === "member_expression"
|
|
781
|
+
? ((_g = (_f = (_e = nameChild.childForFieldName) === null || _e === void 0 ? void 0 : _e.call(nameChild, "property")) === null || _f === void 0 ? void 0 : _f.text) !== null && _g !== void 0 ? _g : nameChild.text)
|
|
782
|
+
: nameChild.text;
|
|
783
|
+
if (name && /^[A-Z]/.test(name)) {
|
|
784
|
+
referencedSymbols.push(name);
|
|
754
785
|
}
|
|
755
786
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
787
|
+
}
|
|
788
|
+
// Identifier-as-value references: edges the call-expression capture
|
|
789
|
+
// above misses. These feed the graph-walk consumers (PPR,
|
|
790
|
+
// `gmax dead <ClassName>`, audit) that need class/enum references,
|
|
791
|
+
// not just method-call names. Node types are grammar-specific but
|
|
792
|
+
// each falls into one of three shapes — instantiation, type-test, or
|
|
793
|
+
// member/scope access — handled uniformly across the 14 grammars.
|
|
794
|
+
// Shape 1 — instantiation: `new ClassName(...)`, `ClassName{...}`.
|
|
795
|
+
if (n.type === "new_expression") {
|
|
796
|
+
// TS/JS — constructor may be qualified (`ns.ClassName`).
|
|
797
|
+
const ctor = n.childForFieldName
|
|
798
|
+
? n.childForFieldName("constructor")
|
|
799
|
+
: null;
|
|
800
|
+
addRef(simpleRefName(ctor));
|
|
801
|
+
}
|
|
802
|
+
else if (INSTANTIATION_TYPES.has(n.type)) {
|
|
803
|
+
addRef(instantiatedTypeName(n));
|
|
804
|
+
}
|
|
805
|
+
// Shape 2 — type-test: `x instanceof T`, `x is T`.
|
|
806
|
+
if (n.type === "binary_expression") {
|
|
807
|
+
// TS/JS, PHP — instanceof is a binary operator.
|
|
808
|
+
const op = n.childForFieldName
|
|
809
|
+
? n.childForFieldName("operator")
|
|
810
|
+
: null;
|
|
811
|
+
if ((op === null || op === void 0 ? void 0 : op.text) === "instanceof") {
|
|
812
|
+
const right = n.childForFieldName
|
|
813
|
+
? n.childForFieldName("right")
|
|
767
814
|
: null;
|
|
768
|
-
addRef(simpleRefName(
|
|
815
|
+
addRef(simpleRefName(right));
|
|
769
816
|
}
|
|
770
|
-
|
|
771
|
-
|
|
817
|
+
}
|
|
818
|
+
else if (n.type === "instanceof_expression" || // Java
|
|
819
|
+
n.type === "check_expression" // Kotlin, Swift (`x is T`)
|
|
820
|
+
) {
|
|
821
|
+
addRef(firstTypeIdent(n));
|
|
822
|
+
}
|
|
823
|
+
else if (n.type === "is_pattern_expression") {
|
|
824
|
+
// C# — `x is BeyondError`: the type sits in a constant_pattern.
|
|
825
|
+
const cp = ((_h = n.namedChildren) !== null && _h !== void 0 ? _h : []).find((c) => c.type === "constant_pattern");
|
|
826
|
+
const id = cp ? firstNamed(cp) : null;
|
|
827
|
+
if (isLeafId(id) && id && /^[A-Z]/.test(id.text))
|
|
828
|
+
addRef(id.text);
|
|
829
|
+
}
|
|
830
|
+
// Shape 3 — member / scope access: `ErrorCodes.MEMBER`,
|
|
831
|
+
// `ErrorCodes::MEMBER`. Capitalized head only (skip `this.x`).
|
|
832
|
+
if (MEMBER_ACCESS_TYPES.has(n.type)) {
|
|
833
|
+
const head = firstNamed(n);
|
|
834
|
+
if (head && isLeafId(head) && /^[A-Z]/.test(head.text)) {
|
|
835
|
+
addRef(head.text);
|
|
772
836
|
}
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
837
|
+
}
|
|
838
|
+
// Shape 4 — type-position reference (TS/TSX): `: T`, `<T>`, `T[]`,
|
|
839
|
+
// `as T`, `interface X extends T`, type aliases. TS uses a distinct
|
|
840
|
+
// `type_identifier` node for type names (primitives are
|
|
841
|
+
// `predefined_type`, so they never reach here). Capture Capitalized
|
|
842
|
+
// ones, skipping the chunk's own name and any locally-declared type
|
|
843
|
+
// parameter (`<T>` and its uses `value: T`). Constraints
|
|
844
|
+
// (`T extends Foo`) still yield `Foo`. Type list, never referencedSymbols.
|
|
845
|
+
if (n.type === "type_identifier" &&
|
|
846
|
+
/^[A-Z]/.test(n.text) &&
|
|
847
|
+
n.text !== name &&
|
|
848
|
+
!declaredTypeParams.has(n.text)) {
|
|
849
|
+
addTypeRef(n.text);
|
|
850
|
+
}
|
|
851
|
+
// Shape 5 — class heritage `class A extends B`: the superclass is an
|
|
852
|
+
// `identifier` (a runtime value), not a `type_identifier`, so Shape 4
|
|
853
|
+
// misses it. `implements I` and `interface X extends Y` use
|
|
854
|
+
// type_identifier and are already covered. Reduce `extends ns.Base`
|
|
855
|
+
// to `Base`; skip type arguments (Shape 4 handles those).
|
|
856
|
+
if (n.type === "extends_clause") {
|
|
857
|
+
for (const c of (_j = n.namedChildren) !== null && _j !== void 0 ? _j : []) {
|
|
858
|
+
if (c.type === "type_arguments")
|
|
859
|
+
continue;
|
|
860
|
+
const sup = simpleRefName(c);
|
|
861
|
+
if (sup && /^[A-Z]/.test(sup) && sup !== name)
|
|
862
|
+
addTypeRef(sup);
|
|
785
863
|
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
864
|
+
}
|
|
865
|
+
// Shape 6 — type-position references for grammars whose type names
|
|
866
|
+
// are plain `identifier` / `name` nodes (not `type_identifier`), so
|
|
867
|
+
// Shape 4 can't reach them. Read the annotation off the carrier
|
|
868
|
+
// node's type field and harvest from THAT subtree only — never the
|
|
869
|
+
// node as a whole — so value expressions stay out of the type list.
|
|
870
|
+
// Each grammar is gated by `lang` because the carrier node/field
|
|
871
|
+
// names collide across grammars (`function_definition`,
|
|
872
|
+
// `method_declaration`, `assignment`, …). The other typed grammars
|
|
873
|
+
// (Go/Rust/Java/Kotlin/Scala/Swift) spell type names as
|
|
874
|
+
// `type_identifier` and are already covered by Shape 4.
|
|
875
|
+
if (lang === "python") {
|
|
876
|
+
// `x: Foo`, `-> Bar`, `v: Baz = …`, and `class C(Base)` bases.
|
|
877
|
+
if (n.type === "typed_parameter" ||
|
|
878
|
+
n.type === "typed_default_parameter") {
|
|
879
|
+
harvestTypeLeaves((_l = (_k = n.childForFieldName) === null || _k === void 0 ? void 0 : _k.call(n, "type")) !== null && _l !== void 0 ? _l : null);
|
|
790
880
|
}
|
|
791
|
-
else if (n.type === "
|
|
792
|
-
|
|
793
|
-
const cp = ((_h = n.namedChildren) !== null && _h !== void 0 ? _h : []).find((c) => c.type === "constant_pattern");
|
|
794
|
-
const id = cp ? firstNamed(cp) : null;
|
|
795
|
-
if (isLeafId(id) && id && /^[A-Z]/.test(id.text))
|
|
796
|
-
addRef(id.text);
|
|
881
|
+
else if (n.type === "function_definition") {
|
|
882
|
+
harvestTypeLeaves((_o = (_m = n.childForFieldName) === null || _m === void 0 ? void 0 : _m.call(n, "return_type")) !== null && _o !== void 0 ? _o : null);
|
|
797
883
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
if (MEMBER_ACCESS_TYPES.has(n.type)) {
|
|
801
|
-
const head = firstNamed(n);
|
|
802
|
-
if (head && isLeafId(head) && /^[A-Z]/.test(head.text)) {
|
|
803
|
-
addRef(head.text);
|
|
804
|
-
}
|
|
884
|
+
else if (n.type === "assignment") {
|
|
885
|
+
harvestTypeLeaves((_q = (_p = n.childForFieldName) === null || _p === void 0 ? void 0 : _p.call(n, "type")) !== null && _q !== void 0 ? _q : null);
|
|
805
886
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
// `type_identifier` node for type names (primitives are
|
|
809
|
-
// `predefined_type`, so they never reach here). Capture Capitalized
|
|
810
|
-
// ones, skipping the chunk's own name and any locally-declared type
|
|
811
|
-
// parameter (`<T>` and its uses `value: T`). Constraints
|
|
812
|
-
// (`T extends Foo`) still yield `Foo`. Type list, never referencedSymbols.
|
|
813
|
-
if (n.type === "type_identifier" &&
|
|
814
|
-
/^[A-Z]/.test(n.text) &&
|
|
815
|
-
n.text !== name &&
|
|
816
|
-
!declaredTypeParams.has(n.text)) {
|
|
817
|
-
addTypeRef(n.text);
|
|
887
|
+
else if (n.type === "class_definition") {
|
|
888
|
+
harvestTypeLeaves((_s = (_r = n.childForFieldName) === null || _r === void 0 ? void 0 : _r.call(n, "superclasses")) !== null && _s !== void 0 ? _s : null);
|
|
818
889
|
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
//
|
|
822
|
-
//
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
continue;
|
|
828
|
-
const sup = simpleRefName(c);
|
|
829
|
-
if (sup && /^[A-Z]/.test(sup) && sup !== name)
|
|
830
|
-
addTypeRef(sup);
|
|
831
|
-
}
|
|
890
|
+
}
|
|
891
|
+
else if (lang === "c_sharp") {
|
|
892
|
+
// Method return (`returns`), parameter / property / variable
|
|
893
|
+
// (`type`), and the base/interface list.
|
|
894
|
+
if (n.type === "parameter" ||
|
|
895
|
+
n.type === "property_declaration" ||
|
|
896
|
+
n.type === "variable_declaration") {
|
|
897
|
+
harvestTypeLeaves((_u = (_t = n.childForFieldName) === null || _t === void 0 ? void 0 : _t.call(n, "type")) !== null && _u !== void 0 ? _u : null);
|
|
832
898
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
// Shape 4 can't reach them. Read the annotation off the carrier
|
|
836
|
-
// node's type field and harvest from THAT subtree only — never the
|
|
837
|
-
// node as a whole — so value expressions stay out of the type list.
|
|
838
|
-
// Each grammar is gated by `lang` because the carrier node/field
|
|
839
|
-
// names collide across grammars (`function_definition`,
|
|
840
|
-
// `method_declaration`, `assignment`, …). The other typed grammars
|
|
841
|
-
// (Go/Rust/Java/Kotlin/Scala/Swift) spell type names as
|
|
842
|
-
// `type_identifier` and are already covered by Shape 4.
|
|
843
|
-
if (lang === "python") {
|
|
844
|
-
// `x: Foo`, `-> Bar`, `v: Baz = …`, and `class C(Base)` bases.
|
|
845
|
-
if (n.type === "typed_parameter" ||
|
|
846
|
-
n.type === "typed_default_parameter") {
|
|
847
|
-
harvestTypeLeaves((_l = (_k = n.childForFieldName) === null || _k === void 0 ? void 0 : _k.call(n, "type")) !== null && _l !== void 0 ? _l : null);
|
|
848
|
-
}
|
|
849
|
-
else if (n.type === "function_definition") {
|
|
850
|
-
harvestTypeLeaves((_o = (_m = n.childForFieldName) === null || _m === void 0 ? void 0 : _m.call(n, "return_type")) !== null && _o !== void 0 ? _o : null);
|
|
851
|
-
}
|
|
852
|
-
else if (n.type === "assignment") {
|
|
853
|
-
harvestTypeLeaves((_q = (_p = n.childForFieldName) === null || _p === void 0 ? void 0 : _p.call(n, "type")) !== null && _q !== void 0 ? _q : null);
|
|
854
|
-
}
|
|
855
|
-
else if (n.type === "class_definition") {
|
|
856
|
-
harvestTypeLeaves((_s = (_r = n.childForFieldName) === null || _r === void 0 ? void 0 : _r.call(n, "superclasses")) !== null && _s !== void 0 ? _s : null);
|
|
857
|
-
}
|
|
899
|
+
else if (n.type === "method_declaration") {
|
|
900
|
+
harvestTypeLeaves((_w = (_v = n.childForFieldName) === null || _v === void 0 ? void 0 : _v.call(n, "returns")) !== null && _w !== void 0 ? _w : null);
|
|
858
901
|
}
|
|
859
|
-
else if (
|
|
860
|
-
|
|
861
|
-
// (`type`), and the base/interface list.
|
|
862
|
-
if (n.type === "parameter" ||
|
|
863
|
-
n.type === "property_declaration" ||
|
|
864
|
-
n.type === "variable_declaration") {
|
|
865
|
-
harvestTypeLeaves((_u = (_t = n.childForFieldName) === null || _t === void 0 ? void 0 : _t.call(n, "type")) !== null && _u !== void 0 ? _u : null);
|
|
866
|
-
}
|
|
867
|
-
else if (n.type === "method_declaration") {
|
|
868
|
-
harvestTypeLeaves((_w = (_v = n.childForFieldName) === null || _v === void 0 ? void 0 : _v.call(n, "returns")) !== null && _w !== void 0 ? _w : null);
|
|
869
|
-
}
|
|
870
|
-
else if (n.type === "base_list") {
|
|
871
|
-
harvestTypeLeaves(n);
|
|
872
|
-
}
|
|
902
|
+
else if (n.type === "base_list") {
|
|
903
|
+
harvestTypeLeaves(n);
|
|
873
904
|
}
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
else if (n.type === "function_definition" ||
|
|
883
|
-
n.type === "method_declaration") {
|
|
884
|
-
harvestTypeLeaves((_0 = (_z = n.childForFieldName) === null || _z === void 0 ? void 0 : _z.call(n, "return_type")) !== null && _0 !== void 0 ? _0 : null);
|
|
885
|
-
}
|
|
886
|
-
else if (n.type === "base_clause" ||
|
|
887
|
-
n.type === "class_interface_clause") {
|
|
888
|
-
harvestTypeLeaves(n);
|
|
889
|
-
}
|
|
905
|
+
}
|
|
906
|
+
else if (lang === "php") {
|
|
907
|
+
// Types are `name` inside `named_type` / `optional_type`.
|
|
908
|
+
// Parameter / property (`type`), function & method return
|
|
909
|
+
// (`return_type`), and the extends / implements clauses.
|
|
910
|
+
if (n.type === "simple_parameter" ||
|
|
911
|
+
n.type === "property_declaration") {
|
|
912
|
+
harvestTypeLeaves((_y = (_x = n.childForFieldName) === null || _x === void 0 ? void 0 : _x.call(n, "type")) !== null && _y !== void 0 ? _y : null);
|
|
890
913
|
}
|
|
891
|
-
|
|
892
|
-
|
|
914
|
+
else if (n.type === "function_definition" ||
|
|
915
|
+
n.type === "method_declaration") {
|
|
916
|
+
harvestTypeLeaves((_0 = (_z = n.childForFieldName) === null || _z === void 0 ? void 0 : _z.call(n, "return_type")) !== null && _0 !== void 0 ? _0 : null);
|
|
893
917
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
// declarations. Chunk-scoped, which matches single-definition chunks.
|
|
898
|
-
const declaredTypeParams = new Set();
|
|
899
|
-
const collectTypeParams = (n) => {
|
|
900
|
-
var _a;
|
|
901
|
-
if (n.type === "type_parameter") {
|
|
902
|
-
const nm = firstNamed(n);
|
|
903
|
-
if ((nm === null || nm === void 0 ? void 0 : nm.type) === "type_identifier")
|
|
904
|
-
declaredTypeParams.add(nm.text);
|
|
918
|
+
else if (n.type === "base_clause" ||
|
|
919
|
+
n.type === "class_interface_clause") {
|
|
920
|
+
harvestTypeLeaves(n);
|
|
905
921
|
}
|
|
906
|
-
for (const child of (_a = n.namedChildren) !== null && _a !== void 0 ? _a : [])
|
|
907
|
-
collectTypeParams(child);
|
|
908
|
-
};
|
|
909
|
-
collectTypeParams(effective);
|
|
910
|
-
extractRefs(effective);
|
|
911
|
-
// Classify role
|
|
912
|
-
let role = "IMPLEMENTATION";
|
|
913
|
-
if (isDefinition)
|
|
914
|
-
role = "DEFINITION";
|
|
915
|
-
if (complexity > 5 && referencedSymbols.length > 5)
|
|
916
|
-
role = "ORCHESTRATION";
|
|
917
|
-
if (effective.type.includes("import"))
|
|
918
|
-
role = "IMPORT";
|
|
919
|
-
chunks.push({
|
|
920
|
-
content: node.text,
|
|
921
|
-
startLine: node.startPosition.row,
|
|
922
|
-
endLine: node.endPosition.row,
|
|
923
|
-
type: classify(effective),
|
|
924
|
-
context,
|
|
925
|
-
docstring,
|
|
926
|
-
complexity,
|
|
927
|
-
isExported,
|
|
928
|
-
definedSymbols,
|
|
929
|
-
referencedSymbols,
|
|
930
|
-
typeReferencedSymbols,
|
|
931
|
-
memberReferencedSymbols,
|
|
932
|
-
role,
|
|
933
|
-
parentSymbol: stack.length > 1
|
|
934
|
-
? stack[stack.length - 1].replace(/^(Class|Method|Function|Interface|Type): /, "")
|
|
935
|
-
: undefined,
|
|
936
|
-
});
|
|
937
|
-
nextStack = context;
|
|
938
|
-
}
|
|
939
|
-
for (const child of (_a = effective.namedChildren) !== null && _a !== void 0 ? _a : []) {
|
|
940
|
-
visit(child, nextStack);
|
|
941
|
-
}
|
|
942
|
-
};
|
|
943
|
-
for (const child of (_b = root.namedChildren) !== null && _b !== void 0 ? _b : []) {
|
|
944
|
-
visit(child, [fileContext]);
|
|
945
|
-
const effective = unwrapExport(child);
|
|
946
|
-
const isDefinition = isDefType(effective.type) || isTopLevelValueDef(effective);
|
|
947
|
-
if (!isDefinition)
|
|
948
|
-
continue;
|
|
949
|
-
if (child.startIndex > cursorIndex) {
|
|
950
|
-
const gapText = content.slice(cursorIndex, child.startIndex);
|
|
951
|
-
if (gapText.trim().length > 0) {
|
|
952
|
-
blockChunks.push({
|
|
953
|
-
content: gapText,
|
|
954
|
-
startLine: cursorRow,
|
|
955
|
-
endLine: child.startPosition.row,
|
|
956
|
-
type: "block",
|
|
957
|
-
context: [fileContext],
|
|
958
|
-
});
|
|
959
922
|
}
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
923
|
+
for (const child of (_1 = n.namedChildren) !== null && _1 !== void 0 ? _1 : []) {
|
|
924
|
+
extractRefs(child);
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
// Collect locally-declared type-parameter names (`<T, K>`) so their
|
|
928
|
+
// *uses* (`value: T`) are excluded from type refs, not just their
|
|
929
|
+
// declarations. Chunk-scoped, which matches single-definition chunks.
|
|
930
|
+
const declaredTypeParams = new Set();
|
|
931
|
+
const collectTypeParams = (n) => {
|
|
932
|
+
var _a;
|
|
933
|
+
if (n.type === "type_parameter") {
|
|
934
|
+
const nm = firstNamed(n);
|
|
935
|
+
if ((nm === null || nm === void 0 ? void 0 : nm.type) === "type_identifier")
|
|
936
|
+
declaredTypeParams.add(nm.text);
|
|
937
|
+
}
|
|
938
|
+
for (const child of (_a = n.namedChildren) !== null && _a !== void 0 ? _a : [])
|
|
939
|
+
collectTypeParams(child);
|
|
940
|
+
};
|
|
941
|
+
collectTypeParams(effective);
|
|
942
|
+
extractRefs(effective);
|
|
943
|
+
// Classify role
|
|
944
|
+
let role = "IMPLEMENTATION";
|
|
945
|
+
if (isDefinition)
|
|
946
|
+
role = "DEFINITION";
|
|
947
|
+
if (complexity > 5 && referencedSymbols.length > 5)
|
|
948
|
+
role = "ORCHESTRATION";
|
|
949
|
+
if (effective.type.includes("import"))
|
|
950
|
+
role = "IMPORT";
|
|
951
|
+
chunks.push({
|
|
952
|
+
content: node.text,
|
|
953
|
+
startLine: node.startPosition.row,
|
|
954
|
+
endLine: node.endPosition.row,
|
|
955
|
+
type: classify(effective),
|
|
956
|
+
context,
|
|
957
|
+
docstring,
|
|
958
|
+
complexity,
|
|
959
|
+
isExported,
|
|
960
|
+
definedSymbols,
|
|
961
|
+
referencedSymbols,
|
|
962
|
+
typeReferencedSymbols,
|
|
963
|
+
memberReferencedSymbols,
|
|
964
|
+
role,
|
|
965
|
+
parentSymbol: stack.length > 1
|
|
966
|
+
? stack[stack.length - 1].replace(/^(Class|Method|Function|Interface|Type): /, "")
|
|
967
|
+
: undefined,
|
|
968
|
+
});
|
|
969
|
+
nextStack = context;
|
|
970
|
+
}
|
|
971
|
+
for (const child of (_a = effective.namedChildren) !== null && _a !== void 0 ? _a : []) {
|
|
972
|
+
visit(child, nextStack);
|
|
963
973
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
974
|
+
};
|
|
975
|
+
for (const child of (_a = root.namedChildren) !== null && _a !== void 0 ? _a : []) {
|
|
976
|
+
visit(child, [fileContext]);
|
|
977
|
+
const effective = unwrapExport(child);
|
|
978
|
+
const isDefinition = isDefType(effective.type) || isTopLevelValueDef(effective);
|
|
979
|
+
if (!isDefinition)
|
|
980
|
+
continue;
|
|
981
|
+
if (child.startIndex > cursorIndex) {
|
|
982
|
+
const gapText = content.slice(cursorIndex, child.startIndex);
|
|
983
|
+
if (gapText.trim().length > 0) {
|
|
967
984
|
blockChunks.push({
|
|
968
|
-
content:
|
|
985
|
+
content: gapText,
|
|
969
986
|
startLine: cursorRow,
|
|
970
|
-
endLine:
|
|
987
|
+
endLine: child.startPosition.row,
|
|
971
988
|
type: "block",
|
|
972
989
|
context: [fileContext],
|
|
973
990
|
});
|
|
974
991
|
}
|
|
975
992
|
}
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
993
|
+
cursorIndex = child.endIndex;
|
|
994
|
+
cursorRow = child.endPosition.row;
|
|
995
|
+
}
|
|
996
|
+
if (cursorIndex < content.length) {
|
|
997
|
+
const tailText = content.slice(cursorIndex);
|
|
998
|
+
if (tailText.trim().length > 0) {
|
|
999
|
+
blockChunks.push({
|
|
1000
|
+
content: tailText,
|
|
1001
|
+
startLine: cursorRow,
|
|
1002
|
+
endLine: root.endPosition.row,
|
|
1003
|
+
type: "block",
|
|
1004
|
+
context: [fileContext],
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
if (!sawDefinition)
|
|
1009
|
+
return { chunks: [], metadata };
|
|
1010
|
+
const combined = [...blockChunks, ...chunks].sort((a, b) => a.startLine - b.startLine || a.endLine - b.endLine);
|
|
1011
|
+
return { chunks: combined, metadata };
|
|
981
1012
|
}
|
|
982
1013
|
/**
|
|
983
1014
|
* Restrict a sub-chunk's symbol lists to names that actually occur in its
|