scip-query 0.4.3 → 0.5.0

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.
Files changed (98) hide show
  1. package/dist/{chunk-46ZTW4AI.js → chunk-2MQ5DPY6.js} +2 -2
  2. package/dist/{chunk-FIMTTUGE.js → chunk-2QTYIOJ5.js} +2 -2
  3. package/dist/{chunk-YQIWS5V6.js → chunk-3VI4YXCL.js} +2 -2
  4. package/dist/{chunk-UIRCHPOU.js → chunk-3VV2G6U7.js} +3 -3
  5. package/dist/{chunk-PKDFXASW.js → chunk-44PFXVXG.js} +2 -2
  6. package/dist/{chunk-XJSPWHNT.js → chunk-6SLFQR36.js} +2 -2
  7. package/dist/{chunk-334PCFO3.js → chunk-7DBPRGMS.js} +12 -12
  8. package/dist/{chunk-B7LDMCUS.js → chunk-DTB724R3.js} +2 -2
  9. package/dist/{chunk-R2QBMQQN.js → chunk-FLOYI6I4.js} +3 -3
  10. package/dist/{chunk-4TYLS5XX.js → chunk-GJT3MO2T.js} +9 -2
  11. package/dist/{chunk-7KGTWDAX.js → chunk-HAP4LJKX.js} +7 -28
  12. package/dist/{chunk-E74RY6AQ.js → chunk-JCOJQ4I6.js} +2 -2
  13. package/dist/{chunk-J34HAAEQ.js → chunk-JGQMOS4V.js} +2 -2
  14. package/dist/{chunk-T6UVM534.js → chunk-JMD4WJ2Q.js} +2 -2
  15. package/dist/{chunk-UNS6ZQVX.js → chunk-JSQPZOPO.js} +10 -17
  16. package/dist/{chunk-ZPEI7DRJ.js → chunk-JZN3DRCT.js} +14 -21
  17. package/dist/{chunk-NYZ6INK3.js → chunk-KMWYB3CX.js} +11 -35
  18. package/dist/{chunk-B747RITP.js → chunk-MRM755FU.js} +7 -8
  19. package/dist/{chunk-AEBM56CO.js → chunk-N2XO3Z5F.js} +2 -2
  20. package/dist/{chunk-PSK5BPFE.js → chunk-OLW5UL36.js} +18 -24
  21. package/dist/{chunk-FMAYH7GS.js → chunk-OMCRXXDX.js} +72 -14
  22. package/dist/{chunk-6PVHJ332.js → chunk-OWJOHUZE.js} +2 -2
  23. package/dist/{chunk-IC5NTO47.js → chunk-PEDH3D4G.js} +2 -2
  24. package/dist/{chunk-U74VYTLX.js → chunk-POAN4SCR.js} +2 -2
  25. package/dist/{chunk-5YB6UXQ3.js → chunk-PTMGEBU3.js} +2 -2
  26. package/dist/{chunk-LQXBFCP2.js → chunk-PU44HK7P.js} +2 -2
  27. package/dist/{chunk-QZ4FRB65.js → chunk-QJI7EECA.js} +15 -29
  28. package/dist/{chunk-C2VSV54P.js → chunk-R5HICGMB.js} +3 -3
  29. package/dist/{chunk-NWXTQGUE.js → chunk-RJ2D6YWQ.js} +2 -2
  30. package/dist/{chunk-PGQXIAJF.js → chunk-RZ5GYPBP.js} +2 -2
  31. package/dist/chunk-SRLQNO6O.js +101 -0
  32. package/dist/{chunk-OMVF3BHY.js → chunk-UGS7HJI4.js} +2 -2
  33. package/dist/{chunk-7OGXSMLY.js → chunk-VKUUXOE7.js} +2 -2
  34. package/dist/{chunk-XMZAC2VU.js → chunk-VPUJSJCI.js} +2 -2
  35. package/dist/{chunk-NNFP4ZRF.js → chunk-VRWVV3EP.js} +2 -2
  36. package/dist/{chunk-HESWGDIV.js → chunk-WJWQEU4A.js} +2 -2
  37. package/dist/chunk-WJZHDUSB.js +40 -0
  38. package/dist/{chunk-HL2LXSBW.js → chunk-WWOCQ5W4.js} +2 -2
  39. package/dist/chunk-X3Q2OVRL.js +77 -0
  40. package/dist/chunk-Y3P7QKKN.js +27 -0
  41. package/dist/{chunk-DIYEUFVP.js → chunk-Y6FAHY4N.js} +2 -2
  42. package/dist/{chunk-4YN3PE57.js → chunk-ZDL3U4W2.js} +2 -2
  43. package/dist/{chunk-HW76DVE4.js → chunk-ZXNX5JRE.js} +2 -2
  44. package/dist/cli.js +175 -355
  45. package/dist/index.js +43 -43
  46. package/dist/queries/affected.js +2 -2
  47. package/dist/queries/bottlenecks.js +2 -2
  48. package/dist/queries/by-kind.js +2 -2
  49. package/dist/queries/call-graph.js +2 -2
  50. package/dist/queries/change-surface.d.ts +3 -0
  51. package/dist/queries/change-surface.js +2 -2
  52. package/dist/queries/clean-signature.d.ts +9 -1
  53. package/dist/queries/clean-signature.js +5 -3
  54. package/dist/queries/code.js +2 -2
  55. package/dist/queries/complexity-hotspots.d.ts +7 -0
  56. package/dist/queries/complexity-hotspots.js +2 -2
  57. package/dist/queries/complexity.js +2 -2
  58. package/dist/queries/convergence.js +2 -2
  59. package/dist/queries/coupling.js +2 -2
  60. package/dist/queries/cycles.js +2 -2
  61. package/dist/queries/dataflow.js +2 -2
  62. package/dist/queries/dead.js +3 -3
  63. package/dist/queries/deep-chains.js +2 -2
  64. package/dist/queries/deps.js +2 -2
  65. package/dist/queries/drift.js +2 -2
  66. package/dist/queries/extract-candidates.js +2 -2
  67. package/dist/queries/fan.js +2 -2
  68. package/dist/queries/health.js +13 -13
  69. package/dist/queries/hierarchy.js +2 -2
  70. package/dist/queries/hotspots.js +2 -2
  71. package/dist/queries/imports.js +2 -2
  72. package/dist/queries/index.js +43 -43
  73. package/dist/queries/isolated.js +3 -3
  74. package/dist/queries/members.d.ts +3 -0
  75. package/dist/queries/members.js +2 -2
  76. package/dist/queries/methods.js +2 -2
  77. package/dist/queries/outline.d.ts +3 -0
  78. package/dist/queries/outline.js +2 -2
  79. package/dist/queries/passthrough-candidates.js +2 -2
  80. package/dist/queries/redundant-reexports.js +3 -3
  81. package/dist/queries/refs.js +2 -2
  82. package/dist/queries/similar-chains.js +2 -2
  83. package/dist/queries/similar-files.js +2 -2
  84. package/dist/queries/similar-signatures.js +2 -2
  85. package/dist/queries/similar.js +2 -2
  86. package/dist/queries/slice.js +2 -2
  87. package/dist/queries/stale-abstractions.js +2 -2
  88. package/dist/queries/surface.js +2 -2
  89. package/dist/queries/symbols.js +3 -3
  90. package/dist/queries/system.d.ts +5 -1
  91. package/dist/queries/system.js +3 -3
  92. package/dist/queries/trace.js +3 -3
  93. package/dist/queries/wrapper-candidates.js +2 -2
  94. package/package.json +1 -1
  95. package/dist/chunk-NML6M5AS.js +0 -37
  96. package/dist/chunk-T3ALCNCP.js +0 -113
  97. package/dist/chunk-VJMTX3OR.js +0 -115
  98. package/dist/chunk-Y7FKURZG.js +0 -130
