scip-query 0.4.1 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-JHVQB4Y5.js → chunk-334PCFO3.js} +12 -12
- package/dist/{chunk-OXX3QF24.js → chunk-46ZTW4AI.js} +2 -2
- package/dist/{chunk-24LF6IZB.js → chunk-4YN3PE57.js} +3 -3
- package/dist/{chunk-3NJSJ7TE.js → chunk-5YB6UXQ3.js} +4 -15
- package/dist/{chunk-Z5VSUOEE.js → chunk-6PVHJ332.js} +2 -2
- package/dist/{chunk-EAGKJFDX.js → chunk-7KGTWDAX.js} +3 -3
- package/dist/{chunk-PU2254N2.js → chunk-7OGXSMLY.js} +5 -15
- package/dist/{chunk-R7HPHMRZ.js → chunk-AEBM56CO.js} +3 -3
- package/dist/{chunk-SYQR4QGK.js → chunk-B747RITP.js} +3 -3
- package/dist/{chunk-A6XLXV6W.js → chunk-B7LDMCUS.js} +3 -3
- package/dist/{chunk-KLNKDX6A.js → chunk-C2VSV54P.js} +4 -4
- package/dist/{chunk-VKBOLNYN.js → chunk-DIYEUFVP.js} +10 -8
- package/dist/{chunk-RE7POFGI.js → chunk-E74RY6AQ.js} +2 -2
- package/dist/{chunk-RL74LF47.js → chunk-FIMTTUGE.js} +2 -2
- package/dist/{chunk-ALUFWH3U.js → chunk-FMAYH7GS.js} +116 -269
- package/dist/{chunk-MGNMHKX3.js → chunk-FO2CBB7U.js} +10 -2
- package/dist/{chunk-7BS4CPJX.js → chunk-HESWGDIV.js} +3 -3
- package/dist/{chunk-FVJE4MQL.js → chunk-HL2LXSBW.js} +5 -8
- package/dist/{chunk-KG4OFQEN.js → chunk-HW76DVE4.js} +3 -3
- package/dist/{chunk-RJ5GULL6.js → chunk-IC5NTO47.js} +2 -2
- package/dist/{chunk-VY2L4TP6.js → chunk-J34HAAEQ.js} +3 -3
- package/dist/{chunk-PCU455MX.js → chunk-JSXGC2EH.js} +2 -2
- package/dist/{chunk-NXUIWD6K.js → chunk-LQXBFCP2.js} +3 -3
- package/dist/{chunk-GNAMV3JC.js → chunk-NML6M5AS.js} +3 -3
- package/dist/{chunk-UJWI5CBB.js → chunk-NNFP4ZRF.js} +4 -7
- package/dist/{chunk-6ECR2FLR.js → chunk-NWXTQGUE.js} +4 -15
- package/dist/{chunk-ZVZAIIB5.js → chunk-NYZ6INK3.js} +3 -3
- package/dist/{chunk-JKXHHV4B.js → chunk-OMVF3BHY.js} +2 -2
- package/dist/{chunk-CBIWNZZZ.js → chunk-PGQXIAJF.js} +3 -3
- package/dist/{chunk-J47VSL6I.js → chunk-PKDFXASW.js} +3 -3
- package/dist/{chunk-POLELLNM.js → chunk-PSK5BPFE.js} +3 -3
- package/dist/{chunk-6CH23IAS.js → chunk-QZ4FRB65.js} +9 -7
- package/dist/{chunk-TWVXFKJA.js → chunk-R2QBMQQN.js} +4 -4
- package/dist/{chunk-5GCORUNV.js → chunk-T3ALCNCP.js} +30 -17
- package/dist/{chunk-QMXSLHZP.js → chunk-T6UVM534.js} +2 -2
- package/dist/{chunk-6UZU7DFL.js → chunk-U74VYTLX.js} +3 -3
- package/dist/{chunk-XUVPQDXW.js → chunk-UIRCHPOU.js} +4 -4
- package/dist/{chunk-43A4UCS7.js → chunk-UNS6ZQVX.js} +3 -3
- package/dist/{chunk-J6QXMYAQ.js → chunk-VJMTX3OR.js} +3 -3
- package/dist/{chunk-SVLUJSY7.js → chunk-XJSPWHNT.js} +4 -15
- package/dist/{chunk-W46L2BXT.js → chunk-XMZAC2VU.js} +2 -2
- package/dist/{chunk-ELFGD5EW.js → chunk-Y7FKURZG.js} +3 -3
- package/dist/{chunk-TO3L4YNK.js → chunk-YMSJCSRG.js} +5 -1
- package/dist/{chunk-DUJNJQPO.js → chunk-YQIWS5V6.js} +3 -3
- package/dist/{chunk-KYPXKV64.js → chunk-ZPEI7DRJ.js} +30 -15
- package/dist/cli.js +471 -473
- package/dist/{db-C4rPbKkI.d.ts → db-6F9R9e_t.d.ts} +0 -4
- package/dist/index.d.ts +20 -5
- package/dist/index.js +237 -81
- package/dist/queries/affected.d.ts +1 -1
- package/dist/queries/affected.js +3 -3
- package/dist/queries/bottlenecks.d.ts +1 -1
- package/dist/queries/bottlenecks.js +3 -3
- package/dist/queries/by-kind.d.ts +1 -1
- package/dist/queries/by-kind.js +3 -3
- package/dist/queries/call-graph.d.ts +1 -1
- package/dist/queries/call-graph.js +3 -3
- package/dist/queries/change-surface.d.ts +1 -1
- package/dist/queries/change-surface.js +3 -3
- package/dist/queries/code.d.ts +1 -1
- package/dist/queries/code.js +3 -3
- package/dist/queries/complexity-hotspots.d.ts +1 -1
- package/dist/queries/complexity-hotspots.js +3 -3
- package/dist/queries/complexity.d.ts +1 -1
- package/dist/queries/complexity.js +3 -3
- package/dist/queries/convergence.d.ts +1 -1
- package/dist/queries/convergence.js +3 -3
- package/dist/queries/coupling.d.ts +1 -1
- package/dist/queries/coupling.js +3 -3
- package/dist/queries/cycles.d.ts +1 -1
- package/dist/queries/cycles.js +3 -3
- package/dist/queries/dataflow.d.ts +1 -1
- package/dist/queries/dataflow.js +3 -3
- package/dist/queries/dead.d.ts +1 -1
- package/dist/queries/dead.js +4 -4
- package/dist/queries/deep-chains.d.ts +1 -1
- package/dist/queries/deep-chains.js +3 -3
- package/dist/queries/deps.d.ts +1 -1
- package/dist/queries/deps.js +3 -3
- package/dist/queries/diff-impact.d.ts +1 -1
- package/dist/queries/diff-impact.js +2 -2
- package/dist/queries/drift.d.ts +1 -1
- package/dist/queries/drift.js +3 -3
- package/dist/queries/extract-candidates.d.ts +1 -1
- package/dist/queries/extract-candidates.js +3 -3
- package/dist/queries/fan.d.ts +1 -1
- package/dist/queries/fan.js +3 -3
- package/dist/queries/files.d.ts +1 -1
- package/dist/queries/files.js +1 -1
- package/dist/queries/health.d.ts +1 -1
- package/dist/queries/health.js +14 -14
- package/dist/queries/hierarchy.d.ts +1 -1
- package/dist/queries/hierarchy.js +3 -3
- package/dist/queries/hotspots.d.ts +1 -1
- package/dist/queries/hotspots.js +3 -3
- package/dist/queries/imports.d.ts +1 -1
- package/dist/queries/imports.js +3 -3
- package/dist/queries/index.d.ts +1 -1
- package/dist/queries/index.js +45 -45
- package/dist/queries/isolated.d.ts +1 -1
- package/dist/queries/isolated.js +4 -4
- package/dist/queries/members.d.ts +1 -1
- package/dist/queries/members.js +3 -3
- package/dist/queries/methods.d.ts +1 -1
- package/dist/queries/methods.js +3 -3
- package/dist/queries/outline.d.ts +1 -1
- package/dist/queries/outline.js +3 -3
- package/dist/queries/passthrough-candidates.d.ts +1 -1
- package/dist/queries/passthrough-candidates.js +3 -3
- package/dist/queries/redundant-reexports.d.ts +1 -1
- package/dist/queries/redundant-reexports.js +4 -4
- package/dist/queries/refs.d.ts +1 -1
- package/dist/queries/refs.js +3 -3
- package/dist/queries/similar-chains.d.ts +1 -1
- package/dist/queries/similar-chains.js +3 -3
- package/dist/queries/similar-files.d.ts +1 -1
- package/dist/queries/similar-files.js +3 -3
- package/dist/queries/similar-signatures.d.ts +1 -1
- package/dist/queries/similar-signatures.js +3 -3
- package/dist/queries/similar.d.ts +2 -1
- package/dist/queries/similar.js +3 -3
- package/dist/queries/slice.d.ts +4 -3
- package/dist/queries/slice.js +3 -3
- package/dist/queries/stale-abstractions.d.ts +1 -1
- package/dist/queries/stale-abstractions.js +3 -3
- package/dist/queries/stats.d.ts +1 -1
- package/dist/queries/surface.d.ts +1 -1
- package/dist/queries/surface.js +3 -3
- package/dist/queries/symbols.d.ts +1 -1
- package/dist/queries/symbols.js +3 -3
- package/dist/queries/system.d.ts +1 -1
- package/dist/queries/system.js +3 -3
- package/dist/queries/trace.d.ts +1 -1
- package/dist/queries/trace.js +3 -3
- package/dist/queries/wrapper-candidates.d.ts +1 -1
- package/dist/queries/wrapper-candidates.js +3 -3
- package/dist/reindex-worker.js +146 -5
- package/package.json +3 -1
package/dist/cli.js
CHANGED
|
@@ -20,18 +20,10 @@ var ScipDatabase = class {
|
|
|
20
20
|
this.db = new Database(config.dbPath, { readonly: true });
|
|
21
21
|
this.db.pragma("busy_timeout = 5000");
|
|
22
22
|
}
|
|
23
|
-
/** Attach a gitignore-based path filter for query results */
|
|
24
|
-
setPathFilter(filter) {
|
|
25
|
-
this.pathFilter = filter;
|
|
26
|
-
}
|
|
27
23
|
/** Check if a path should be excluded based on .gitignore rules */
|
|
28
24
|
isIgnored(relativePath) {
|
|
29
25
|
return this.pathFilter?.isIgnored(relativePath) ?? false;
|
|
30
26
|
}
|
|
31
|
-
/** Filter an array of paths using the gitignore filter */
|
|
32
|
-
filterPaths(paths) {
|
|
33
|
-
return this.pathFilter?.filter(paths) ?? paths;
|
|
34
|
-
}
|
|
35
27
|
/**
|
|
36
28
|
* The local-symbol predicate: only match symbols that are defined
|
|
37
29
|
* in files NOT excluded by gitignore. This replaces the old hardcoded
|
|
@@ -115,7 +107,7 @@ var ScipDatabase = class {
|
|
|
115
107
|
// src/gitignore-filter.ts
|
|
116
108
|
import ignore from "ignore";
|
|
117
109
|
import { readFileSync, existsSync } from "fs";
|
|
118
|
-
import { join,
|
|
110
|
+
import { dirname, isAbsolute, join, relative, resolve } from "path";
|
|
119
111
|
function createGitignoreFilter(projectRoot) {
|
|
120
112
|
const ig = ignore();
|
|
121
113
|
let loaded = false;
|
|
@@ -132,8 +124,8 @@ function createGitignoreFilter(projectRoot) {
|
|
|
132
124
|
ig.add(DEFAULT_IGNORES);
|
|
133
125
|
}
|
|
134
126
|
return {
|
|
135
|
-
isIgnored: (relativePath) => ig
|
|
136
|
-
filter: (paths) => paths.filter((p) => !ig
|
|
127
|
+
isIgnored: (relativePath) => safeIgnores(ig, projectRoot, relativePath),
|
|
128
|
+
filter: (paths) => paths.filter((p) => !safeIgnores(ig, projectRoot, p))
|
|
137
129
|
};
|
|
138
130
|
}
|
|
139
131
|
function findGitignoreFiles(projectRoot) {
|
|
@@ -213,10 +205,35 @@ Thumbs.db
|
|
|
213
205
|
# Type definitions (often noise in queries)
|
|
214
206
|
*.d.ts
|
|
215
207
|
`;
|
|
208
|
+
function safeIgnores(ig, projectRoot, inputPath) {
|
|
209
|
+
const relativePath = normalizeForIgnore(projectRoot, inputPath);
|
|
210
|
+
if (!relativePath) {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
return ig.ignores(relativePath);
|
|
215
|
+
} catch {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function normalizeForIgnore(projectRoot, inputPath) {
|
|
220
|
+
if (!inputPath || inputPath === ".") {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
if (!isAbsolute(inputPath) && !inputPath.startsWith("..")) {
|
|
224
|
+
return inputPath.replaceAll("\\", "/");
|
|
225
|
+
}
|
|
226
|
+
const absolutePath = isAbsolute(inputPath) ? inputPath : resolve(projectRoot, inputPath);
|
|
227
|
+
const relativePath = relative(projectRoot, absolutePath).replaceAll("\\", "/");
|
|
228
|
+
if (!relativePath || relativePath === "." || relativePath.startsWith("..")) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
return relativePath;
|
|
232
|
+
}
|
|
216
233
|
|
|
217
234
|
// src/config.ts
|
|
218
235
|
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
|
|
219
|
-
import { join as join2, resolve } from "path";
|
|
236
|
+
import { join as join2, resolve as resolve2 } from "path";
|
|
220
237
|
import { createHash } from "crypto";
|
|
221
238
|
import { homedir } from "os";
|
|
222
239
|
var CONFIG_FILENAME = ".scipquery.json";
|
|
@@ -247,10 +264,10 @@ function resolveWatchConfig(config) {
|
|
|
247
264
|
function resolveCacheDir(projectRoot, config) {
|
|
248
265
|
const envOverride = process.env["SCIP_QUERY_CACHE_DIR"];
|
|
249
266
|
if (envOverride) return ensureDir(envOverride);
|
|
250
|
-
if (config?.dbPath) return ensureDir(
|
|
267
|
+
if (config?.dbPath) return ensureDir(resolve2(projectRoot, config.dbPath));
|
|
251
268
|
const xdgCache = process.env["XDG_CACHE_HOME"];
|
|
252
269
|
const cacheBase = xdgCache || join2(homedir(), ".cache");
|
|
253
|
-
const projectHash = createHash("sha256").update(
|
|
270
|
+
const projectHash = createHash("sha256").update(resolve2(projectRoot)).digest("hex").slice(0, 12);
|
|
254
271
|
const dir = join2(cacheBase, "scip-query", "projects", projectHash);
|
|
255
272
|
return ensureDir(dir);
|
|
256
273
|
}
|
|
@@ -286,8 +303,8 @@ function ensureDir(dir) {
|
|
|
286
303
|
|
|
287
304
|
// src/reindex/index.ts
|
|
288
305
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
289
|
-
import { existsSync as existsSync6, renameSync } from "fs";
|
|
290
|
-
import { join as join6 } from "path";
|
|
306
|
+
import { existsSync as existsSync6, renameSync, rmSync } from "fs";
|
|
307
|
+
import { basename, dirname as dirname2, extname as extname2, join as join6 } from "path";
|
|
291
308
|
|
|
292
309
|
// src/scip-cli.ts
|
|
293
310
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
@@ -968,6 +985,128 @@ function resolveDotnetProject(projectRoot, suffixes) {
|
|
|
968
985
|
return null;
|
|
969
986
|
}
|
|
970
987
|
|
|
988
|
+
// src/reindex/merge.ts
|
|
989
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
990
|
+
import { create } from "@bufbuild/protobuf";
|
|
991
|
+
import {
|
|
992
|
+
deserializeSCIP,
|
|
993
|
+
serializeSCIP,
|
|
994
|
+
DocumentSchema,
|
|
995
|
+
IndexSchema,
|
|
996
|
+
SymbolInformationSchema
|
|
997
|
+
} from "@c4312/scip";
|
|
998
|
+
function mergeScipIndexes(indexes) {
|
|
999
|
+
if (indexes.length === 0) {
|
|
1000
|
+
throw new Error("Cannot merge zero SCIP indexes");
|
|
1001
|
+
}
|
|
1002
|
+
if (indexes.length === 1) {
|
|
1003
|
+
return indexes[0];
|
|
1004
|
+
}
|
|
1005
|
+
const metadata = mergeMetadata(indexes);
|
|
1006
|
+
const documents = mergeDocuments(indexes.flatMap((index) => index.documents ?? []));
|
|
1007
|
+
const externalSymbols = mergeSymbolInfos(indexes.flatMap((index) => index.externalSymbols ?? []));
|
|
1008
|
+
return create(IndexSchema, {
|
|
1009
|
+
metadata,
|
|
1010
|
+
documents,
|
|
1011
|
+
externalSymbols
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
function mergeScipFiles(inputPaths, outputPath) {
|
|
1015
|
+
if (inputPaths.length === 0) {
|
|
1016
|
+
throw new Error("Cannot merge zero SCIP files");
|
|
1017
|
+
}
|
|
1018
|
+
const indexes = inputPaths.map((path2) => deserializeSCIP(readFileSync3(path2)));
|
|
1019
|
+
const merged = mergeScipIndexes(indexes);
|
|
1020
|
+
writeFileSync2(outputPath, Buffer.from(serializeSCIP(merged)));
|
|
1021
|
+
return {
|
|
1022
|
+
documentCount: merged.documents.length,
|
|
1023
|
+
externalSymbolCount: merged.externalSymbols.length,
|
|
1024
|
+
inputCount: inputPaths.length
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
function mergeMetadata(indexes) {
|
|
1028
|
+
const first = indexes[0]?.metadata;
|
|
1029
|
+
if (!first) {
|
|
1030
|
+
return void 0;
|
|
1031
|
+
}
|
|
1032
|
+
const expectedProjectRoot = first.projectRoot;
|
|
1033
|
+
for (const index of indexes.slice(1)) {
|
|
1034
|
+
const actualProjectRoot = index.metadata?.projectRoot;
|
|
1035
|
+
if (expectedProjectRoot && actualProjectRoot && actualProjectRoot !== expectedProjectRoot) {
|
|
1036
|
+
throw new Error(
|
|
1037
|
+
`Cannot merge SCIP indexes with different project roots: ${expectedProjectRoot} vs ${actualProjectRoot}`
|
|
1038
|
+
);
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
return first;
|
|
1042
|
+
}
|
|
1043
|
+
function mergeDocuments(documents) {
|
|
1044
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
1045
|
+
for (const document of documents) {
|
|
1046
|
+
const existing = byPath.get(document.relativePath);
|
|
1047
|
+
if (!existing) {
|
|
1048
|
+
byPath.set(document.relativePath, document);
|
|
1049
|
+
continue;
|
|
1050
|
+
}
|
|
1051
|
+
byPath.set(document.relativePath, create(DocumentSchema, {
|
|
1052
|
+
language: existing.language || document.language,
|
|
1053
|
+
relativePath: existing.relativePath || document.relativePath,
|
|
1054
|
+
occurrences: [...existing.occurrences, ...document.occurrences],
|
|
1055
|
+
symbols: mergeSymbolInfos([...existing.symbols, ...document.symbols]),
|
|
1056
|
+
text: chooseText(existing.text, document.text),
|
|
1057
|
+
positionEncoding: existing.positionEncoding || document.positionEncoding
|
|
1058
|
+
}));
|
|
1059
|
+
}
|
|
1060
|
+
return [...byPath.values()];
|
|
1061
|
+
}
|
|
1062
|
+
function mergeSymbolInfos(symbols2) {
|
|
1063
|
+
const bySymbol = /* @__PURE__ */ new Map();
|
|
1064
|
+
for (const symbol of symbols2) {
|
|
1065
|
+
const existing = bySymbol.get(symbol.symbol);
|
|
1066
|
+
if (!existing) {
|
|
1067
|
+
bySymbol.set(symbol.symbol, symbol);
|
|
1068
|
+
continue;
|
|
1069
|
+
}
|
|
1070
|
+
bySymbol.set(symbol.symbol, create(SymbolInformationSchema, {
|
|
1071
|
+
symbol: existing.symbol,
|
|
1072
|
+
documentation: uniqueStrings([...existing.documentation, ...symbol.documentation]),
|
|
1073
|
+
relationships: mergeRelationships([...existing.relationships, ...symbol.relationships]),
|
|
1074
|
+
kind: existing.kind || symbol.kind,
|
|
1075
|
+
displayName: existing.displayName || symbol.displayName,
|
|
1076
|
+
enclosingSymbol: existing.enclosingSymbol || symbol.enclosingSymbol,
|
|
1077
|
+
signatureDocumentation: existing.signatureDocumentation ?? symbol.signatureDocumentation
|
|
1078
|
+
}));
|
|
1079
|
+
}
|
|
1080
|
+
return [...bySymbol.values()];
|
|
1081
|
+
}
|
|
1082
|
+
function mergeRelationships(relationships) {
|
|
1083
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1084
|
+
const merged = [];
|
|
1085
|
+
for (const relationship of relationships) {
|
|
1086
|
+
const key = [
|
|
1087
|
+
relationship.symbol,
|
|
1088
|
+
relationship.isReference ? "1" : "0",
|
|
1089
|
+
relationship.isImplementation ? "1" : "0",
|
|
1090
|
+
relationship.isTypeDefinition ? "1" : "0",
|
|
1091
|
+
relationship.isDefinition ? "1" : "0"
|
|
1092
|
+
].join("|");
|
|
1093
|
+
if (seen.has(key)) {
|
|
1094
|
+
continue;
|
|
1095
|
+
}
|
|
1096
|
+
seen.add(key);
|
|
1097
|
+
merged.push(relationship);
|
|
1098
|
+
}
|
|
1099
|
+
return merged;
|
|
1100
|
+
}
|
|
1101
|
+
function chooseText(left, right) {
|
|
1102
|
+
if (!left) return right;
|
|
1103
|
+
if (!right) return left;
|
|
1104
|
+
return left.length >= right.length ? left : right;
|
|
1105
|
+
}
|
|
1106
|
+
function uniqueStrings(values) {
|
|
1107
|
+
return [...new Set(values)];
|
|
1108
|
+
}
|
|
1109
|
+
|
|
971
1110
|
// src/reindex/index.ts
|
|
972
1111
|
async function reindex(opts) {
|
|
973
1112
|
const {
|
|
@@ -1003,7 +1142,11 @@ async function reindex(opts) {
|
|
|
1003
1142
|
...process.env,
|
|
1004
1143
|
NODE_OPTIONS: `--max-old-space-size=${maxHeapMb}`
|
|
1005
1144
|
};
|
|
1006
|
-
|
|
1145
|
+
const languageOutputs = languages.map((language, index) => ({
|
|
1146
|
+
language,
|
|
1147
|
+
scipPath: languages.length > 1 ? tempScipPath(outputScip, language, index) : outputScip
|
|
1148
|
+
}));
|
|
1149
|
+
for (const { language: lang, scipPath } of languageOutputs) {
|
|
1007
1150
|
const config = getIndexerConfig(lang);
|
|
1008
1151
|
const binaryLabel = describeIndexerBinary(config);
|
|
1009
1152
|
const projectLocalBinary = resolveProjectLocalIndexerBinary(config, projectRoot);
|
|
@@ -1033,7 +1176,7 @@ async function reindex(opts) {
|
|
|
1033
1176
|
const indexerEnv = getIndexerExecutionEnv(config, env, resolvedBinary);
|
|
1034
1177
|
const { binary, args } = config.indexArgs({
|
|
1035
1178
|
projectRoot,
|
|
1036
|
-
outputPath:
|
|
1179
|
+
outputPath: scipPath,
|
|
1037
1180
|
pnpmWorkspaces: opts.pnpmWorkspaces,
|
|
1038
1181
|
indexerBinary: resolvedBinary
|
|
1039
1182
|
});
|
|
@@ -1052,7 +1195,11 @@ Make sure ${binaryLabel} is installed and available on PATH.`,
|
|
|
1052
1195
|
{ cause: err }
|
|
1053
1196
|
);
|
|
1054
1197
|
}
|
|
1055
|
-
moveDefaultOutputIfNeeded(config, projectRoot,
|
|
1198
|
+
moveDefaultOutputIfNeeded(config, projectRoot, scipPath);
|
|
1199
|
+
}
|
|
1200
|
+
if (languageOutputs.length > 1) {
|
|
1201
|
+
onStatus(`Merging ${languageOutputs.length} language indexes...`);
|
|
1202
|
+
mergeScipFiles(languageOutputs.map((entry) => entry.scipPath), outputScip);
|
|
1056
1203
|
}
|
|
1057
1204
|
onStatus("Converting to SQLite...");
|
|
1058
1205
|
if (!existsSync6(outputScip)) {
|
|
@@ -1067,6 +1214,12 @@ Make sure ${binaryLabel} is installed and available on PATH.`,
|
|
|
1067
1214
|
} catch (err) {
|
|
1068
1215
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1069
1216
|
throw new Error(`Failed to convert SCIP index to SQLite: ${msg}`, { cause: err });
|
|
1217
|
+
} finally {
|
|
1218
|
+
for (const { scipPath } of languageOutputs) {
|
|
1219
|
+
if (scipPath !== outputScip) {
|
|
1220
|
+
rmSync(scipPath, { force: true });
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1070
1223
|
}
|
|
1071
1224
|
const durationMs = Date.now() - start;
|
|
1072
1225
|
onStatus(`Done in ${(durationMs / 1e3).toFixed(1)}s`);
|
|
@@ -1081,11 +1234,16 @@ function moveDefaultOutputIfNeeded(config, projectRoot, outputScip) {
|
|
|
1081
1234
|
renameSync(defaultOutputPath, outputScip);
|
|
1082
1235
|
}
|
|
1083
1236
|
}
|
|
1237
|
+
function tempScipPath(outputScip, language, index) {
|
|
1238
|
+
const extension = extname2(outputScip) || ".scip";
|
|
1239
|
+
const stem = basename(outputScip, extension);
|
|
1240
|
+
return join6(dirname2(outputScip), `${stem}.${index + 1}.${language}${extension}`);
|
|
1241
|
+
}
|
|
1084
1242
|
|
|
1085
1243
|
// src/watch.ts
|
|
1086
1244
|
import { watch } from "fs";
|
|
1087
1245
|
import { existsSync as existsSync7, renameSync as renameSync2 } from "fs";
|
|
1088
|
-
import { join as join7, relative } from "path";
|
|
1246
|
+
import { join as join7, relative as relative2 } from "path";
|
|
1089
1247
|
import { fork } from "child_process";
|
|
1090
1248
|
import ignore2 from "ignore";
|
|
1091
1249
|
var Watcher = class {
|
|
@@ -1157,13 +1315,9 @@ var Watcher = class {
|
|
|
1157
1315
|
if (this.cooldownTimer) clearTimeout(this.cooldownTimer);
|
|
1158
1316
|
this.setStatus({ state: "idle" });
|
|
1159
1317
|
}
|
|
1160
|
-
/** Get current watcher status */
|
|
1161
|
-
getStatus() {
|
|
1162
|
-
return this.status;
|
|
1163
|
-
}
|
|
1164
1318
|
// ── Internal ─────────────────────────────────────────────
|
|
1165
1319
|
handleFileChange(filename) {
|
|
1166
|
-
const rel =
|
|
1320
|
+
const rel = relative2(this.projectRoot, join7(this.projectRoot, filename));
|
|
1167
1321
|
if (this.gitignoreFilter.isIgnored(rel)) return;
|
|
1168
1322
|
if (this.extraIgnore.ignores(rel)) return;
|
|
1169
1323
|
if (filename.endsWith("index.db") || filename.endsWith("index.scip") || filename.endsWith("index.db.tmp") || filename.endsWith(".scipquery.json")) {
|
|
@@ -1244,10 +1398,10 @@ var Watcher = class {
|
|
|
1244
1398
|
* Writes to index.db.tmp, then atomically renames to index.db.
|
|
1245
1399
|
*/
|
|
1246
1400
|
runReindex() {
|
|
1247
|
-
return new Promise((
|
|
1401
|
+
return new Promise((resolve5, reject) => {
|
|
1248
1402
|
const start = Date.now();
|
|
1249
1403
|
const tmpDb = this.indexPaths.dbPath + ".tmp";
|
|
1250
|
-
const tmpScip =
|
|
1404
|
+
const tmpScip = tempScipPath2(this.indexPaths.indexPath);
|
|
1251
1405
|
const child = fork(
|
|
1252
1406
|
new URL("./reindex-worker.js", import.meta.url).pathname,
|
|
1253
1407
|
[],
|
|
@@ -1272,7 +1426,7 @@ var Watcher = class {
|
|
|
1272
1426
|
if (existsSync7(tmpScip)) {
|
|
1273
1427
|
renameSync2(tmpScip, this.indexPaths.indexPath);
|
|
1274
1428
|
}
|
|
1275
|
-
|
|
1429
|
+
resolve5(Date.now() - start);
|
|
1276
1430
|
} catch (err) {
|
|
1277
1431
|
reject(new Error(`Atomic swap failed: ${err}`));
|
|
1278
1432
|
}
|
|
@@ -1288,7 +1442,7 @@ var Watcher = class {
|
|
|
1288
1442
|
this.onStatus(status);
|
|
1289
1443
|
}
|
|
1290
1444
|
};
|
|
1291
|
-
function
|
|
1445
|
+
function tempScipPath2(indexPath) {
|
|
1292
1446
|
return indexPath.endsWith(".scip") ? indexPath.slice(0, -".scip".length) + ".tmp.scip" : indexPath + ".tmp.scip";
|
|
1293
1447
|
}
|
|
1294
1448
|
|
|
@@ -1313,31 +1467,39 @@ function stats(db) {
|
|
|
1313
1467
|
}
|
|
1314
1468
|
|
|
1315
1469
|
// src/queries/files.ts
|
|
1470
|
+
function globToLike(pattern) {
|
|
1471
|
+
const hasGlobChars = /[*?]/.test(pattern);
|
|
1472
|
+
if (!hasGlobChars) {
|
|
1473
|
+
return `%${pattern}%`;
|
|
1474
|
+
}
|
|
1475
|
+
return pattern.replace(/\*\*/g, "%").replace(/\*/g, "%").replace(/\?/g, "_");
|
|
1476
|
+
}
|
|
1316
1477
|
function files(db, pattern) {
|
|
1478
|
+
const likePattern = globToLike(pattern);
|
|
1317
1479
|
const rows = db.all(
|
|
1318
1480
|
`SELECT relative_path FROM documents
|
|
1319
1481
|
WHERE relative_path LIKE ?
|
|
1320
1482
|
ORDER BY relative_path`,
|
|
1321
|
-
|
|
1483
|
+
likePattern
|
|
1322
1484
|
);
|
|
1323
1485
|
return rows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({ relativePath: r.relative_path }));
|
|
1324
1486
|
}
|
|
1325
1487
|
|
|
1326
1488
|
// src/query-support.ts
|
|
1327
|
-
import { basename as
|
|
1489
|
+
import { basename as basename3 } from "path";
|
|
1328
1490
|
|
|
1329
1491
|
// src/source-analysis.ts
|
|
1330
1492
|
import {
|
|
1331
1493
|
existsSync as existsSync8,
|
|
1332
|
-
readFileSync as
|
|
1494
|
+
readFileSync as readFileSync4
|
|
1333
1495
|
} from "fs";
|
|
1334
1496
|
import {
|
|
1335
|
-
basename,
|
|
1336
|
-
dirname as
|
|
1337
|
-
extname as
|
|
1497
|
+
basename as basename2,
|
|
1498
|
+
dirname as dirname3,
|
|
1499
|
+
extname as extname3,
|
|
1338
1500
|
join as join8,
|
|
1339
|
-
relative as
|
|
1340
|
-
resolve as
|
|
1501
|
+
relative as relative3,
|
|
1502
|
+
resolve as resolve3
|
|
1341
1503
|
} from "path";
|
|
1342
1504
|
var SOURCE_IMPORT_CACHE = /* @__PURE__ */ new WeakMap();
|
|
1343
1505
|
var SOURCE_EXPORT_CACHE = /* @__PURE__ */ new WeakMap();
|
|
@@ -1366,7 +1528,7 @@ function getSourceImports(db, relativePath) {
|
|
|
1366
1528
|
cache.set(normalized, []);
|
|
1367
1529
|
return [];
|
|
1368
1530
|
}
|
|
1369
|
-
const source =
|
|
1531
|
+
const source = readFileSync4(fullPath, "utf-8");
|
|
1370
1532
|
const parsed = isPythonSourcePath(normalized) ? parsePythonImports(db, normalized, source) : isJavaScriptSourcePath(normalized) ? parseJavaScriptImports(db, normalized, source) : isJvmSourcePath(normalized) ? parseJvmImports(db, normalized, source) : isRustSourcePath(normalized) ? parseRustImports(db, normalized, source) : isRubySourcePath(normalized) ? parseRubyImports(db, normalized, source) : isCLikeSourcePath(normalized) ? parseCLikeImports(db, normalized, source) : isDotNetSourcePath(normalized) ? parseDotNetImports(db, normalized, source) : isDartSourcePath(normalized) ? parseDartImports(db, normalized, source) : isPhpSourcePath(normalized) ? parsePhpImports(db, normalized, source) : [];
|
|
1371
1533
|
cache.set(normalized, parsed);
|
|
1372
1534
|
return parsed;
|
|
@@ -1383,7 +1545,7 @@ function getSourceExports(db, relativePath) {
|
|
|
1383
1545
|
cache.set(normalized, []);
|
|
1384
1546
|
return [];
|
|
1385
1547
|
}
|
|
1386
|
-
const source =
|
|
1548
|
+
const source = readFileSync4(fullPath, "utf-8");
|
|
1387
1549
|
const parsed = isDartSourcePath(normalized) ? parseDartExports(db, normalized, source) : isRustSourcePath(normalized) ? parseRustExports(db, normalized, source) : [];
|
|
1388
1550
|
cache.set(normalized, parsed);
|
|
1389
1551
|
return parsed;
|
|
@@ -1680,7 +1842,7 @@ function parseRubyImports(db, importerPath, source) {
|
|
|
1680
1842
|
return statements;
|
|
1681
1843
|
}
|
|
1682
1844
|
function rubyConstantName(specifier) {
|
|
1683
|
-
return
|
|
1845
|
+
return basename2(specifier).replace(/\.[^.]+$/, "").split("_").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
1684
1846
|
}
|
|
1685
1847
|
function parseCLikeImports(db, importerPath, source) {
|
|
1686
1848
|
const statements = [];
|
|
@@ -1689,7 +1851,7 @@ function parseCLikeImports(db, importerPath, source) {
|
|
|
1689
1851
|
const full = match[0];
|
|
1690
1852
|
if (!specifier || !full || typeof match.index !== "number") continue;
|
|
1691
1853
|
const body = buildUsageBody(source, match.index, match.index + full.length);
|
|
1692
|
-
const localName =
|
|
1854
|
+
const localName = basename2(specifier).replace(/\.[^.]+$/, "");
|
|
1693
1855
|
statements.push({
|
|
1694
1856
|
importedName: specifier,
|
|
1695
1857
|
localName,
|
|
@@ -2164,16 +2326,16 @@ function resolveJavaScriptImportPath(db, importerPath, specifier) {
|
|
|
2164
2326
|
if (!specifier.startsWith(".") && !specifier.startsWith("/")) {
|
|
2165
2327
|
return null;
|
|
2166
2328
|
}
|
|
2167
|
-
const importerDir =
|
|
2168
|
-
const absolute =
|
|
2329
|
+
const importerDir = dirname3(join8(db.config.projectRoot, importerPath));
|
|
2330
|
+
const absolute = resolve3(importerDir, specifier);
|
|
2169
2331
|
const indexedPaths = getIndexedPaths(db);
|
|
2170
2332
|
for (const candidate of candidateImportPaths(absolute)) {
|
|
2171
|
-
const relativeCandidate = normalizePath(
|
|
2333
|
+
const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
|
|
2172
2334
|
if (indexedPaths.has(relativeCandidate) || existsSync8(candidate)) {
|
|
2173
2335
|
return relativeCandidate;
|
|
2174
2336
|
}
|
|
2175
2337
|
}
|
|
2176
|
-
return normalizePath(
|
|
2338
|
+
return normalizePath(relative3(db.config.projectRoot, absolute));
|
|
2177
2339
|
}
|
|
2178
2340
|
function resolvePythonImportPath(db, importerPath, specifier) {
|
|
2179
2341
|
const indexedPaths = getIndexedPaths(db);
|
|
@@ -2183,16 +2345,16 @@ function resolvePythonImportPath(db, importerPath, specifier) {
|
|
|
2183
2345
|
if (!match) return null;
|
|
2184
2346
|
const dots = match[1].length;
|
|
2185
2347
|
const remainder = match[2].replace(/^\./, "");
|
|
2186
|
-
let baseDir =
|
|
2348
|
+
let baseDir = dirname3(join8(db.config.projectRoot, importerPath));
|
|
2187
2349
|
for (let i = 1; i < dots; i++) {
|
|
2188
|
-
baseDir =
|
|
2350
|
+
baseDir = dirname3(baseDir);
|
|
2189
2351
|
}
|
|
2190
|
-
basePath = remainder ?
|
|
2352
|
+
basePath = remainder ? resolve3(baseDir, remainder.replace(/\./g, "/")) : baseDir;
|
|
2191
2353
|
} else {
|
|
2192
|
-
basePath =
|
|
2354
|
+
basePath = resolve3(db.config.projectRoot, specifier.replace(/\./g, "/"));
|
|
2193
2355
|
}
|
|
2194
2356
|
for (const candidate of pythonCandidateImportPaths(basePath)) {
|
|
2195
|
-
const relativeCandidate = normalizePath(
|
|
2357
|
+
const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
|
|
2196
2358
|
if (indexedPaths.has(relativeCandidate) || existsSync8(candidate)) {
|
|
2197
2359
|
return relativeCandidate;
|
|
2198
2360
|
}
|
|
@@ -2205,17 +2367,17 @@ function resolveRustImportPath(db, importerPath, specifier) {
|
|
|
2205
2367
|
if (!normalizedSpecifier.startsWith("crate::") && !normalizedSpecifier.startsWith("self::") && !normalizedSpecifier.startsWith("super::")) {
|
|
2206
2368
|
return null;
|
|
2207
2369
|
}
|
|
2208
|
-
const importerDir =
|
|
2370
|
+
const importerDir = dirname3(join8(db.config.projectRoot, importerPath));
|
|
2209
2371
|
let basePath;
|
|
2210
2372
|
if (normalizedSpecifier.startsWith("crate::")) {
|
|
2211
|
-
basePath =
|
|
2373
|
+
basePath = resolve3(db.config.projectRoot, "src", normalizedSpecifier.slice("crate::".length).replace(/::/g, "/"));
|
|
2212
2374
|
} else if (normalizedSpecifier.startsWith("self::")) {
|
|
2213
|
-
basePath =
|
|
2375
|
+
basePath = resolve3(importerDir, normalizedSpecifier.slice("self::".length).replace(/::/g, "/"));
|
|
2214
2376
|
} else {
|
|
2215
|
-
basePath =
|
|
2377
|
+
basePath = resolve3(dirname3(importerDir), normalizedSpecifier.slice("super::".length).replace(/::/g, "/"));
|
|
2216
2378
|
}
|
|
2217
2379
|
for (const candidate of rustCandidateImportPaths(basePath)) {
|
|
2218
|
-
const relativeCandidate = normalizePath(
|
|
2380
|
+
const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
|
|
2219
2381
|
if (getIndexedPaths(db).has(relativeCandidate) || existsSync8(candidate)) {
|
|
2220
2382
|
return relativeCandidate;
|
|
2221
2383
|
}
|
|
@@ -2223,10 +2385,10 @@ function resolveRustImportPath(db, importerPath, specifier) {
|
|
|
2223
2385
|
return null;
|
|
2224
2386
|
}
|
|
2225
2387
|
function resolveRubyImportPath(db, importerPath, specifier) {
|
|
2226
|
-
const importerDir =
|
|
2227
|
-
const absolute =
|
|
2388
|
+
const importerDir = dirname3(join8(db.config.projectRoot, importerPath));
|
|
2389
|
+
const absolute = resolve3(importerDir, specifier);
|
|
2228
2390
|
for (const candidate of rubyCandidateImportPaths(absolute)) {
|
|
2229
|
-
const relativeCandidate = normalizePath(
|
|
2391
|
+
const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
|
|
2230
2392
|
if (getIndexedPaths(db).has(relativeCandidate) || existsSync8(candidate)) {
|
|
2231
2393
|
return relativeCandidate;
|
|
2232
2394
|
}
|
|
@@ -2235,15 +2397,15 @@ function resolveRubyImportPath(db, importerPath, specifier) {
|
|
|
2235
2397
|
}
|
|
2236
2398
|
function resolveCLikeImportPath(db, importerPath, specifier) {
|
|
2237
2399
|
const indexedPaths = getIndexedPaths(db);
|
|
2238
|
-
const importerDir =
|
|
2400
|
+
const importerDir = dirname3(join8(db.config.projectRoot, importerPath));
|
|
2239
2401
|
const candidates = [
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2402
|
+
resolve3(importerDir, specifier),
|
|
2403
|
+
resolve3(db.config.projectRoot, specifier),
|
|
2404
|
+
resolve3(db.config.projectRoot, "include", specifier),
|
|
2405
|
+
resolve3(db.config.projectRoot, "src", specifier)
|
|
2244
2406
|
];
|
|
2245
2407
|
for (const candidate of candidates) {
|
|
2246
|
-
const relativeCandidate = normalizePath(
|
|
2408
|
+
const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
|
|
2247
2409
|
if (indexedPaths.has(relativeCandidate) || existsSync8(candidate)) {
|
|
2248
2410
|
return relativeCandidate;
|
|
2249
2411
|
}
|
|
@@ -2261,10 +2423,10 @@ function resolveQualifiedImportPath(db, specifier, extensions) {
|
|
|
2261
2423
|
if (exact) return exact;
|
|
2262
2424
|
}
|
|
2263
2425
|
for (const ext of extensions) {
|
|
2264
|
-
const basenameMatch = [...indexedPaths].find((relativePath) =>
|
|
2426
|
+
const basenameMatch = [...indexedPaths].find((relativePath) => basename2(relativePath) === `${basenameOnly}${ext}`);
|
|
2265
2427
|
if (basenameMatch) return basenameMatch;
|
|
2266
2428
|
}
|
|
2267
|
-
const folderMatches = [...indexedPaths].filter((relativePath) => extensions.includes(
|
|
2429
|
+
const folderMatches = [...indexedPaths].filter((relativePath) => extensions.includes(extname3(relativePath).toLowerCase())).filter((relativePath) => relativePath.includes(`/${pathified}/`) || relativePath.includes(`/${basenameOnly}/`)).sort((left, right) => left.localeCompare(right));
|
|
2268
2430
|
if (folderMatches.length === 1) {
|
|
2269
2431
|
return folderMatches[0];
|
|
2270
2432
|
}
|
|
@@ -2281,10 +2443,10 @@ function resolveDartImportPath(db, importerPath, specifier) {
|
|
|
2281
2443
|
if (indexedPaths.has(candidate)) return candidate;
|
|
2282
2444
|
return null;
|
|
2283
2445
|
}
|
|
2284
|
-
const importerDir =
|
|
2285
|
-
const absolute =
|
|
2446
|
+
const importerDir = dirname3(join8(db.config.projectRoot, importerPath));
|
|
2447
|
+
const absolute = resolve3(importerDir, specifier);
|
|
2286
2448
|
for (const candidate of dartCandidateImportPaths(absolute)) {
|
|
2287
|
-
const relativeCandidate = normalizePath(
|
|
2449
|
+
const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
|
|
2288
2450
|
if (indexedPaths.has(relativeCandidate) || existsSync8(candidate)) {
|
|
2289
2451
|
return relativeCandidate;
|
|
2290
2452
|
}
|
|
@@ -2292,7 +2454,7 @@ function resolveDartImportPath(db, importerPath, specifier) {
|
|
|
2292
2454
|
return null;
|
|
2293
2455
|
}
|
|
2294
2456
|
function pythonCandidateImportPaths(basePath) {
|
|
2295
|
-
const ext =
|
|
2457
|
+
const ext = extname3(basePath);
|
|
2296
2458
|
if (PYTHON_SOURCE_EXTENSIONS.includes(ext)) {
|
|
2297
2459
|
return [basePath];
|
|
2298
2460
|
}
|
|
@@ -2304,7 +2466,7 @@ function pythonCandidateImportPaths(basePath) {
|
|
|
2304
2466
|
];
|
|
2305
2467
|
}
|
|
2306
2468
|
function rustCandidateImportPaths(basePath) {
|
|
2307
|
-
const ext =
|
|
2469
|
+
const ext = extname3(basePath);
|
|
2308
2470
|
if (RUST_SOURCE_EXTENSIONS.includes(ext)) {
|
|
2309
2471
|
return [basePath];
|
|
2310
2472
|
}
|
|
@@ -2314,7 +2476,7 @@ function rustCandidateImportPaths(basePath) {
|
|
|
2314
2476
|
];
|
|
2315
2477
|
}
|
|
2316
2478
|
function rubyCandidateImportPaths(basePath) {
|
|
2317
|
-
const ext =
|
|
2479
|
+
const ext = extname3(basePath);
|
|
2318
2480
|
if (RUBY_SOURCE_EXTENSIONS.includes(ext)) {
|
|
2319
2481
|
return [basePath];
|
|
2320
2482
|
}
|
|
@@ -2324,14 +2486,14 @@ function rubyCandidateImportPaths(basePath) {
|
|
|
2324
2486
|
];
|
|
2325
2487
|
}
|
|
2326
2488
|
function dartCandidateImportPaths(basePath) {
|
|
2327
|
-
const ext =
|
|
2489
|
+
const ext = extname3(basePath);
|
|
2328
2490
|
if (DART_SOURCE_EXTENSIONS.includes(ext)) {
|
|
2329
2491
|
return [basePath];
|
|
2330
2492
|
}
|
|
2331
2493
|
return [`${basePath}.dart`, basePath];
|
|
2332
2494
|
}
|
|
2333
2495
|
function candidateImportPaths(absolute) {
|
|
2334
|
-
const ext =
|
|
2496
|
+
const ext = extname3(absolute);
|
|
2335
2497
|
const candidates = /* @__PURE__ */ new Set();
|
|
2336
2498
|
if (ext) {
|
|
2337
2499
|
candidates.add(absolute);
|
|
@@ -2373,44 +2535,55 @@ function getCachedMap(cache, db) {
|
|
|
2373
2535
|
function normalizePath(path2) {
|
|
2374
2536
|
return path2.replace(/\\/g, "/");
|
|
2375
2537
|
}
|
|
2538
|
+
var LANGUAGE_EXTENSION_FAMILIES = [
|
|
2539
|
+
SOURCE_EXTENSIONS,
|
|
2540
|
+
PYTHON_SOURCE_EXTENSIONS,
|
|
2541
|
+
JVM_SOURCE_EXTENSIONS,
|
|
2542
|
+
RUST_SOURCE_EXTENSIONS,
|
|
2543
|
+
RUBY_SOURCE_EXTENSIONS,
|
|
2544
|
+
C_LIKE_SOURCE_EXTENSIONS,
|
|
2545
|
+
DOTNET_SOURCE_EXTENSIONS,
|
|
2546
|
+
DART_SOURCE_EXTENSIONS,
|
|
2547
|
+
PHP_SOURCE_EXTENSIONS
|
|
2548
|
+
];
|
|
2549
|
+
function hasExtensionIn(relativePath, extensions) {
|
|
2550
|
+
return extensions.includes(extname3(relativePath).toLowerCase());
|
|
2551
|
+
}
|
|
2376
2552
|
function isJavaScriptSourcePath(relativePath) {
|
|
2377
|
-
return
|
|
2553
|
+
return hasExtensionIn(relativePath, SOURCE_EXTENSIONS);
|
|
2378
2554
|
}
|
|
2379
2555
|
function isPythonSourcePath(relativePath) {
|
|
2380
|
-
return
|
|
2556
|
+
return hasExtensionIn(relativePath, PYTHON_SOURCE_EXTENSIONS);
|
|
2381
2557
|
}
|
|
2382
2558
|
function isJvmSourcePath(relativePath) {
|
|
2383
|
-
return
|
|
2559
|
+
return hasExtensionIn(relativePath, JVM_SOURCE_EXTENSIONS);
|
|
2384
2560
|
}
|
|
2385
2561
|
function isRustSourcePath(relativePath) {
|
|
2386
|
-
return
|
|
2562
|
+
return hasExtensionIn(relativePath, RUST_SOURCE_EXTENSIONS);
|
|
2387
2563
|
}
|
|
2388
2564
|
function isRubySourcePath(relativePath) {
|
|
2389
|
-
return
|
|
2565
|
+
return hasExtensionIn(relativePath, RUBY_SOURCE_EXTENSIONS);
|
|
2390
2566
|
}
|
|
2391
2567
|
function isCLikeSourcePath(relativePath) {
|
|
2392
|
-
return
|
|
2568
|
+
return hasExtensionIn(relativePath, C_LIKE_SOURCE_EXTENSIONS);
|
|
2393
2569
|
}
|
|
2394
2570
|
function isDotNetSourcePath(relativePath) {
|
|
2395
|
-
return
|
|
2571
|
+
return hasExtensionIn(relativePath, DOTNET_SOURCE_EXTENSIONS);
|
|
2396
2572
|
}
|
|
2397
2573
|
function isVisualBasicSourcePath(relativePath) {
|
|
2398
|
-
return
|
|
2574
|
+
return extname3(relativePath).toLowerCase() === ".vb";
|
|
2399
2575
|
}
|
|
2400
2576
|
function isDartSourcePath(relativePath) {
|
|
2401
|
-
return
|
|
2577
|
+
return hasExtensionIn(relativePath, DART_SOURCE_EXTENSIONS);
|
|
2402
2578
|
}
|
|
2403
2579
|
function isPhpSourcePath(relativePath) {
|
|
2404
|
-
return
|
|
2580
|
+
return hasExtensionIn(relativePath, PHP_SOURCE_EXTENSIONS);
|
|
2405
2581
|
}
|
|
2406
2582
|
function extensionFamilyFor(relativePath) {
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
if (isCLikeSourcePath(relativePath)) return C_LIKE_SOURCE_EXTENSIONS;
|
|
2412
|
-
if (isRustSourcePath(relativePath)) return RUST_SOURCE_EXTENSIONS;
|
|
2413
|
-
if (isRubySourcePath(relativePath)) return RUBY_SOURCE_EXTENSIONS;
|
|
2583
|
+
const ext = extname3(relativePath).toLowerCase();
|
|
2584
|
+
for (const family of LANGUAGE_EXTENSION_FAMILIES) {
|
|
2585
|
+
if (family.includes(ext)) return family;
|
|
2586
|
+
}
|
|
2414
2587
|
return SOURCE_EXTENSIONS;
|
|
2415
2588
|
}
|
|
2416
2589
|
function getSourceText(db, relativePath) {
|
|
@@ -2425,7 +2598,7 @@ function getSourceText(db, relativePath) {
|
|
|
2425
2598
|
cache.set(normalized, "");
|
|
2426
2599
|
return "";
|
|
2427
2600
|
}
|
|
2428
|
-
const source =
|
|
2601
|
+
const source = readFileSync4(fullPath, "utf-8");
|
|
2429
2602
|
cache.set(normalized, source);
|
|
2430
2603
|
return source;
|
|
2431
2604
|
}
|
|
@@ -2612,6 +2785,9 @@ function leafSuffix(raw) {
|
|
|
2612
2785
|
const last = sym.descriptors[sym.descriptors.length - 1];
|
|
2613
2786
|
return last?.suffix ?? null;
|
|
2614
2787
|
}
|
|
2788
|
+
function isCallableSymbol(raw) {
|
|
2789
|
+
return raw.endsWith("().") || leafSuffix(raw) === "method";
|
|
2790
|
+
}
|
|
2615
2791
|
function isFunctionLikeSymbol(raw) {
|
|
2616
2792
|
const suffix = leafSuffix(raw);
|
|
2617
2793
|
return suffix === "method" || suffix === "term";
|
|
@@ -3065,56 +3241,41 @@ function findEnclosingDefinition(definitions, line) {
|
|
|
3065
3241
|
}
|
|
3066
3242
|
return best;
|
|
3067
3243
|
}
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
}
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
)
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
if (seen.has(key)) continue;
|
|
3104
|
-
seen.add(key);
|
|
3105
|
-
rows.push({
|
|
3106
|
-
symbol: resolved.symbol,
|
|
3107
|
-
file: resolved.relativePath,
|
|
3108
|
-
chunkId
|
|
3109
|
-
});
|
|
3110
|
-
}
|
|
3111
|
-
return applyLimit(rows, limit);
|
|
3112
|
-
}
|
|
3113
|
-
function getJavaScriptSourceCalleeRows(db, symbol, limit) {
|
|
3114
|
-
const match = getFullSymbolMatch(db, symbol);
|
|
3115
|
-
if (!match || !isJavaScriptDocument(db, match.relativePath)) {
|
|
3116
|
-
return [];
|
|
3117
|
-
}
|
|
3244
|
+
var LANGUAGE_CALLEE_CONFIGS = [
|
|
3245
|
+
// Python (index 0) — complex resolver
|
|
3246
|
+
{ kind: "complex", languageIndex: 0, resolver: resolvePythonCallTarget },
|
|
3247
|
+
// JavaScript/TypeScript (index 1) — complex resolver
|
|
3248
|
+
{ kind: "complex", languageIndex: 1, resolver: resolveJavaScriptCallTarget },
|
|
3249
|
+
// Java (index 2) — simple with field bindings
|
|
3250
|
+
{ kind: "simple", languageIndex: 2, parseBindings: (_db, source) => parseJavaFieldBindings(source) },
|
|
3251
|
+
// Kotlin (index 3) — simple with field bindings
|
|
3252
|
+
{ kind: "simple", languageIndex: 3, parseBindings: (_db, source) => parseKotlinFieldBindings(source) },
|
|
3253
|
+
// Scala (index 4) — simple, no bindings
|
|
3254
|
+
{ kind: "simple", languageIndex: 4, parseBindings: null },
|
|
3255
|
+
// C# (index 5) — simple, no bindings
|
|
3256
|
+
{ kind: "simple", languageIndex: 5, parseBindings: null },
|
|
3257
|
+
// Visual Basic (index 6) — simple, no bindings
|
|
3258
|
+
{ kind: "simple", languageIndex: 6, parseBindings: null },
|
|
3259
|
+
// C++ (index 7) — simple with receiver bindings
|
|
3260
|
+
{ kind: "simple", languageIndex: 7, parseBindings: (_db, source) => parseCppReceiverBindings(source) },
|
|
3261
|
+
// Rust (index 8) — simple, no bindings
|
|
3262
|
+
{ kind: "simple", languageIndex: 8, parseBindings: null },
|
|
3263
|
+
// Ruby (index 9) — simple with dual-attempt logic
|
|
3264
|
+
{
|
|
3265
|
+
kind: "simple",
|
|
3266
|
+
languageIndex: 9,
|
|
3267
|
+
parseBindings: (db, source) => parseRubyReceiverBindings(db, source),
|
|
3268
|
+
dualAttempt: {
|
|
3269
|
+
baseOpts: { allowInstanceVariables: true },
|
|
3270
|
+
extendedOpts: { allowInstanceVariables: true, allowBareMemberCalls: true }
|
|
3271
|
+
}
|
|
3272
|
+
},
|
|
3273
|
+
// Dart (index 10) — simple, no bindings
|
|
3274
|
+
{ kind: "simple", languageIndex: 10, parseBindings: null },
|
|
3275
|
+
// PHP (index 11) — simple, no bindings
|
|
3276
|
+
{ kind: "simple", languageIndex: 11, parseBindings: null }
|
|
3277
|
+
];
|
|
3278
|
+
function getComplexSourceCalleeRows(db, match, config, limit) {
|
|
3118
3279
|
const definitions = getDefinitionsForFile(db, match.relativePath);
|
|
3119
3280
|
const current = definitions.find((definition) => definition.symbolId === match.symbolId);
|
|
3120
3281
|
if (!current) {
|
|
@@ -3133,7 +3294,7 @@ function getJavaScriptSourceCalleeRows(db, symbol, limit) {
|
|
|
3133
3294
|
startLine: match.startLine,
|
|
3134
3295
|
endLine: match.endLine
|
|
3135
3296
|
})) {
|
|
3136
|
-
const resolved =
|
|
3297
|
+
const resolved = config.resolver(
|
|
3137
3298
|
db,
|
|
3138
3299
|
current,
|
|
3139
3300
|
definitions,
|
|
@@ -3155,134 +3316,31 @@ function getJavaScriptSourceCalleeRows(db, symbol, limit) {
|
|
|
3155
3316
|
}
|
|
3156
3317
|
return applyLimit(rows, limit);
|
|
3157
3318
|
}
|
|
3158
|
-
function
|
|
3159
|
-
|
|
3160
|
-
if (
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
}
|
|
3167
|
-
function getKotlinSourceCalleeRows(db, symbol, limit) {
|
|
3168
|
-
const match = getFullSymbolMatch(db, symbol);
|
|
3169
|
-
if (!match || !isKotlinDocument(db, match.relativePath)) {
|
|
3170
|
-
return [];
|
|
3171
|
-
}
|
|
3172
|
-
const calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine);
|
|
3173
|
-
const bindings = parseKotlinFieldBindings(getSourceText(db, match.relativePath));
|
|
3174
|
-
return resolveSimpleSourceCallees(db, match, calls, bindings, limit);
|
|
3175
|
-
}
|
|
3176
|
-
function getScalaSourceCalleeRows(db, symbol, limit) {
|
|
3177
|
-
const match = getFullSymbolMatch(db, symbol);
|
|
3178
|
-
if (!match || !isScalaDocument(db, match.relativePath)) {
|
|
3179
|
-
return [];
|
|
3180
|
-
}
|
|
3181
|
-
const calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine);
|
|
3182
|
-
return resolveSimpleSourceCallees(db, match, calls, /* @__PURE__ */ new Map(), limit);
|
|
3183
|
-
}
|
|
3184
|
-
function getCSharpSourceCalleeRows(db, symbol, limit) {
|
|
3185
|
-
const match = getFullSymbolMatch(db, symbol);
|
|
3186
|
-
if (!match || !isCSharpDocument(db, match.relativePath)) {
|
|
3187
|
-
return [];
|
|
3188
|
-
}
|
|
3189
|
-
const calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine);
|
|
3190
|
-
return resolveSimpleSourceCallees(db, match, calls, /* @__PURE__ */ new Map(), limit);
|
|
3191
|
-
}
|
|
3192
|
-
function getVisualBasicSourceCalleeRows(db, symbol, limit) {
|
|
3193
|
-
const match = getFullSymbolMatch(db, symbol);
|
|
3194
|
-
if (!match || !isVisualBasicDocument(db, match.relativePath)) {
|
|
3195
|
-
return [];
|
|
3196
|
-
}
|
|
3197
|
-
const calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine);
|
|
3198
|
-
return resolveSimpleSourceCallees(db, match, calls, /* @__PURE__ */ new Map(), limit);
|
|
3199
|
-
}
|
|
3200
|
-
function getCppSourceCalleeRows(db, symbol, limit) {
|
|
3201
|
-
const match = getFullSymbolMatch(db, symbol);
|
|
3202
|
-
if (!match || !isCppDocument(db, match.relativePath)) {
|
|
3203
|
-
return [];
|
|
3319
|
+
function getSimpleLanguageCalleeRows(db, match, config, limit) {
|
|
3320
|
+
let calls;
|
|
3321
|
+
if (config.dualAttempt) {
|
|
3322
|
+
const baseCalls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine, config.dualAttempt.baseOpts);
|
|
3323
|
+
const extendedCalls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine, config.dualAttempt.extendedOpts);
|
|
3324
|
+
calls = extendedCalls.length > 0 ? extendedCalls : baseCalls;
|
|
3325
|
+
} else {
|
|
3326
|
+
calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine, config.sourceCallOpts);
|
|
3204
3327
|
}
|
|
3205
|
-
const
|
|
3206
|
-
const bindings = parseCppReceiverBindings(getSourceText(db, match.relativePath));
|
|
3328
|
+
const bindings = config.parseBindings ? config.parseBindings(db, getSourceText(db, match.relativePath)) : /* @__PURE__ */ new Map();
|
|
3207
3329
|
return resolveSimpleSourceCallees(db, match, calls, bindings, limit);
|
|
3208
3330
|
}
|
|
3209
|
-
function getRustSourceCalleeRows(db, symbol, limit) {
|
|
3210
|
-
const match = getFullSymbolMatch(db, symbol);
|
|
3211
|
-
if (!match || !isRustDocument(db, match.relativePath)) {
|
|
3212
|
-
return [];
|
|
3213
|
-
}
|
|
3214
|
-
const calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine);
|
|
3215
|
-
return resolveSimpleSourceCallees(db, match, calls, /* @__PURE__ */ new Map(), limit);
|
|
3216
|
-
}
|
|
3217
|
-
function getRubySourceCalleeRows(db, symbol, limit) {
|
|
3218
|
-
const match = getFullSymbolMatch(db, symbol);
|
|
3219
|
-
if (!match || !isRubyDocument(db, match.relativePath)) {
|
|
3220
|
-
return [];
|
|
3221
|
-
}
|
|
3222
|
-
const calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine, { allowInstanceVariables: true });
|
|
3223
|
-
const rubyCalls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine, {
|
|
3224
|
-
allowInstanceVariables: true,
|
|
3225
|
-
allowBareMemberCalls: true
|
|
3226
|
-
});
|
|
3227
|
-
const bindings = parseRubyReceiverBindings(db, getSourceText(db, match.relativePath));
|
|
3228
|
-
return resolveSimpleSourceCallees(db, match, rubyCalls.length > 0 ? rubyCalls : calls, bindings, limit);
|
|
3229
|
-
}
|
|
3230
|
-
function getDartSourceCalleeRows(db, symbol, limit) {
|
|
3231
|
-
const match = getFullSymbolMatch(db, symbol);
|
|
3232
|
-
if (!match || !isDartDocument(db, match.relativePath)) {
|
|
3233
|
-
return [];
|
|
3234
|
-
}
|
|
3235
|
-
const calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine);
|
|
3236
|
-
return resolveSimpleSourceCallees(db, match, calls, /* @__PURE__ */ new Map(), limit);
|
|
3237
|
-
}
|
|
3238
|
-
function getPhpSourceCalleeRows(db, symbol, limit) {
|
|
3239
|
-
const match = getFullSymbolMatch(db, symbol);
|
|
3240
|
-
if (!match || !isPhpDocument(db, match.relativePath)) {
|
|
3241
|
-
return [];
|
|
3242
|
-
}
|
|
3243
|
-
const calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine);
|
|
3244
|
-
return resolveSimpleSourceCallees(db, match, calls, /* @__PURE__ */ new Map(), limit);
|
|
3245
|
-
}
|
|
3246
3331
|
function getSourceBackedCalleeRows(db, symbol, limit) {
|
|
3247
3332
|
const match = getFullSymbolMatch(db, symbol);
|
|
3248
3333
|
if (!match) {
|
|
3249
3334
|
return [];
|
|
3250
3335
|
}
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
return
|
|
3259
|
-
}
|
|
3260
|
-
if (isScalaDocument(db, match.relativePath)) {
|
|
3261
|
-
return getScalaSourceCalleeRows(db, match, limit);
|
|
3262
|
-
}
|
|
3263
|
-
if (isKotlinDocument(db, match.relativePath)) {
|
|
3264
|
-
return getKotlinSourceCalleeRows(db, match, limit);
|
|
3265
|
-
}
|
|
3266
|
-
if (isCSharpDocument(db, match.relativePath)) {
|
|
3267
|
-
return getCSharpSourceCalleeRows(db, match, limit);
|
|
3268
|
-
}
|
|
3269
|
-
if (isVisualBasicDocument(db, match.relativePath)) {
|
|
3270
|
-
return getVisualBasicSourceCalleeRows(db, match, limit);
|
|
3271
|
-
}
|
|
3272
|
-
if (isCppDocument(db, match.relativePath)) {
|
|
3273
|
-
return getCppSourceCalleeRows(db, match, limit);
|
|
3274
|
-
}
|
|
3275
|
-
if (isRustDocument(db, match.relativePath)) {
|
|
3276
|
-
return getRustSourceCalleeRows(db, match, limit);
|
|
3277
|
-
}
|
|
3278
|
-
if (isRubyDocument(db, match.relativePath)) {
|
|
3279
|
-
return getRubySourceCalleeRows(db, match, limit);
|
|
3280
|
-
}
|
|
3281
|
-
if (isDartDocument(db, match.relativePath)) {
|
|
3282
|
-
return getDartSourceCalleeRows(db, match, limit);
|
|
3283
|
-
}
|
|
3284
|
-
if (isPhpDocument(db, match.relativePath)) {
|
|
3285
|
-
return getPhpSourceCalleeRows(db, match, limit);
|
|
3336
|
+
for (const config of LANGUAGE_CALLEE_CONFIGS) {
|
|
3337
|
+
if (!isDocumentLanguage(db, match.relativePath, DOCUMENT_LANGUAGE_TABLE[config.languageIndex])) {
|
|
3338
|
+
continue;
|
|
3339
|
+
}
|
|
3340
|
+
if (config.kind === "complex") {
|
|
3341
|
+
return getComplexSourceCalleeRows(db, match, config, limit);
|
|
3342
|
+
}
|
|
3343
|
+
return getSimpleLanguageCalleeRows(db, match, config, limit);
|
|
3286
3344
|
}
|
|
3287
3345
|
return [];
|
|
3288
3346
|
}
|
|
@@ -3292,9 +3350,11 @@ function getPythonSourceCallerRows(db, target, limit) {
|
|
|
3292
3350
|
}
|
|
3293
3351
|
const rows = [];
|
|
3294
3352
|
const seen = /* @__PURE__ */ new Set();
|
|
3353
|
+
const pythonConfig = LANGUAGE_CALLEE_CONFIGS[0];
|
|
3295
3354
|
for (const candidate of getAllFunctionLikeDefinitions(db)) {
|
|
3296
3355
|
if (candidate.symbolId === target.symbolId) continue;
|
|
3297
|
-
|
|
3356
|
+
if (!isDocumentLanguage(db, candidate.relativePath, DOCUMENT_LANGUAGE_TABLE[pythonConfig.languageIndex])) continue;
|
|
3357
|
+
const callees = getComplexSourceCalleeRows(db, candidate, pythonConfig);
|
|
3298
3358
|
if (!callees.some((callee) => callee.symbol === target.symbol)) continue;
|
|
3299
3359
|
const key = `${candidate.symbol}|${candidate.relativePath}`;
|
|
3300
3360
|
if (seen.has(key) || db.isIgnored(candidate.relativePath)) continue;
|
|
@@ -3636,6 +3696,17 @@ function getAllDefinitions(db, opts = {}) {
|
|
|
3636
3696
|
);
|
|
3637
3697
|
return rows.filter((row) => !db.isIgnored(row.relative_path)).flatMap((row) => getDefinitionsForFile(db, row.relative_path));
|
|
3638
3698
|
}
|
|
3699
|
+
function getScopedDefinitions(db, scope) {
|
|
3700
|
+
const scopeFilter = scope ? `AND relative_path LIKE '%${scope}%'` : "";
|
|
3701
|
+
return db.all(
|
|
3702
|
+
`SELECT relative_path
|
|
3703
|
+
FROM documents
|
|
3704
|
+
WHERE 1 = 1
|
|
3705
|
+
${db.pathExclusionsFor("documents")}
|
|
3706
|
+
${scopeFilter}
|
|
3707
|
+
ORDER BY relative_path`
|
|
3708
|
+
).flatMap((row) => getDefinitionsForFile(db, row.relative_path)).filter((row) => !db.isIgnored(row.relativePath));
|
|
3709
|
+
}
|
|
3639
3710
|
function getAllFunctionLikeDefinitions(db) {
|
|
3640
3711
|
return getAllDefinitions(db).filter((definition) => definition.isFunctionLike);
|
|
3641
3712
|
}
|
|
@@ -4053,89 +4124,29 @@ function parentTypeName(rawSymbol) {
|
|
|
4053
4124
|
}
|
|
4054
4125
|
return null;
|
|
4055
4126
|
}
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
)
|
|
4061
|
-
|
|
4062
|
-
}
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
function
|
|
4071
|
-
const row = db.get(
|
|
4072
|
-
`SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
|
|
4073
|
-
relativePath
|
|
4074
|
-
);
|
|
4075
|
-
return row?.language === "java" || relativePath.endsWith(".java");
|
|
4076
|
-
}
|
|
4077
|
-
function isKotlinDocument(db, relativePath) {
|
|
4078
|
-
const row = db.get(
|
|
4079
|
-
`SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
|
|
4080
|
-
relativePath
|
|
4081
|
-
);
|
|
4082
|
-
return row?.language === "kotlin" || relativePath.endsWith(".kt") || relativePath.endsWith(".kts");
|
|
4083
|
-
}
|
|
4084
|
-
function isScalaDocument(db, relativePath) {
|
|
4085
|
-
const row = db.get(
|
|
4086
|
-
`SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
|
|
4087
|
-
relativePath
|
|
4088
|
-
);
|
|
4089
|
-
return row?.language === "scala" || relativePath.endsWith(".scala");
|
|
4090
|
-
}
|
|
4091
|
-
function isCSharpDocument(db, relativePath) {
|
|
4092
|
-
const row = db.get(
|
|
4093
|
-
`SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
|
|
4094
|
-
relativePath
|
|
4095
|
-
);
|
|
4096
|
-
return row?.language === "C#" || relativePath.endsWith(".cs");
|
|
4097
|
-
}
|
|
4098
|
-
function isVisualBasicDocument(db, relativePath) {
|
|
4099
|
-
const row = db.get(
|
|
4100
|
-
`SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
|
|
4101
|
-
relativePath
|
|
4102
|
-
);
|
|
4103
|
-
return row?.language === "Visual Basic" || relativePath.endsWith(".vb");
|
|
4104
|
-
}
|
|
4105
|
-
function isCppDocument(db, relativePath) {
|
|
4106
|
-
const row = db.get(
|
|
4107
|
-
`SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
|
|
4108
|
-
relativePath
|
|
4109
|
-
);
|
|
4110
|
-
return row?.language === "CPP" || /\.(?:cc|cpp|cxx|hpp|hh|hxx)$/.test(relativePath);
|
|
4111
|
-
}
|
|
4112
|
-
function isRustDocument(db, relativePath) {
|
|
4113
|
-
const row = db.get(
|
|
4114
|
-
`SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
|
|
4115
|
-
relativePath
|
|
4116
|
-
);
|
|
4117
|
-
return row?.language === "Rust" || relativePath.endsWith(".rs");
|
|
4118
|
-
}
|
|
4119
|
-
function isRubyDocument(db, relativePath) {
|
|
4120
|
-
const row = db.get(
|
|
4121
|
-
`SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
|
|
4122
|
-
relativePath
|
|
4123
|
-
);
|
|
4124
|
-
return row?.language === "ruby" || relativePath.endsWith(".rb");
|
|
4125
|
-
}
|
|
4126
|
-
function isDartDocument(db, relativePath) {
|
|
4127
|
+
var DOCUMENT_LANGUAGE_TABLE = [
|
|
4128
|
+
{ languages: ["python"], extensionPattern: /\.(?:py|pyi)$/ },
|
|
4129
|
+
{ languages: ["typescript", "javascript"], extensionPattern: /\.(?:[cm]?[jt]sx?)$/ },
|
|
4130
|
+
{ languages: ["java"], extensionPattern: /\.java$/ },
|
|
4131
|
+
{ languages: ["kotlin"], extensionPattern: /\.(?:kt|kts)$/ },
|
|
4132
|
+
{ languages: ["scala"], extensionPattern: /\.scala$/ },
|
|
4133
|
+
{ languages: ["C#"], extensionPattern: /\.cs$/ },
|
|
4134
|
+
{ languages: ["Visual Basic"], extensionPattern: /\.vb$/ },
|
|
4135
|
+
{ languages: ["CPP"], extensionPattern: /\.(?:cc|cpp|cxx|hpp|hh|hxx)$/ },
|
|
4136
|
+
{ languages: ["Rust"], extensionPattern: /\.rs$/ },
|
|
4137
|
+
{ languages: ["ruby"], extensionPattern: /\.rb$/ },
|
|
4138
|
+
{ languages: ["Dart"], extensionPattern: /\.dart$/ },
|
|
4139
|
+
{ languages: ["PHP"], extensionPattern: /\.php$/ }
|
|
4140
|
+
];
|
|
4141
|
+
function isDocumentLanguage(db, relativePath, entry) {
|
|
4127
4142
|
const row = db.get(
|
|
4128
4143
|
`SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
|
|
4129
4144
|
relativePath
|
|
4130
4145
|
);
|
|
4131
|
-
return row?.language
|
|
4146
|
+
return entry.languages.includes(row?.language ?? "") || entry.extensionPattern.test(relativePath);
|
|
4132
4147
|
}
|
|
4133
|
-
function
|
|
4134
|
-
|
|
4135
|
-
`SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
|
|
4136
|
-
relativePath
|
|
4137
|
-
);
|
|
4138
|
-
return row?.language === "PHP" || relativePath.endsWith(".php");
|
|
4148
|
+
function isPythonDocument(db, relativePath) {
|
|
4149
|
+
return isDocumentLanguage(db, relativePath, DOCUMENT_LANGUAGE_TABLE[0]);
|
|
4139
4150
|
}
|
|
4140
4151
|
function applyLimit(values, limit) {
|
|
4141
4152
|
return typeof limit === "number" ? values.slice(0, limit) : values;
|
|
@@ -4174,8 +4185,8 @@ function resolveDocumentCandidates(db, filePattern, opts) {
|
|
|
4174
4185
|
}
|
|
4175
4186
|
function scoreDocumentPath(relativePath, rawPattern) {
|
|
4176
4187
|
const normalizedPath = normalizeLookupPath(relativePath);
|
|
4177
|
-
const pathBase =
|
|
4178
|
-
const patternBase =
|
|
4188
|
+
const pathBase = basename3(normalizedPath);
|
|
4189
|
+
const patternBase = basename3(rawPattern);
|
|
4179
4190
|
let score = 0;
|
|
4180
4191
|
if (normalizedPath === rawPattern) score += 1200;
|
|
4181
4192
|
if (normalizedPath.endsWith(`/${rawPattern}`)) score += 1100;
|
|
@@ -4221,7 +4232,7 @@ function symbols(db, filePattern) {
|
|
|
4221
4232
|
}
|
|
4222
4233
|
|
|
4223
4234
|
// src/queries/methods.ts
|
|
4224
|
-
import { basename as
|
|
4235
|
+
import { basename as basename4 } from "path";
|
|
4225
4236
|
function methods(db, className) {
|
|
4226
4237
|
const classMatch = findFirstSymbolMatch(db, className);
|
|
4227
4238
|
if (!classMatch) {
|
|
@@ -4230,16 +4241,13 @@ function methods(db, className) {
|
|
|
4230
4241
|
const ownerName = leafName(classMatch.symbol);
|
|
4231
4242
|
const definitions = getDefinitionsForFile(db, classMatch.relativePath).filter((definition) => isCallableSymbol(definition.symbol));
|
|
4232
4243
|
const directMethods = definitions.filter((definition) => definition.parentTypeName === ownerName || definition.symbol.includes(ownerName));
|
|
4233
|
-
const fileScopedMethods = directMethods.length > 0 ? directMethods : stripExtension(
|
|
4244
|
+
const fileScopedMethods = directMethods.length > 0 ? directMethods : stripExtension(basename4(classMatch.relativePath)) === ownerName ? definitions.filter((definition) => definition.symbol.includes("<invalid-global-code>")) : [];
|
|
4234
4245
|
return fileScopedMethods.map((definition) => ({
|
|
4235
4246
|
startLine: definition.startLine,
|
|
4236
4247
|
endLine: definition.endLine,
|
|
4237
4248
|
name: leafName(definition.symbol)
|
|
4238
4249
|
}));
|
|
4239
4250
|
}
|
|
4240
|
-
function isCallableSymbol(rawSymbol) {
|
|
4241
|
-
return rawSymbol.endsWith("().") || leafSuffix(rawSymbol) === "method";
|
|
4242
|
-
}
|
|
4243
4251
|
function stripExtension(relativePath) {
|
|
4244
4252
|
return relativePath.replace(/\.[^.]+$/, "");
|
|
4245
4253
|
}
|
|
@@ -4577,7 +4585,7 @@ function surface(db, modulePattern) {
|
|
|
4577
4585
|
...matchedPaths
|
|
4578
4586
|
);
|
|
4579
4587
|
const exposedDefinitions = matchedPaths.flatMap(
|
|
4580
|
-
(relativePath) => getDefinitionsForFile(db, relativePath).filter((definition) =>
|
|
4588
|
+
(relativePath) => getDefinitionsForFile(db, relativePath).filter((definition) => isCallableSymbol(definition.symbol)).map((definition) => ({
|
|
4581
4589
|
relative_path: relativePath,
|
|
4582
4590
|
symbol: definition.symbol
|
|
4583
4591
|
}))
|
|
@@ -4594,9 +4602,6 @@ function surface(db, modulePattern) {
|
|
|
4594
4602
|
shortName: shortenSymbol(r.symbol)
|
|
4595
4603
|
}));
|
|
4596
4604
|
}
|
|
4597
|
-
function isCallableSymbol2(rawSymbol) {
|
|
4598
|
-
return rawSymbol.endsWith("().") || leafSuffix(rawSymbol) === "method";
|
|
4599
|
-
}
|
|
4600
4605
|
|
|
4601
4606
|
// src/entry-surfaces.ts
|
|
4602
4607
|
var liveBarrelCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -4614,11 +4619,11 @@ function isWorkerEntrySurface(path2) {
|
|
|
4614
4619
|
function isStructuralEntrySurface(path2) {
|
|
4615
4620
|
const normalized = normalizePath2(path2);
|
|
4616
4621
|
const segments = normalized.split("/");
|
|
4617
|
-
const
|
|
4618
|
-
if (
|
|
4622
|
+
const basename6 = segments[segments.length - 1] ?? normalized;
|
|
4623
|
+
if (basename6 === "cli.ts" || basename6 === "cli.js" || basename6 === "postinstall.ts" || basename6 === "postinstall.js" || basename6 === "main.ts" || basename6 === "main.js" || basename6 === "main.rs" || basename6 === "main.go" || basename6 === "main.py") {
|
|
4619
4624
|
return true;
|
|
4620
4625
|
}
|
|
4621
|
-
if (
|
|
4626
|
+
if (basename6 === "index.ts" || basename6 === "index.js") {
|
|
4622
4627
|
return segments.length <= 2;
|
|
4623
4628
|
}
|
|
4624
4629
|
return normalized.endsWith("/mod.rs") || normalized.endsWith("/__init__.py");
|
|
@@ -4982,22 +4987,37 @@ function outline(db, filePattern) {
|
|
|
4982
4987
|
ORDER BY d.relative_path, der.start_line`,
|
|
4983
4988
|
...resolvedPaths
|
|
4984
4989
|
);
|
|
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,
|
|
4995
|
+
children: []
|
|
4996
|
+
}));
|
|
4985
4997
|
const nodeMap = /* @__PURE__ */ new Map();
|
|
4998
|
+
for (const n of nodes) nodeMap.set(n.symbol, n);
|
|
4986
4999
|
const roots = [];
|
|
4987
|
-
for (
|
|
4988
|
-
const
|
|
4989
|
-
|
|
4990
|
-
shortName: shortenSymbol(r.symbol),
|
|
4991
|
-
startLine: r.start_line,
|
|
4992
|
-
endLine: r.end_line,
|
|
4993
|
-
children: []
|
|
4994
|
-
};
|
|
4995
|
-
nodeMap.set(r.symbol, node);
|
|
4996
|
-
}
|
|
4997
|
-
for (const r of rows) {
|
|
4998
|
-
const node = nodeMap.get(r.symbol);
|
|
5000
|
+
for (let i = 0; i < rows.length; i++) {
|
|
5001
|
+
const r = rows[i];
|
|
5002
|
+
const node = nodes[i];
|
|
4999
5003
|
if (r.enclosing_symbol && nodeMap.has(r.enclosing_symbol)) {
|
|
5000
5004
|
nodeMap.get(r.enclosing_symbol).children.push(node);
|
|
5005
|
+
continue;
|
|
5006
|
+
}
|
|
5007
|
+
let bestParent = null;
|
|
5008
|
+
let bestSize = Infinity;
|
|
5009
|
+
for (const candidate of nodes) {
|
|
5010
|
+
if (candidate === node) continue;
|
|
5011
|
+
if (candidate.startLine <= node.startLine && candidate.endLine >= node.endLine) {
|
|
5012
|
+
const size = candidate.endLine - candidate.startLine;
|
|
5013
|
+
if (size < bestSize) {
|
|
5014
|
+
bestSize = size;
|
|
5015
|
+
bestParent = candidate;
|
|
5016
|
+
}
|
|
5017
|
+
}
|
|
5018
|
+
}
|
|
5019
|
+
if (bestParent) {
|
|
5020
|
+
bestParent.children.push(node);
|
|
5001
5021
|
} else {
|
|
5002
5022
|
roots.push(node);
|
|
5003
5023
|
}
|
|
@@ -5719,7 +5739,7 @@ function similar(db, symbolPattern, opts = {}) {
|
|
|
5719
5739
|
return similarBySourceShape(db, symbolPattern, { minSimilarity, limit });
|
|
5720
5740
|
}
|
|
5721
5741
|
function similarAll(db, opts = {}) {
|
|
5722
|
-
const { minSimilarity = 0.5, limit = 20, scope, minCallees = 4 } = opts;
|
|
5742
|
+
const { minSimilarity = 0.5, limit = 20, scope, minCallees = 4, crossFileOnly = false } = opts;
|
|
5723
5743
|
const all = getAllCalleeFingerprints(db, { minCallees, scope });
|
|
5724
5744
|
const idfWeights = computeIdf(all);
|
|
5725
5745
|
const results = [];
|
|
@@ -5727,14 +5747,16 @@ function similarAll(db, opts = {}) {
|
|
|
5727
5747
|
for (let j = i + 1; j < all.length; j++) {
|
|
5728
5748
|
const a = all[i];
|
|
5729
5749
|
const b = all[j];
|
|
5730
|
-
if (a.file === b.file) continue;
|
|
5750
|
+
if (crossFileOnly && a.file === b.file) continue;
|
|
5731
5751
|
const { similarity, significantShared } = weightedSimilarity(
|
|
5732
5752
|
a.callees,
|
|
5733
5753
|
b.callees,
|
|
5734
5754
|
idfWeights
|
|
5735
5755
|
);
|
|
5736
5756
|
if (similarity < minSimilarity) continue;
|
|
5737
|
-
|
|
5757
|
+
const sharedCount = intersection(a.callees, b.callees).size;
|
|
5758
|
+
if (significantShared.length < 2 && sharedCount < 4) continue;
|
|
5759
|
+
const displayShared = significantShared.length > 0 ? significantShared : [...intersection(a.callees, b.callees)];
|
|
5738
5760
|
results.push({
|
|
5739
5761
|
symbolA: a.symbol,
|
|
5740
5762
|
shortNameA: shortenSymbol(a.symbol),
|
|
@@ -5743,7 +5765,7 @@ function similarAll(db, opts = {}) {
|
|
|
5743
5765
|
shortNameB: shortenSymbol(b.symbol),
|
|
5744
5766
|
fileB: b.file,
|
|
5745
5767
|
similarity,
|
|
5746
|
-
sharedCallees:
|
|
5768
|
+
sharedCallees: displayShared.map(shortenSymbol),
|
|
5747
5769
|
uniqueToA: [...difference(a.callees, b.callees)].map(shortenSymbol),
|
|
5748
5770
|
uniqueToB: [...difference(b.callees, a.callees)].map(shortenSymbol)
|
|
5749
5771
|
});
|
|
@@ -6121,8 +6143,8 @@ function similarChains(db, opts = {}) {
|
|
|
6121
6143
|
}
|
|
6122
6144
|
const structuralNames = ["index.ts", "index.js", "cli.ts", "main.ts", "health.ts", "health.js"];
|
|
6123
6145
|
for (const node of nodeFreq.keys()) {
|
|
6124
|
-
const
|
|
6125
|
-
if (structuralNames.includes(
|
|
6146
|
+
const basename6 = node.split("/").pop() ?? "";
|
|
6147
|
+
if (structuralNames.includes(basename6)) infraNodes.add(node);
|
|
6126
6148
|
}
|
|
6127
6149
|
const filteredChains = [];
|
|
6128
6150
|
for (const chain of rawChains) {
|
|
@@ -6296,8 +6318,8 @@ function extractCandidates(db, opts = {}) {
|
|
|
6296
6318
|
const results = [];
|
|
6297
6319
|
for (const sym of symbols2) {
|
|
6298
6320
|
if (db.isIgnored(sym.relativePath)) continue;
|
|
6299
|
-
const
|
|
6300
|
-
if (
|
|
6321
|
+
const basename6 = sym.relativePath.split("/").pop() ?? "";
|
|
6322
|
+
if (basename6.includes("types")) continue;
|
|
6301
6323
|
const calleeChunks = getCalleeRowsForSymbol(db, sym);
|
|
6302
6324
|
const calleeSet = new Set(calleeChunks.map((c) => c.symbol));
|
|
6303
6325
|
if (calleeSet.size < minCallees) continue;
|
|
@@ -6373,17 +6395,6 @@ function extractCandidates(db, opts = {}) {
|
|
|
6373
6395
|
results.sort((a, b) => b.clusters.length - a.clusters.length || b.loc - a.loc);
|
|
6374
6396
|
return results.slice(0, limit);
|
|
6375
6397
|
}
|
|
6376
|
-
function getScopedDefinitions(db, scope) {
|
|
6377
|
-
const scopeFilter = scope ? `AND relative_path LIKE '%${scope}%'` : "";
|
|
6378
|
-
return db.all(
|
|
6379
|
-
`SELECT relative_path
|
|
6380
|
-
FROM documents
|
|
6381
|
-
WHERE 1 = 1
|
|
6382
|
-
${db.pathExclusionsFor("documents")}
|
|
6383
|
-
${scopeFilter}
|
|
6384
|
-
ORDER BY relative_path`
|
|
6385
|
-
).flatMap((row) => getDefinitionsForFile(db, row.relative_path)).filter((row) => !db.isIgnored(row.relativePath));
|
|
6386
|
-
}
|
|
6387
6398
|
function definitionLoc(definition) {
|
|
6388
6399
|
return definition.endLine - definition.startLine + 1;
|
|
6389
6400
|
}
|
|
@@ -6809,32 +6820,32 @@ function isLikelyTypeOnlyDep(dep) {
|
|
|
6809
6820
|
function shouldSkipDriftFile(filePath) {
|
|
6810
6821
|
return isStructuralRole(path.basename(filePath)) || isTestLikePath(filePath);
|
|
6811
6822
|
}
|
|
6812
|
-
function isStructuralRole(
|
|
6813
|
-
if (
|
|
6814
|
-
if (
|
|
6815
|
-
if (
|
|
6816
|
-
if (
|
|
6823
|
+
function isStructuralRole(basename6) {
|
|
6824
|
+
if (basename6 === "index.ts" || basename6 === "index.js") return true;
|
|
6825
|
+
if (basename6 === "cli.ts" || basename6 === "main.ts" || basename6 === "main.rs") return true;
|
|
6826
|
+
if (basename6.includes("worker.") || basename6.includes("postinstall.")) return true;
|
|
6827
|
+
if (basename6 === "health.ts" || basename6 === "health.js") return true;
|
|
6817
6828
|
return false;
|
|
6818
6829
|
}
|
|
6819
6830
|
function isTestLikePath(filePath) {
|
|
6820
6831
|
const normalized = filePath.replace(/\\/g, "/");
|
|
6821
|
-
const
|
|
6822
|
-
return normalized.includes("/__tests__/") || normalized.includes("/tests/") || normalized.includes("/test/") || /\.(test|spec)\.[A-Za-z0-9]+$/.test(
|
|
6832
|
+
const basename6 = path.basename(normalized);
|
|
6833
|
+
return normalized.includes("/__tests__/") || normalized.includes("/tests/") || normalized.includes("/test/") || /\.(test|spec)\.[A-Za-z0-9]+$/.test(basename6) || /_(test|spec)\.[A-Za-z0-9]+$/.test(basename6) || /^test[_-]/.test(basename6) || /^test\./.test(basename6);
|
|
6823
6834
|
}
|
|
6824
6835
|
|
|
6825
6836
|
// src/queries/wrapper-candidates.ts
|
|
6826
|
-
import { basename as
|
|
6837
|
+
import { basename as basename5, extname as extname4 } from "path";
|
|
6827
6838
|
function wrapperCandidates(db, opts) {
|
|
6828
6839
|
const { scope, maxLoc = 15, limit = 30 } = opts ?? {};
|
|
6829
6840
|
const reverseFanIn = buildReverseFileFanIn(buildFileDepGraph(db, scope));
|
|
6830
|
-
const symbols2 =
|
|
6841
|
+
const symbols2 = getScopedDefinitions(db, scope).filter((definition) => definitionLoc2(definition) <= maxLoc && definitionLoc2(definition) >= 2);
|
|
6831
6842
|
const results = [];
|
|
6832
6843
|
for (const symbol of symbols2) {
|
|
6833
6844
|
if (db.isIgnored(symbol.relativePath) || !isFunctionLikeSymbol(symbol.symbol)) continue;
|
|
6834
|
-
const symbolStem =
|
|
6845
|
+
const symbolStem = basename5(symbol.relativePath, extname4(symbol.relativePath));
|
|
6835
6846
|
const callerRows = dedupeRows(
|
|
6836
6847
|
getCallerRowsForSymbol(db, symbol, { limit: 200 }).filter((row) => row.file !== symbol.relativePath)
|
|
6837
|
-
).filter((row) =>
|
|
6848
|
+
).filter((row) => basename5(row.file, extname4(row.file)) !== symbolStem);
|
|
6838
6849
|
if (callerRows.length !== 1) continue;
|
|
6839
6850
|
const caller = callerRows[0];
|
|
6840
6851
|
const callerDefinition = getDefinitionsForFile(db, caller.file).find((definition) => definition.symbol === caller.symbol);
|
|
@@ -6856,7 +6867,7 @@ function wrapperCandidates(db, opts) {
|
|
|
6856
6867
|
endLine: symbol.endLine,
|
|
6857
6868
|
loc: definitionLoc2(symbol),
|
|
6858
6869
|
singleCaller: caller.symbol,
|
|
6859
|
-
singleCallerShort: useDefinitionFanIn ? shortenSymbol(caller.symbol) :
|
|
6870
|
+
singleCallerShort: useDefinitionFanIn ? shortenSymbol(caller.symbol) : basename5(caller.file),
|
|
6860
6871
|
callerFanIn
|
|
6861
6872
|
});
|
|
6862
6873
|
}
|
|
@@ -6866,17 +6877,6 @@ function wrapperCandidates(db, opts) {
|
|
|
6866
6877
|
function definitionLoc2(definition) {
|
|
6867
6878
|
return definition.endLine - definition.startLine + 1;
|
|
6868
6879
|
}
|
|
6869
|
-
function getScopedDefinitions2(db, scope) {
|
|
6870
|
-
const scopeFilter = scope ? `AND relative_path LIKE '%${scope}%'` : "";
|
|
6871
|
-
return db.all(
|
|
6872
|
-
`SELECT relative_path
|
|
6873
|
-
FROM documents
|
|
6874
|
-
WHERE 1 = 1
|
|
6875
|
-
${db.pathExclusionsFor("documents")}
|
|
6876
|
-
${scopeFilter}
|
|
6877
|
-
ORDER BY relative_path`
|
|
6878
|
-
).flatMap((row) => getDefinitionsForFile(db, row.relative_path)).filter((row) => !db.isIgnored(row.relativePath));
|
|
6879
|
-
}
|
|
6880
6880
|
function dedupeRows(rows) {
|
|
6881
6881
|
const seen = /* @__PURE__ */ new Set();
|
|
6882
6882
|
const unique = [];
|
|
@@ -6911,11 +6911,11 @@ function fallbackCallerFanIn(db, reverseFanIn, callerFile) {
|
|
|
6911
6911
|
if (direct > 0) {
|
|
6912
6912
|
return direct;
|
|
6913
6913
|
}
|
|
6914
|
-
const stem =
|
|
6914
|
+
const stem = basename5(callerFile, extname4(callerFile));
|
|
6915
6915
|
let best = 0;
|
|
6916
6916
|
for (const [file, fanIn2] of reverseFanIn) {
|
|
6917
6917
|
if (file === callerFile) continue;
|
|
6918
|
-
if (
|
|
6918
|
+
if (basename5(file, extname4(file)) !== stem) continue;
|
|
6919
6919
|
if (fanIn2 > best) {
|
|
6920
6920
|
best = fanIn2;
|
|
6921
6921
|
}
|
|
@@ -6926,7 +6926,7 @@ function fallbackCallerFanIn(db, reverseFanIn, callerFile) {
|
|
|
6926
6926
|
// src/queries/passthrough-candidates.ts
|
|
6927
6927
|
function passthroughCandidates(db, opts) {
|
|
6928
6928
|
const { scope, maxLoc = 15, limit = 30 } = opts ?? {};
|
|
6929
|
-
const symbols2 =
|
|
6929
|
+
const symbols2 = getScopedDefinitions(db, scope).filter((definition) => definitionLoc3(definition) >= 3 && definitionLoc3(definition) <= maxLoc);
|
|
6930
6930
|
const results = [];
|
|
6931
6931
|
for (const sym of symbols2) {
|
|
6932
6932
|
if (db.isIgnored(sym.relativePath) || !isFunctionLikeSymbol(sym.symbol)) continue;
|
|
@@ -6955,17 +6955,6 @@ function passthroughCandidates(db, opts) {
|
|
|
6955
6955
|
results.sort((a, b) => a.loc - b.loc || a.file.localeCompare(b.file));
|
|
6956
6956
|
return results.slice(0, limit);
|
|
6957
6957
|
}
|
|
6958
|
-
function getScopedDefinitions3(db, scope) {
|
|
6959
|
-
const scopeFilter = scope ? `AND relative_path LIKE '%${scope}%'` : "";
|
|
6960
|
-
return db.all(
|
|
6961
|
-
`SELECT relative_path
|
|
6962
|
-
FROM documents
|
|
6963
|
-
WHERE 1 = 1
|
|
6964
|
-
${db.pathExclusionsFor("documents")}
|
|
6965
|
-
${scopeFilter}
|
|
6966
|
-
ORDER BY relative_path`
|
|
6967
|
-
).flatMap((row) => getDefinitionsForFile(db, row.relative_path)).filter((row) => !db.isIgnored(row.relativePath));
|
|
6968
|
-
}
|
|
6969
6958
|
function definitionLoc3(definition) {
|
|
6970
6959
|
return definition.endLine - definition.startLine + 1;
|
|
6971
6960
|
}
|
|
@@ -6973,7 +6962,7 @@ function definitionLoc3(definition) {
|
|
|
6973
6962
|
// src/queries/stale-abstractions.ts
|
|
6974
6963
|
function staleAbstractions(db, opts) {
|
|
6975
6964
|
const { scope, minLoc = 3, limit = 30 } = opts ?? {};
|
|
6976
|
-
const rows =
|
|
6965
|
+
const rows = getScopedDefinitions(db, scope).filter((definition) => definition.isTypeLike && definitionLoc4(definition) >= minLoc).map((definition) => ({
|
|
6977
6966
|
symbol: definition.symbol,
|
|
6978
6967
|
file: definition.relativePath,
|
|
6979
6968
|
start_line: definition.startLine,
|
|
@@ -6993,11 +6982,11 @@ function staleAbstractions(db, opts) {
|
|
|
6993
6982
|
})).slice(0, limit);
|
|
6994
6983
|
}
|
|
6995
6984
|
function getFilesWithFunctions(db, scope) {
|
|
6996
|
-
return new Set(
|
|
6985
|
+
return new Set(getScopedDefinitions(db, scope).filter((definition) => definition.isFunctionLike).map((definition) => definition.relativePath));
|
|
6997
6986
|
}
|
|
6998
6987
|
function isTrueStaleAbstraction(row, filesWithFunctions) {
|
|
6999
|
-
const
|
|
7000
|
-
const isTypeFile =
|
|
6988
|
+
const basename6 = row.file.split("/").pop() ?? "";
|
|
6989
|
+
const isTypeFile = basename6.includes("types") || row.file.includes("/types/");
|
|
7001
6990
|
if (isTypeFile && row.consumers > 0) {
|
|
7002
6991
|
return false;
|
|
7003
6992
|
}
|
|
@@ -7006,17 +6995,6 @@ function isTrueStaleAbstraction(row, filesWithFunctions) {
|
|
|
7006
6995
|
}
|
|
7007
6996
|
return true;
|
|
7008
6997
|
}
|
|
7009
|
-
function getScopedDefinitions4(db, scope) {
|
|
7010
|
-
const scopeFilter = scope ? `AND relative_path LIKE '%${scope}%'` : "";
|
|
7011
|
-
return db.all(
|
|
7012
|
-
`SELECT relative_path
|
|
7013
|
-
FROM documents
|
|
7014
|
-
WHERE 1 = 1
|
|
7015
|
-
${db.pathExclusionsFor("documents")}
|
|
7016
|
-
${scopeFilter}
|
|
7017
|
-
ORDER BY relative_path`
|
|
7018
|
-
).flatMap((row) => getDefinitionsForFile(db, row.relative_path)).filter((row) => !db.isIgnored(row.relativePath));
|
|
7019
|
-
}
|
|
7020
6998
|
function countCrossFileConsumers(db, definition) {
|
|
7021
6999
|
const callers = db.all(
|
|
7022
7000
|
`SELECT DISTINCT d.relative_path
|
|
@@ -7395,7 +7373,7 @@ function convergence(db, symbolPatternA, symbolPatternB) {
|
|
|
7395
7373
|
}
|
|
7396
7374
|
|
|
7397
7375
|
// src/queries/code.ts
|
|
7398
|
-
import { readFileSync as
|
|
7376
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
7399
7377
|
import { join as join9 } from "path";
|
|
7400
7378
|
function code(db, symbolPattern, opts = {}) {
|
|
7401
7379
|
const { context = 0 } = opts;
|
|
@@ -7412,7 +7390,7 @@ function code(db, symbolPattern, opts = {}) {
|
|
|
7412
7390
|
const filePath = join9(db.config.projectRoot, match.relativePath);
|
|
7413
7391
|
let fileContent;
|
|
7414
7392
|
try {
|
|
7415
|
-
fileContent =
|
|
7393
|
+
fileContent = readFileSync5(filePath, "utf-8");
|
|
7416
7394
|
} catch {
|
|
7417
7395
|
return null;
|
|
7418
7396
|
}
|
|
@@ -7424,9 +7402,11 @@ function code(db, symbolPattern, opts = {}) {
|
|
|
7424
7402
|
symbol: match.symbol,
|
|
7425
7403
|
shortName: shortenSymbol(match.symbol),
|
|
7426
7404
|
relativePath: match.relativePath,
|
|
7427
|
-
|
|
7428
|
-
// 1-indexed
|
|
7429
|
-
|
|
7405
|
+
// 0-indexed, like every other query result. The CLI's displayLine()
|
|
7406
|
+
// converts once at render time. Returning 1-indexed here caused a
|
|
7407
|
+
// double-conversion in the CLI and printed labels off by +1.
|
|
7408
|
+
startLine,
|
|
7409
|
+
endLine,
|
|
7430
7410
|
language: doc?.language ?? null,
|
|
7431
7411
|
source
|
|
7432
7412
|
};
|
|
@@ -7442,7 +7422,7 @@ function readFileRange(db, filePath, startLine, endLine, context) {
|
|
|
7442
7422
|
const fullPath = join9(db.config.projectRoot, doc.relative_path);
|
|
7443
7423
|
let fileContent;
|
|
7444
7424
|
try {
|
|
7445
|
-
fileContent =
|
|
7425
|
+
fileContent = readFileSync5(fullPath, "utf-8");
|
|
7446
7426
|
} catch {
|
|
7447
7427
|
return null;
|
|
7448
7428
|
}
|
|
@@ -7454,15 +7434,15 @@ function readFileRange(db, filePath, startLine, endLine, context) {
|
|
|
7454
7434
|
symbol: `${doc.relative_path}:${startLine}-${endLine}`,
|
|
7455
7435
|
shortName: `${doc.relative_path}:${startLine}-${endLine}`,
|
|
7456
7436
|
relativePath: doc.relative_path,
|
|
7457
|
-
startLine: start
|
|
7458
|
-
endLine: end
|
|
7437
|
+
startLine: start,
|
|
7438
|
+
endLine: end,
|
|
7459
7439
|
language: doc.language,
|
|
7460
7440
|
source
|
|
7461
7441
|
};
|
|
7462
7442
|
}
|
|
7463
7443
|
|
|
7464
7444
|
// src/queries/complexity.ts
|
|
7465
|
-
import { readFileSync as
|
|
7445
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
7466
7446
|
import { join as join10 } from "path";
|
|
7467
7447
|
function complexity(db, symbolPattern) {
|
|
7468
7448
|
const match = findFirstSymbolMatch(db, symbolPattern);
|
|
@@ -7475,7 +7455,7 @@ function complexity(db, symbolPattern) {
|
|
|
7475
7455
|
const filePath = join10(db.config.projectRoot, match.relativePath);
|
|
7476
7456
|
let source = "";
|
|
7477
7457
|
try {
|
|
7478
|
-
const lines =
|
|
7458
|
+
const lines = readFileSync6(filePath, "utf-8").split("\n");
|
|
7479
7459
|
source = lines.slice(match.startLine, match.endLine + 1).join("\n");
|
|
7480
7460
|
} catch {
|
|
7481
7461
|
}
|
|
@@ -7635,28 +7615,40 @@ function uniqueSymbolRows(rows) {
|
|
|
7635
7615
|
|
|
7636
7616
|
// src/queries/slice.ts
|
|
7637
7617
|
function slice(db, symbolPattern, opts = {}) {
|
|
7638
|
-
const { direction = "backward" } = opts;
|
|
7618
|
+
const { direction = "backward", maxDepth = 3 } = opts;
|
|
7639
7619
|
const match = findFirstSymbolMatch(db, symbolPattern);
|
|
7640
7620
|
if (!match) return null;
|
|
7641
7621
|
if (direction === "backward") {
|
|
7642
|
-
return backwardSlice(db, match);
|
|
7622
|
+
return backwardSlice(db, match, maxDepth);
|
|
7643
7623
|
} else {
|
|
7644
7624
|
return forwardSlice(db, match);
|
|
7645
7625
|
}
|
|
7646
7626
|
}
|
|
7647
|
-
function backwardSlice(db, match) {
|
|
7648
|
-
const callees = getCalleeRowsForSymbol(db, match);
|
|
7649
|
-
const seen = /* @__PURE__ */ new Set();
|
|
7627
|
+
function backwardSlice(db, match, maxDepth) {
|
|
7650
7628
|
const connected = [];
|
|
7651
|
-
|
|
7652
|
-
|
|
7653
|
-
|
|
7654
|
-
|
|
7655
|
-
|
|
7656
|
-
|
|
7657
|
-
|
|
7658
|
-
|
|
7659
|
-
|
|
7629
|
+
const visited = /* @__PURE__ */ new Set([match.symbol]);
|
|
7630
|
+
let frontier = [match];
|
|
7631
|
+
for (let depth = 1; depth <= maxDepth; depth++) {
|
|
7632
|
+
if (frontier.length === 0) break;
|
|
7633
|
+
const nextFrontier = [];
|
|
7634
|
+
for (const current of frontier) {
|
|
7635
|
+
const callees = getCalleeRowsForSymbol(db, current);
|
|
7636
|
+
for (const c of callees) {
|
|
7637
|
+
if (visited.has(c.symbol)) continue;
|
|
7638
|
+
visited.add(c.symbol);
|
|
7639
|
+
connected.push({
|
|
7640
|
+
symbol: c.symbol,
|
|
7641
|
+
shortName: shortenSymbol(c.symbol),
|
|
7642
|
+
file: c.file,
|
|
7643
|
+
relationship: depth === 1 ? "referenced within definition (callee)" : `depth ${depth} callee`
|
|
7644
|
+
});
|
|
7645
|
+
const calleeMatch = findExactSymbolMatch(db, c.symbol);
|
|
7646
|
+
if (calleeMatch && !db.isIgnored(calleeMatch.relativePath)) {
|
|
7647
|
+
nextFrontier.push(calleeMatch);
|
|
7648
|
+
}
|
|
7649
|
+
}
|
|
7650
|
+
}
|
|
7651
|
+
frontier = nextFrontier;
|
|
7660
7652
|
}
|
|
7661
7653
|
return {
|
|
7662
7654
|
symbol: match.symbol,
|
|
@@ -8047,7 +8039,7 @@ import {
|
|
|
8047
8039
|
readlinkSync,
|
|
8048
8040
|
unlinkSync
|
|
8049
8041
|
} from "fs";
|
|
8050
|
-
import { join as join11, dirname as
|
|
8042
|
+
import { join as join11, dirname as dirname4, resolve as resolve4 } from "path";
|
|
8051
8043
|
import { homedir as homedir2, platform as platform3 } from "os";
|
|
8052
8044
|
import { fileURLToPath } from "url";
|
|
8053
8045
|
var IS_WINDOWS3 = platform3() === "win32";
|
|
@@ -8062,7 +8054,7 @@ function installSkills(opts = {}) {
|
|
|
8062
8054
|
const log = opts.quiet ? () => {
|
|
8063
8055
|
} : console.log;
|
|
8064
8056
|
const thisFile = fileURLToPath(import.meta.url);
|
|
8065
|
-
const skillsSource =
|
|
8057
|
+
const skillsSource = resolve4(dirname4(thisFile), "..", "skills");
|
|
8066
8058
|
const targets = [
|
|
8067
8059
|
join11(homedir2(), ".claude", "skills"),
|
|
8068
8060
|
join11(homedir2(), ".codex", "skills")
|
|
@@ -8073,7 +8065,7 @@ function installSkills(opts = {}) {
|
|
|
8073
8065
|
alreadyLinked: []
|
|
8074
8066
|
};
|
|
8075
8067
|
for (const targetDir of targets) {
|
|
8076
|
-
const parentDir =
|
|
8068
|
+
const parentDir = dirname4(targetDir);
|
|
8077
8069
|
if (!existsSync9(parentDir)) {
|
|
8078
8070
|
continue;
|
|
8079
8071
|
}
|
|
@@ -8089,7 +8081,7 @@ function installSkills(opts = {}) {
|
|
|
8089
8081
|
if (existsSync9(target)) {
|
|
8090
8082
|
try {
|
|
8091
8083
|
const existing = readlinkSync(target);
|
|
8092
|
-
if (
|
|
8084
|
+
if (resolve4(existing) === resolve4(source)) {
|
|
8093
8085
|
result.alreadyLinked.push(`${toolName}/${skill}`);
|
|
8094
8086
|
log(` ok: ${skill} \u2192 ${toolName} (already linked)`);
|
|
8095
8087
|
continue;
|
|
@@ -8216,11 +8208,15 @@ var queries = {
|
|
|
8216
8208
|
program.name("scip-query").description("Language-agnostic code intelligence CLI powered by SCIP indexes").version(cliVersion);
|
|
8217
8209
|
program.command("reindex").description("Index the codebase and convert to SQLite").option("-l, --language <lang>", "Index only this language (can be repeated)", collect, []).option("--pnpm-workspaces", "Enable pnpm workspace support (TypeScript)").action(async (opts) => {
|
|
8218
8210
|
const projectRoot = resolveProjectRoot();
|
|
8211
|
+
const config = loadProjectConfig(projectRoot);
|
|
8212
|
+
const paths = resolveIndexPaths(projectRoot, config);
|
|
8219
8213
|
try {
|
|
8220
8214
|
const result = await reindex({
|
|
8221
8215
|
projectRoot,
|
|
8222
|
-
languages: opts.language.length > 0 ? opts.language :
|
|
8223
|
-
|
|
8216
|
+
languages: opts.language.length > 0 ? opts.language : config.languages,
|
|
8217
|
+
outputScip: paths.indexPath,
|
|
8218
|
+
outputDb: paths.dbPath,
|
|
8219
|
+
pnpmWorkspaces: opts.pnpmWorkspaces || config.indexer?.typescript?.pnpmWorkspaces
|
|
8224
8220
|
});
|
|
8225
8221
|
console.log(`Indexed ${result.languages.join(", ")} in ${(result.durationMs / 1e3).toFixed(1)}s`);
|
|
8226
8222
|
} catch (err) {
|
|
@@ -8663,7 +8659,7 @@ program.command("call-graph <symbol>").description("Show incoming callers and ou
|
|
|
8663
8659
|
}
|
|
8664
8660
|
);
|
|
8665
8661
|
});
|
|
8666
|
-
program.command("similar [symbol]").description("Find functions with similar callee fingerprints (consolidation candidates)").option("--min-similarity <n>", "Minimum Jaccard similarity (0-1)", parseFloat, 0.4).option("-n, --limit <n>", "Number of results", parseIntSafe, 20).option("-s, --scope <path>", "Limit to files matching path").option("--min-callees <n>", "Minimum callees to consider", parseIntSafe, 4).action((symbol, opts) => {
|
|
8662
|
+
program.command("similar [symbol]").description("Find functions with similar callee fingerprints (consolidation candidates)").option("--min-similarity <n>", "Minimum Jaccard similarity (0-1)", parseFloat, 0.4).option("-n, --limit <n>", "Number of results", parseIntSafe, 20).option("-s, --scope <path>", "Limit to files matching path").option("--min-callees <n>", "Minimum callees to consider", parseIntSafe, 4).option("--cross-file-only", "Only show cross-file pairs (skip same-file matches)").action((symbol, opts) => {
|
|
8667
8663
|
withDb((db) => {
|
|
8668
8664
|
if (symbol) {
|
|
8669
8665
|
const results = queries.similar(db, symbol, {
|
|
@@ -8688,7 +8684,8 @@ ${Math.round(r.similarity * 100)}% similar:`);
|
|
|
8688
8684
|
minSimilarity: opts.minSimilarity,
|
|
8689
8685
|
limit: opts.limit,
|
|
8690
8686
|
scope: opts.scope,
|
|
8691
|
-
minCallees: opts.minCallees
|
|
8687
|
+
minCallees: opts.minCallees,
|
|
8688
|
+
crossFileOnly: opts.crossFileOnly
|
|
8692
8689
|
});
|
|
8693
8690
|
if (results.length === 0) {
|
|
8694
8691
|
console.log("No similar symbol pairs found.");
|
|
@@ -9083,10 +9080,10 @@ program.command("dataflow <symbol>").description("Reference-level dataflow: defi
|
|
|
9083
9080
|
}
|
|
9084
9081
|
db.close();
|
|
9085
9082
|
});
|
|
9086
|
-
program.command("slice <symbol>").description("Reference-level program slice: what affects this (backward) or what this affects (forward)").option("--forward", "Forward slice (what does this affect). Default is backward.").action((symbol, opts) => {
|
|
9083
|
+
program.command("slice <symbol>").description("Reference-level program slice: what affects this (backward) or what this affects (forward)").option("--forward", "Forward slice (what does this affect). Default is backward.").option("--depth <n>", "Max transitive depth for backward slice", parseIntSafe, 3).action((symbol, opts) => {
|
|
9087
9084
|
const db = openDb();
|
|
9088
9085
|
const direction = opts.forward ? "forward" : "backward";
|
|
9089
|
-
const result = queries.slice(db, symbol, { direction });
|
|
9086
|
+
const result = queries.slice(db, symbol, { direction, maxDepth: opts.depth });
|
|
9090
9087
|
if (!result) {
|
|
9091
9088
|
console.log("Symbol not found.");
|
|
9092
9089
|
db.close();
|
|
@@ -9205,6 +9202,7 @@ program.command("watch").description("Watch for file changes and reindex automat
|
|
|
9205
9202
|
const watcher = new Watcher({
|
|
9206
9203
|
projectRoot,
|
|
9207
9204
|
config,
|
|
9205
|
+
languages: config.languages,
|
|
9208
9206
|
onStatus: (status) => {
|
|
9209
9207
|
process.stdout.write(`\r\x1B[K${formatStatus(status)}`);
|
|
9210
9208
|
},
|