package/dist/cli.js CHANGED
@@ -2832,10 +2832,6 @@ var TEST_FILE_PATTERNS = [
2832
2832
  var TEST_SUPPORT_PATH_PATTERNS = [
2833
2833
  "%/test-utils/%"
2834
2834
  ];
2835
- function testFileExclusionSql(alias, extraPatterns = []) {
2836
- const patterns = uniquePatterns([...TEST_FILE_PATTERNS, ...extraPatterns]);
2837
- return patterns.map((pattern) => `${alias}.relative_path NOT LIKE '${pattern}'`).join("\n AND ");
2838
- }
2839
2835
  function buildFileDepGraph(db, scope) {
2840
2836
  const scopeFilter = scope ? `AND d1.relative_path LIKE '%${scope}%'` : "";
2841
2837
  const edges = db.all(
@@ -2890,6 +2886,8 @@ function findFirstSymbolMatch(db, symbolPattern) {
2890
2886
  const fileLineMatch = symbolPattern.match(/^(.+):(\d+)-(\d+)$/);
2891
2887
  if (fileLineMatch) {
2892
2888
  const [, filePath, startStr, endStr] = fileLineMatch;
2889
+ const userStart0 = Math.max(0, parseInt(startStr, 10) - 1);
2890
+ const userEnd0 = Math.max(userStart0, parseInt(endStr, 10) - 1);
2893
2891
  let row = db.get(
2894
2892
  `SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path
2895
2893
  FROM global_symbols gs
@@ -2901,8 +2899,8 @@ function findFirstSymbolMatch(db, symbolPattern) {
2901
2899
  ORDER BY (der.end_line - der.start_line) ASC
2902
2900
  LIMIT 1`,
2903
2901
  `%${filePath}%`,
2904
- parseInt(startStr, 10),
2905
- parseInt(endStr, 10)
2902
+ userStart0,
2903
+ userEnd0
2906
2904
  );
2907
2905
  if (!row) {
2908
2906
  row = db.get(
@@ -2919,8 +2917,8 @@ function findFirstSymbolMatch(db, symbolPattern) {
2919
2917
  ORDER BY (MAX(c.end_line) - MIN(c.start_line)) ASC
2920
2918
  LIMIT 1`,
2921
2919
  `%${filePath}%`,
2922
- parseInt(startStr, 10),
2923
- parseInt(endStr, 10)
2920
+ userStart0,
2921
+ userEnd0
2924
2922
  );
2925
2923
  }
2926
2924
  if (row && !db.isIgnored(row.relative_path)) {
@@ -3212,6 +3210,59 @@ function getSourceReferenceSites(db, symbol) {
3212
3210
  }
3213
3211
  return sites;
3214
3212
  }
3213
+ function getResolvedReferenceSites(db, symbol) {
3214
+ const match = getFullSymbolMatch(db, symbol);
3215
+ if (!match) {
3216
+ return [];
3217
+ }
3218
+ const rows = db.all(
3219
+ `SELECT DISTINCT d.relative_path, c.start_line, c.end_line
3220
+ FROM mentions m
3221
+ JOIN chunks c ON m.chunk_id = c.id
3222
+ JOIN documents d ON c.document_id = d.id
3223
+ WHERE m.symbol_id = ?
3224
+ AND m.role != 1
3225
+ ${db.pathExclusionsFor("d")}
3226
+ ORDER BY d.relative_path, c.start_line`,
3227
+ match.symbolId
3228
+ );
3229
+ const chunksByFile = /* @__PURE__ */ new Map();
3230
+ for (const row of rows) {
3231
+ if (db.isIgnored(row.relative_path)) continue;
3232
+ let bucket = chunksByFile.get(row.relative_path);
3233
+ if (!bucket) {
3234
+ bucket = [];
3235
+ chunksByFile.set(row.relative_path, bucket);
3236
+ }
3237
+ bucket.push({ start_line: row.start_line, end_line: row.end_line });
3238
+ }
3239
+ const identifier = leafName(match.symbol);
3240
+ const sites = [];
3241
+ const seen = /* @__PURE__ */ new Set();
3242
+ for (const [file, chunks] of chunksByFile) {
3243
+ const definitions = getDefinitionsForFile(db, file);
3244
+ const excludeOpts = file === match.relativePath ? { excludeStartLine: match.startLine, excludeEndLine: match.endLine } : {};
3245
+ const allHits = identifier ? findIdentifierLines(db, file, identifier, excludeOpts) : [];
3246
+ for (const chunk of chunks) {
3247
+ const hitsInChunk = allHits.filter(
3248
+ (line) => line >= chunk.start_line && line <= chunk.end_line
3249
+ );
3250
+ const lines = hitsInChunk.length > 0 ? hitsInChunk : [chunk.start_line];
3251
+ for (const line of lines) {
3252
+ const enclosing = findEnclosingDefinition(definitions, line);
3253
+ const key = `${file}|${line}|${enclosing?.symbol ?? ""}`;
3254
+ if (seen.has(key)) continue;
3255
+ seen.add(key);
3256
+ sites.push({
3257
+ file,
3258
+ line,
3259
+ enclosingSymbol: enclosing?.symbol ?? null
3260
+ });
3261
+ }
3262
+ }
3263
+ }
3264
+ return sites;
3265
+ }
3215
3266
  function calleeQueryParams(symbol, limit) {
3216
3267
  const params = [
3217
3268
  symbol.documentId,
@@ -3577,7 +3628,16 @@ function resolveCallableDefinitionStartLine(lines, definition) {
3577
3628
  const escapedLeaf = escapeRegex2(definition.leaf);
3578
3629
  const strongPatterns = [
3579
3630
  new RegExp(`\\b(?:function|def|fn)\\s+${escapedLeaf}\\b`),
3580
- new RegExp(`\\b${escapedLeaf}\\b\\s*[:=]\\s*(?:async\\s*)?(?:function\\b|\\()`)
3631
+ new RegExp(`\\b${escapedLeaf}\\b\\s*[:=]\\s*(?:async\\s*)?(?:function\\b|\\()`),
3632
+ // Method/function declaration with optional TypeScript generic
3633
+ // parameters: matches `foo(`, `foo<T>(`, `foo<T = Record<string, unknown>>(`.
3634
+ // `[^(]*` with greedy backtracking handles nested generics as long as
3635
+ // the generic block itself doesn't contain a `(`. Anchored to start of
3636
+ // line content (optional leading whitespace + optional access modifiers)
3637
+ // so it prefers the declaration line over call sites.
3638
+ new RegExp(
3639
+ `^\\s*(?:export\\s+|public\\s+|private\\s+|protected\\s+|static\\s+|readonly\\s+|async\\s+|abstract\\s+|get\\s+|set\\s+)*${escapedLeaf}\\s*(?:<[^(]*>)?\\s*\\(`
3640
+ )
3581
3641
  ];
3582
3642
  const fallbackPatterns = [
3583
3643
  new RegExp(`\\b${escapedLeaf}\\b\\s*\\(`)
@@ -4198,15 +4258,18 @@ function scoreDocumentPath(relativePath, rawPattern) {
4198
4258
  function normalizeLookupPath(filePattern) {
4199
4259
  return filePattern.trim().replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "").replace(/\/+$/, "");
4200
4260
  }
4201
- function uniquePatterns(patterns) {
4202
- return [...new Set(patterns)];
4203
- }
4204
4261
 
4205
4262
  // src/queries/clean-signature.ts
4206
4263
  function cleanSignature(sig) {
4207
4264
  if (!sig || !sig.trim()) return null;
4208
4265
  return sig.replace(/^```\w*\s*/, "").replace(/\s*```$/, "").replace(/^\(method\)\s*/, "").replace(/^\(property\)\s*/, "").replace(/^\(function\)\s*/, "").replace(/^\(class\)\s*/, "").replace(/^\(interface\)\s*/, "").replace(/^\(enum\)\s*/, "").replace(/^\(type alias\)\s*/, "").replace(/^\(const\)\s*/, "").replace(/^\(var\)\s*/, "").trim() || null;
4209
4266
  }
4267
+ function extractSignature(doc) {
4268
+ if (!doc) return null;
4269
+ const pipeIdx = doc.indexOf("|");
4270
+ if (pipeIdx === -1) return doc.replace(/\n/g, " ");
4271
+ return doc.slice(pipeIdx + 1).replace(/\n/g, " ");
4272
+ }
4210
4273
 
4211
4274
  // src/queries/symbols.ts
4212
4275
  function symbols(db, filePattern) {
@@ -4216,9 +4279,7 @@ function symbols(db, filePattern) {
4216
4279
  }
4217
4280
  return resolvedPaths.flatMap((relativePath) => getDefinitionsForFile(db, relativePath)).filter((row) => !db.isIgnored(row.relativePath)).map((row) => {
4218
4281
  const docRow = db.get(
4219
- `SELECT REPLACE(SUBSTR(documentation, INSTR(documentation, '|') + 1), char(10), ' ') AS sig
4220
- FROM global_symbols
4221
- WHERE id = ?`,
4282
+ "SELECT documentation FROM global_symbols WHERE id = ?",
4222
4283
  row.symbolId
4223
4284
  );
4224
4285
  return {
@@ -4226,7 +4287,7 @@ function symbols(db, filePattern) {
4226
4287
  endLine: row.endLine,
4227
4288
  symbol: row.symbol,
4228
4289
  shortName: shortenSymbol(row.symbol),
4229
- signature: cleanSignature(docRow?.sig ?? null)
4290
+ signature: cleanSignature(extractSignature(docRow?.documentation ?? null))
4230
4291
  };
4231
4292
  });
4232
4293
  }
@@ -4255,60 +4316,21 @@ function stripExtension(relativePath) {
4255
4316
  // src/queries/refs.ts
4256
4317
  function refs(db, symbolPattern) {
4257
4318
  const match = findFirstSymbolMatch(db, symbolPattern);
4258
- if (match) {
4259
- const includeDefinitionSite = !isFunctionLikeSymbol(match.symbol);
4260
- const definitionRows = includeDefinitionSite ? [{
4261
- relativePath: match.relativePath,
4262
- line: match.startLine
4263
- }] : [];
4264
- const sourceSites = getSourceReferenceSites(db, match).filter((site) => !db.isIgnored(site.file)).map((site) => ({
4265
- relativePath: site.file,
4266
- line: site.line
4267
- }));
4268
- if (sourceSites.length > 0) {
4269
- const seen2 = /* @__PURE__ */ new Set();
4270
- const rows2 = [...definitionRows, ...sourceSites, ...getRubySemanticRefs(db, match)].filter((site) => {
4271
- const key = `${site.relativePath}:${site.line}`;
4272
- if (seen2.has(key)) return false;
4273
- seen2.add(key);
4274
- return true;
4275
- });
4276
- return rows2;
4277
- }
4278
- }
4279
- const rows = db.all(
4280
- `SELECT DISTINCT d.relative_path, c.start_line
4281
- FROM mentions m
4282
- JOIN chunks c ON m.chunk_id = c.id
4283
- JOIN documents d ON c.document_id = d.id
4284
- JOIN global_symbols gs ON m.symbol_id = gs.id
4285
- WHERE m.symbol_id = ?
4286
- AND ${db.localSymbolPredicate}
4287
- AND m.role != 1
4288
- ORDER BY d.relative_path, c.start_line`,
4289
- match?.symbolId ?? -1
4290
- );
4291
- const referenceRows = rows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({
4292
- relativePath: r.relative_path,
4293
- line: r.start_line
4294
- }));
4295
- if (!match || db.isIgnored(match.relativePath) || isFunctionLikeSymbol(match.symbol)) {
4296
- return referenceRows;
4297
- }
4298
- const seen = new Set(referenceRows.map((row) => `${row.relativePath}:${row.line}`));
4299
- if (!seen.has(`${match.relativePath}:${match.startLine}`)) {
4300
- referenceRows.unshift({
4301
- relativePath: match.relativePath,
4302
- line: match.startLine
4303
- });
4304
- }
4305
- for (const row of getRubySemanticRefs(db, match)) {
4319
+ if (!match) return [];
4320
+ const includeDefinitionSite = !isFunctionLikeSymbol(match.symbol);
4321
+ const definitionRows = includeDefinitionSite && !db.isIgnored(match.relativePath) ? [{ relativePath: match.relativePath, line: match.startLine }] : [];
4322
+ const sourceSites = getSourceReferenceSites(db, match);
4323
+ const referenceSites = (sourceSites.length > 0 ? sourceSites : getResolvedReferenceSites(db, match)).filter((site) => !db.isIgnored(site.file)).map((site) => ({ relativePath: site.file, line: site.line }));
4324
+ const rubySites = getRubySemanticRefs(db, match);
4325
+ const seen = /* @__PURE__ */ new Set();
4326
+ const out = [];
4327
+ for (const row of [...definitionRows, ...referenceSites, ...rubySites]) {
4306
4328
  const key = `${row.relativePath}:${row.line}`;
4307
4329
  if (seen.has(key)) continue;
4308
4330
  seen.add(key);
4309
- referenceRows.push(row);
4331
+ out.push(row);
4310
4332
  }
4311
- return referenceRows;
4333
+ return out;
4312
4334
  }
4313
4335
  function getRubySemanticRefs(db, match) {
4314
4336
  if (!match.relativePath.endsWith(".rb")) {
@@ -4361,50 +4383,24 @@ function trace(db, symbolPattern) {
4361
4383
  return { definitions: [], referencedBy: [] };
4362
4384
  }
4363
4385
  const definitionMeta = db.get(
4364
- `SELECT
4365
- gs.display_name,
4366
- REPLACE(SUBSTR(gs.documentation, INSTR(gs.documentation, '|') + 1), char(10), ' ') AS sig
4367
- FROM global_symbols gs
4368
- WHERE gs.id = ?
4369
- LIMIT 10`,
4386
+ "SELECT display_name, documentation FROM global_symbols WHERE id = ?",
4370
4387
  match.symbolId
4371
4388
  );
4389
+ const sig = extractSignature(definitionMeta?.documentation ?? null);
4372
4390
  const definitions = db.isIgnored(match.relativePath) ? [] : [{
4373
4391
  relativePath: match.relativePath,
4374
4392
  startLine: match.startLine,
4375
4393
  endLine: match.endLine,
4376
- signature: buildTraceSignature(definitionMeta?.sig ?? null, definitionMeta?.display_name ?? null, match.symbol),
4394
+ signature: buildTraceSignature(sig, definitionMeta?.display_name ?? null, match.symbol),
4377
4395
  source: definitionSource(db, match.relativePath, match.startLine, match.endLine)
4378
4396
  }];
4379
4397
  const sourceSites = getSourceReferenceSites(db, match);
4380
- const referencedBy = sourceSites.length > 0 ? sourceSites.filter((site) => !db.isIgnored(site.file)).map((site) => ({
4398
+ const resolvedSites = sourceSites.length > 0 ? sourceSites : getResolvedReferenceSites(db, match);
4399
+ const referencedBy = resolvedSites.filter((site) => !db.isIgnored(site.file)).map((site) => ({
4381
4400
  relativePath: site.file,
4382
4401
  line: site.line,
4383
4402
  enclosingSymbol: site.enclosingSymbol,
4384
4403
  enclosingShort: site.enclosingSymbol ? shortenSymbol(site.enclosingSymbol) : "(top-level)"
4385
- })) : db.all(
4386
- `SELECT DISTINCT d.relative_path, c.start_line AS line,
4387
- (SELECT enc_gs.symbol
4388
- FROM defn_enclosing_ranges enc_der
4389
- JOIN global_symbols enc_gs ON enc_der.symbol_id = enc_gs.id
4390
- WHERE enc_der.document_id = d.id
4391
- AND enc_der.start_line <= c.start_line
4392
- AND enc_der.end_line >= c.end_line
4393
- ORDER BY (enc_der.end_line - enc_der.start_line) ASC
4394
- LIMIT 1
4395
- ) AS enclosing_symbol
4396
- FROM mentions m
4397
- JOIN chunks c ON m.chunk_id = c.id
4398
- JOIN documents d ON c.document_id = d.id
4399
- WHERE m.symbol_id = ?
4400
- AND m.role != 1
4401
- ORDER BY d.relative_path, c.start_line`,
4402
- match.symbolId
4403
- ).filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({
4404
- relativePath: r.relative_path,
4405
- line: r.line,
4406
- enclosingSymbol: r.enclosing_symbol,
4407
- enclosingShort: r.enclosing_symbol ? shortenSymbol(r.enclosing_symbol) : "(top-level)"
4408
4404
  }));
4409
4405
  return { definitions, referencedBy };
4410
4406
  }
@@ -4490,26 +4486,18 @@ function system(db, modulePattern) {
4490
4486
  ...matchedPaths
4491
4487
  );
4492
4488
  const files2 = fileRows.map((r) => r.relative_path).filter((p) => !db.isIgnored(p));
4493
- const symbolRows = db.all(
4494
- `SELECT der.start_line, der.end_line, gs.symbol,
4495
- REPLACE(SUBSTR(gs.documentation, INSTR(gs.documentation, '|') + 1), char(10), ' ') AS sig
4496
- FROM defn_enclosing_ranges der
4497
- JOIN global_symbols gs ON der.symbol_id = gs.id
4498
- JOIN documents d ON der.document_id = d.id
4499
- WHERE d.relative_path IN (${placeholders})
4500
- AND ${db.localSymbolPredicate}
4501
- ${db.symbolNoise}
4502
- AND gs.documentation IS NOT NULL
4503
- ORDER BY d.relative_path, der.start_line`,
4504
- ...matchedPaths
4505
- );
4506
- const symbols2 = symbolRows.map((r) => ({
4507
- startLine: r.start_line,
4508
- endLine: r.end_line,
4509
- symbol: r.symbol,
4510
- shortName: shortenSymbol(r.symbol),
4511
- signature: cleanSignature(r.sig)
4512
- }));
4489
+ const symbols2 = files2.flatMap((relativePath) => getDefinitionsForFile(db, relativePath)).filter((d) => d.documentation !== null && d.documentation !== "").sort(
4490
+ (a, b) => a.relativePath.localeCompare(b.relativePath) || a.startLine - b.startLine || a.endLine - b.endLine
4491
+ ).map((d) => {
4492
+ const sig = extractSignature(d.documentation);
4493
+ return {
4494
+ startLine: d.startLine,
4495
+ endLine: d.endLine,
4496
+ symbol: d.symbol,
4497
+ shortName: shortenSymbol(d.symbol),
4498
+ signature: cleanSignature(sig)
4499
+ };
4500
+ });
4513
4501
  const depRows = db.all(
4514
4502
  `SELECT DISTINCT d2.relative_path
4515
4503
  FROM mentions m
@@ -4976,32 +4964,24 @@ function outline(db, filePattern) {
4976
4964
  if (resolvedPaths.length === 0) {
4977
4965
  return [];
4978
4966
  }
4979
- const placeholders = resolvedPaths.map(() => "?").join(", ");
4980
- const rows = db.all(
4981
- `SELECT gs.symbol, gs.enclosing_symbol, der.start_line, der.end_line
4982
- FROM defn_enclosing_ranges der
4983
- JOIN global_symbols gs ON der.symbol_id = gs.id
4984
- JOIN documents d ON der.document_id = d.id
4985
- WHERE d.relative_path IN (${placeholders})
4986
- ${db.symbolNoise}
4987
- ORDER BY d.relative_path, der.start_line`,
4988
- ...resolvedPaths
4967
+ const definitions = resolvedPaths.flatMap((relativePath) => getDefinitionsForFile(db, relativePath)).filter((d) => !db.isIgnored(d.relativePath)).sort(
4968
+ (a, b) => a.relativePath.localeCompare(b.relativePath) || a.startLine - b.startLine || a.endLine - b.endLine
4989
4969
  );
4990
- const nodes = rows.map((r) => ({
4991
- symbol: r.symbol,
4992
- shortName: shortenSymbol(r.symbol),
4993
- startLine: r.start_line,
4994
- endLine: r.end_line,
4970
+ const nodes = definitions.map((d) => ({
4971
+ symbol: d.symbol,
4972
+ shortName: shortenSymbol(d.symbol),
4973
+ startLine: d.startLine,
4974
+ endLine: d.endLine,
4995
4975
  children: []
4996
4976
  }));
4997
4977
  const nodeMap = /* @__PURE__ */ new Map();
4998
4978
  for (const n of nodes) nodeMap.set(n.symbol, n);
4999
4979
  const roots = [];
5000
- for (let i = 0; i < rows.length; i++) {
5001
- const r = rows[i];
4980
+ for (let i = 0; i < definitions.length; i++) {
4981
+ const d = definitions[i];
5002
4982
  const node = nodes[i];
5003
- if (r.enclosing_symbol && nodeMap.has(r.enclosing_symbol)) {
5004
- nodeMap.get(r.enclosing_symbol).children.push(node);
4983
+ if (d.enclosingSymbol && nodeMap.has(d.enclosingSymbol)) {
4984
+ nodeMap.get(d.enclosingSymbol).children.push(node);
5005
4985
  continue;
5006
4986
  }
5007
4987
  let bestParent = null;
@@ -5029,23 +5009,12 @@ function outline(db, filePattern) {
5029
5009
  function members(db, symbolPattern) {
5030
5010
  const parent = findFirstSymbolMatch(db, symbolPattern);
5031
5011
  if (!parent) return [];
5032
- const rows = db.all(
5033
- `SELECT gs.symbol, der.start_line, der.end_line
5034
- FROM global_symbols gs
5035
- JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
5036
- WHERE der.document_id = ?
5037
- AND gs.symbol != ?
5038
- ${db.symbolNoiseFor("gs")}
5039
- ORDER BY der.start_line`,
5040
- parent.documentId,
5041
- parent.symbol
5042
- );
5043
- return rows.filter((row) => isDirectChildSymbol(parent.symbol, row.symbol)).map((row) => ({
5044
- symbol: row.symbol,
5045
- shortName: shortenSymbol(row.symbol),
5046
- startLine: row.start_line,
5047
- endLine: row.end_line,
5048
- kind: leafSuffix(row.symbol) ?? "unknown"
5012
+ return getDefinitionsForFile(db, parent.relativePath).filter((definition) => definition.symbol !== parent.symbol).filter((definition) => isDirectChildSymbol(parent.symbol, definition.symbol)).sort((a, b) => a.startLine - b.startLine || a.endLine - b.endLine).map((definition) => ({
5013
+ symbol: definition.symbol,
5014
+ shortName: shortenSymbol(definition.symbol),
5015
+ startLine: definition.startLine,
5016
+ endLine: definition.endLine,
5017
+ kind: leafSuffix(definition.symbol) ?? "unknown"
5049
5018
  }));
5050
5019
  }
5051
5020
 
@@ -5839,35 +5808,21 @@ function findCallees(db, symbolPattern) {
5839
5808
  }
5840
5809
  function getAllCalleeFingerprints(db, opts) {
5841
5810
  const { minCallees, scope, excludeSymbol } = opts;
5842
- const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : "";
5843
- const excludeFilter = excludeSymbol ? `AND gs.symbol != '${excludeSymbol.replace(/'/g, "''")}'` : "";
5844
- const symbols2 = db.all(
5845
- `SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path
5846
- FROM global_symbols gs
5847
- JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
5848
- JOIN documents d ON der.document_id = d.id
5849
- WHERE 1 = 1
5850
- ${db.pathExclusionsFor("d")}
5851
- ${db.symbolNoiseFor("gs")}
5852
- AND (der.end_line - der.start_line + 1) >= 5
5853
- ${scopeFilter}
5854
- ${excludeFilter}
5855
- ORDER BY d.relative_path`
5856
- );
5857
5811
  const fingerprints = [];
5858
- for (const sym of symbols2) {
5859
- if (db.isIgnored(sym.relative_path)) continue;
5860
- if (!isFunctionLikeSymbol(sym.symbol)) continue;
5861
- const calleeRows = getCalleeRowsForSymbol(db, {
5862
- documentId: sym.document_id,
5863
- startLine: sym.start_line,
5864
- endLine: sym.end_line,
5865
- symbolId: sym.id
5812
+ for (const definition of getAllDefinitions(db, { scope })) {
5813
+ if (db.isIgnored(definition.relativePath)) continue;
5814
+ if (!definition.isFunctionLike) continue;
5815
+ if (excludeSymbol && definition.symbol === excludeSymbol) continue;
5816
+ if (definition.endLine - definition.startLine + 1 < 5) continue;
5817
+ const callees = new Set(
5818
+ getCalleeRowsForSymbol(db, definition).map((row) => row.symbol)
5819
+ );
5820
+ if (callees.size < minCallees) continue;
5821
+ fingerprints.push({
5822
+ symbol: definition.symbol,
5823
+ file: definition.relativePath,
5824
+ callees
5866
5825
  });
5867
- const callees = new Set(calleeRows.map((r) => r.symbol));
5868
- if (callees.size >= minCallees) {
5869
- fingerprints.push({ symbol: sym.symbol, file: sym.relative_path, callees });
5870
- }
5871
5826
  }
5872
5827
  return fingerprints;
5873
5828
  }
@@ -6485,18 +6440,10 @@ function changeSurface(db, filePattern) {
6485
6440
  resolvedFile
6486
6441
  );
6487
6442
  if (!doc || db.isIgnored(doc.relative_path)) return null;
6488
- const syms = db.all(
6489
- `SELECT DISTINCT gs.id AS symbol_id, gs.symbol, der.start_line, der.end_line
6490
- FROM defn_enclosing_ranges der
6491
- JOIN global_symbols gs ON der.symbol_id = gs.id
6492
- WHERE der.document_id = ?
6493
- ${db.symbolNoiseFor("gs")}
6494
- ORDER BY der.start_line`,
6495
- doc.id
6496
- );
6443
+ const definitions = getDefinitionsForFile(db, doc.relative_path).sort((a, b) => a.startLine - b.startLine || a.endLine - b.endLine);
6497
6444
  const symbols2 = [];
6498
6445
  let totalExternalConsumers = 0;
6499
- for (const sym of syms) {
6446
+ for (const def of definitions) {
6500
6447
  const consumerRow = db.get(
6501
6448
  `SELECT COUNT(DISTINCT c.document_id) AS consumer_count
6502
6449
  FROM mentions m
@@ -6504,7 +6451,7 @@ function changeSurface(db, filePattern) {
6504
6451
  WHERE m.symbol_id = ?
6505
6452
  AND m.role != 1
6506
6453
  AND c.document_id != ?`,
6507
- sym.symbol_id,
6454
+ def.symbolId,
6508
6455
  doc.id
6509
6456
  );
6510
6457
  const externalConsumers = consumerRow?.consumer_count ?? 0;
@@ -6518,10 +6465,10 @@ function changeSurface(db, filePattern) {
6518
6465
  }
6519
6466
  totalExternalConsumers += externalConsumers;
6520
6467
  symbols2.push({
6521
- symbol: sym.symbol,
6522
- shortName: shortenSymbol(sym.symbol),
6523
- startLine: sym.start_line,
6524
- endLine: sym.end_line,
6468
+ symbol: def.symbol,
6469
+ shortName: shortenSymbol(def.symbol),
6470
+ startLine: def.startLine,
6471
+ endLine: def.endLine,
6525
6472
  externalConsumers,
6526
6473
  riskLevel
6527
6474
  });
@@ -7015,95 +6962,6 @@ function definitionLoc4(definition) {
7015
6962
  // src/queries/complexity-hotspots.ts
7016
6963
  function complexityHotspots(db, opts) {
7017
6964
  const { scope, minLoc = 10, limit = 30 } = opts ?? {};
7018
- const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : "";
7019
- const rows = db.all(
7020
- `SELECT
7021
- gs.symbol,
7022
- d.relative_path AS file,
7023
- der.start_line,
7024
- der.end_line,
7025
- (der.end_line - der.start_line + 1) AS loc,
7026
- -- fanIn: distinct files that reference this symbol
7027
- (SELECT COUNT(DISTINCT ref_c.document_id)
7028
- FROM mentions ref_m
7029
- JOIN chunks ref_c ON ref_m.chunk_id = ref_c.id
7030
- WHERE ref_m.symbol_id = gs.id AND ref_m.role != 1
7031
- ) AS fan_in,
7032
- -- fanOut: distinct symbols referenced within this definition range
7033
- -- that are defined in different files
7034
- (SELECT COUNT(DISTINCT out_gs.id)
7035
- FROM mentions out_m
7036
- JOIN chunks out_c ON out_m.chunk_id = out_c.id
7037
- JOIN global_symbols out_gs ON out_m.symbol_id = out_gs.id
7038
- JOIN defn_enclosing_ranges out_der ON out_gs.id = out_der.symbol_id
7039
- WHERE out_c.document_id = der.document_id
7040
- AND out_c.start_line >= der.start_line
7041
- AND out_c.end_line <= der.end_line
7042
- AND out_m.role != 1
7043
- AND out_gs.id != gs.id
7044
- AND out_der.document_id != der.document_id
7045
- ) AS fan_out,
7046
- -- calleeCount: total distinct callees within definition range
7047
- (SELECT COUNT(DISTINCT callee_gs.id)
7048
- FROM mentions callee_m
7049
- JOIN chunks callee_c ON callee_m.chunk_id = callee_c.id
7050
- JOIN global_symbols callee_gs ON callee_m.symbol_id = callee_gs.id
7051
- WHERE callee_c.document_id = der.document_id
7052
- AND callee_c.start_line >= der.start_line
7053
- AND callee_c.end_line <= der.end_line
7054
- AND callee_m.role != 1
7055
- AND callee_gs.id != gs.id
7056
- ) AS callee_count
7057
- FROM global_symbols gs
7058
- JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
7059
- JOIN documents d ON der.document_id = d.id
7060
- WHERE 1 = 1
7061
- ${db.pathExclusionsFor("d")}
7062
- AND ${testFileExclusionSql("d")}
7063
- ${db.symbolNoiseFor("gs")}
7064
- AND (der.end_line - der.start_line + 1) >= ?
7065
- ${scopeFilter}
7066
- ORDER BY (
7067
- CAST((der.end_line - der.start_line + 1) AS REAL) / 50.0
7068
- * CAST((SELECT COUNT(DISTINCT ref_c2.document_id)
7069
- FROM mentions ref_m2
7070
- JOIN chunks ref_c2 ON ref_m2.chunk_id = ref_c2.id
7071
- WHERE ref_m2.symbol_id = gs.id AND ref_m2.role != 1
7072
- ) AS REAL) / 5.0
7073
- * MAX(CAST((SELECT COUNT(DISTINCT out_gs2.id)
7074
- FROM mentions out_m2
7075
- JOIN chunks out_c2 ON out_m2.chunk_id = out_c2.id
7076
- JOIN global_symbols out_gs2 ON out_m2.symbol_id = out_gs2.id
7077
- JOIN defn_enclosing_ranges out_der2 ON out_gs2.id = out_der2.symbol_id
7078
- WHERE out_c2.document_id = der.document_id
7079
- AND out_c2.start_line >= der.start_line
7080
- AND out_c2.end_line <= der.end_line
7081
- AND out_m2.role != 1
7082
- AND out_gs2.id != gs.id
7083
- AND out_der2.document_id != der.document_id
7084
- ) AS REAL) / 5.0, 1.0)
7085
- ) DESC
7086
- LIMIT ?`,
7087
- minLoc,
7088
- limit
7089
- );
7090
- const indexedResults = rows.filter((r) => !db.isIgnored(r.file)).map((r) => ({
7091
- symbol: r.symbol,
7092
- shortName: shortenSymbol(r.symbol),
7093
- file: r.file,
7094
- startLine: r.start_line,
7095
- endLine: r.end_line,
7096
- loc: r.loc,
7097
- fanIn: r.fan_in,
7098
- fanOut: r.fan_out,
7099
- calleeCount: r.callee_count,
7100
- score: Math.round(
7101
- r.loc / 50 * (r.fan_in / 5) * Math.max(r.fan_out / 5, 1) * 100
7102
- ) / 100
7103
- }));
7104
- if (indexedResults.length > 0) {
7105
- return indexedResults;
7106
- }
7107
6965
  return getAllDefinitions(db, { scope }).filter((definition) => !db.isIgnored(definition.relativePath)).map((definition) => {
7108
6966
  const loc = definition.endLine - definition.startLine + 1;
7109
6967
  const callerRows = getCallerRowsForSymbol(db, definition, { limit: 500 });
@@ -7548,34 +7406,12 @@ function dataflow(db, symbolPattern) {
7548
7406
  line: match.startLine
7549
7407
  }];
7550
7408
  const sourceUsageSites = getSourceReferenceSites(db, match);
7551
- const usageSites = sourceUsageSites.length > 0 ? sourceUsageSites.map((site) => ({
7552
- file: site.file,
7553
- line: site.line,
7554
- enclosing_symbol: site.enclosingSymbol
7555
- })) : db.all(
7556
- `SELECT d.relative_path AS file, c.start_line AS line,
7557
- (SELECT enc_gs.symbol
7558
- FROM defn_enclosing_ranges enc_der
7559
- JOIN global_symbols enc_gs ON enc_der.symbol_id = enc_gs.id
7560
- WHERE enc_der.document_id = d.id
7561
- AND enc_der.start_line <= c.start_line
7562
- AND enc_der.end_line >= c.end_line
7563
- ORDER BY (enc_der.end_line - enc_der.start_line) ASC
7564
- LIMIT 1
7565
- ) AS enclosing_symbol
7566
- FROM mentions m
7567
- JOIN chunks c ON m.chunk_id = c.id
7568
- JOIN documents d ON c.document_id = d.id
7569
- WHERE m.symbol_id = ? AND m.role != 1
7570
- ${db.pathExclusionsFor("d")}
7571
- ORDER BY d.relative_path, c.start_line`,
7572
- match.symbolId
7573
- );
7574
- const normalizedUsageSites = usageSites.filter((site) => !db.isIgnored(site.file)).map((site) => ({
7409
+ const resolvedSites = sourceUsageSites.length > 0 ? sourceUsageSites : getResolvedReferenceSites(db, match);
7410
+ const normalizedUsageSites = resolvedSites.filter((site) => !db.isIgnored(site.file)).map((site) => ({
7575
7411
  file: site.file,
7576
7412
  line: site.line,
7577
- enclosingSymbol: site.enclosing_symbol ?? "(top-level)",
7578
- enclosingShort: site.enclosing_symbol ? shortenSymbol(site.enclosing_symbol) : "(top-level)"
7413
+ enclosingSymbol: site.enclosingSymbol ?? "(top-level)",
7414
+ enclosingShort: site.enclosingSymbol ? shortenSymbol(site.enclosingSymbol) : "(top-level)"
7579
7415
  }));
7580
7416
  const producers = uniqueSymbolRows(getCalleeRowsForSymbol(db, match, { limit: 30 }).map((row) => ({
7581
7417
  symbol: row.symbol,
@@ -7658,53 +7494,37 @@ function backwardSlice(db, match, maxDepth) {
7658
7494
  };
7659
7495
  }
7660
7496
  function forwardSlice(db, match) {
7661
- const rows = db.all(
7662
- `SELECT DISTINCT
7663
- enc_gs.symbol AS enclosing_symbol,
7664
- enc_d.relative_path AS enclosing_file,
7665
- out_gs.symbol AS output_symbol,
7666
- out_d.relative_path AS output_file
7667
- FROM mentions ref_m
7668
- JOIN chunks ref_c ON ref_m.chunk_id = ref_c.id
7669
- JOIN documents ref_d ON ref_c.document_id = ref_d.id
7670
- -- Find enclosing function at each reference site
7671
- JOIN defn_enclosing_ranges enc_der
7672
- ON enc_der.document_id = ref_d.id
7673
- AND enc_der.start_line <= ref_c.start_line
7674
- AND enc_der.end_line >= ref_c.end_line
7675
- JOIN global_symbols enc_gs ON enc_der.symbol_id = enc_gs.id
7676
- JOIN documents enc_d ON enc_der.document_id = enc_d.id
7677
- -- Find other symbols referenced within that enclosing function
7678
- JOIN mentions out_m ON out_m.role != 1
7679
- JOIN chunks out_c ON out_m.chunk_id = out_c.id
7680
- AND out_c.document_id = enc_der.document_id
7681
- AND out_c.start_line >= enc_der.start_line
7682
- AND out_c.end_line <= enc_der.end_line
7683
- JOIN global_symbols out_gs ON out_m.symbol_id = out_gs.id
7684
- JOIN defn_enclosing_ranges out_der ON out_gs.id = out_der.symbol_id
7685
- JOIN documents out_d ON out_der.document_id = out_d.id
7686
- WHERE ref_m.symbol_id = ? AND ref_m.role != 1
7687
- AND out_gs.id != ? AND out_gs.id != enc_gs.id
7688
- AND out_d.id != ref_d.id
7689
- ${db.symbolNoiseFor("out_gs")}
7690
- ${db.pathExclusionsFor("out_d")}
7691
- ORDER BY out_d.relative_path
7692
- LIMIT 30`,
7693
- match.symbolId,
7694
- match.symbolId
7695
- );
7696
- const seen = /* @__PURE__ */ new Set();
7497
+ const sourceRefs = getSourceReferenceSites(db, match);
7498
+ const refs2 = sourceRefs.length > 0 ? sourceRefs : getResolvedReferenceSites(db, match);
7499
+ const seenOutputs = /* @__PURE__ */ new Set();
7697
7500
  const connected = [];
7698
- for (const r of rows) {
7699
- if (seen.has(r.output_symbol) || db.isIgnored(r.output_file)) continue;
7700
- seen.add(r.output_symbol);
7701
- connected.push({
7702
- symbol: r.output_symbol,
7703
- shortName: shortenSymbol(r.output_symbol),
7704
- file: r.output_file,
7705
- relationship: `used alongside target in ${shortenSymbol(r.enclosing_symbol)}`
7706
- });
7501
+ for (const ref of refs2) {
7502
+ if (connected.length >= 30) break;
7503
+ if (db.isIgnored(ref.file)) continue;
7504
+ const enclosingSymbol = ref.enclosingSymbol ?? findEnclosingDefinition(
7505
+ getDefinitionsForFile(db, ref.file),
7506
+ ref.line
7507
+ )?.symbol ?? null;
7508
+ if (!enclosingSymbol || enclosingSymbol === match.symbol) continue;
7509
+ const enclosingMatch = findExactSymbolMatch(db, enclosingSymbol);
7510
+ if (!enclosingMatch) continue;
7511
+ for (const callee of getCalleeRowsForSymbol(db, enclosingMatch)) {
7512
+ if (callee.symbol === match.symbol) continue;
7513
+ if (callee.symbol === enclosingSymbol) continue;
7514
+ if (callee.file === ref.file) continue;
7515
+ if (db.isIgnored(callee.file)) continue;
7516
+ if (seenOutputs.has(callee.symbol)) continue;
7517
+ seenOutputs.add(callee.symbol);
7518
+ connected.push({
7519
+ symbol: callee.symbol,
7520
+ shortName: shortenSymbol(callee.symbol),
7521
+ file: callee.file,
7522
+ relationship: `used alongside target in ${shortenSymbol(enclosingSymbol)}`
7523
+ });
7524
+ if (connected.length >= 30) break;
7525
+ }
7707
7526
  }
7527
+ connected.sort((a, b) => a.file.localeCompare(b.file));
7708
7528
  return {
7709
7529
  symbol: match.symbol,
7710
7530
  shortName: shortenSymbol(match.symbol),