scip-query 0.4.0 → 0.4.2
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/README.md +1 -1
- package/dist/{chunk-HNURMDF4.js → chunk-24LF6IZB.js} +3 -3
- package/dist/{chunk-IV6NZ426.js → chunk-3NJSJ7TE.js} +3 -3
- package/dist/{chunk-H3FPW5YN.js → chunk-43A4UCS7.js} +3 -3
- package/dist/{chunk-A5BGEBM7.js → chunk-5GCORUNV.js} +3 -3
- package/dist/{chunk-I2JM34UV.js → chunk-6CH23IAS.js} +4 -4
- package/dist/{chunk-5AJJGPZE.js → chunk-6ECR2FLR.js} +3 -3
- package/dist/{chunk-2UISVZGQ.js → chunk-6UZU7DFL.js} +3 -3
- package/dist/{chunk-NWCJWA36.js → chunk-7BS4CPJX.js} +5 -5
- package/dist/{chunk-QCYR4S6T.js → chunk-A6XLXV6W.js} +3 -3
- package/dist/{chunk-QGCEAVJD.js → chunk-ALUFWH3U.js} +232 -66
- package/dist/{chunk-CQRYLK33.js → chunk-CBIWNZZZ.js} +3 -3
- package/dist/{chunk-N2LH3M2P.js → chunk-DUJNJQPO.js} +3 -3
- package/dist/{chunk-XH56HXLC.js → chunk-EAGKJFDX.js} +3 -3
- package/dist/{chunk-SL674KAW.js → chunk-ELFGD5EW.js} +3 -3
- package/dist/{chunk-GIBETK3W.js → chunk-FVJE4MQL.js} +3 -3
- package/dist/{chunk-CQUNEJYM.js → chunk-GNAMV3JC.js} +3 -3
- package/dist/{chunk-VCOJRQPP.js → chunk-J47VSL6I.js} +3 -3
- package/dist/{chunk-7YBLWIXY.js → chunk-J6QXMYAQ.js} +3 -3
- package/dist/{chunk-2F2WH5WQ.js → chunk-JHVQB4Y5.js} +12 -12
- package/dist/{chunk-X3J4VPWM.js → chunk-JKXHHV4B.js} +2 -2
- package/dist/{chunk-4ZT7UGWW.js → chunk-KG4OFQEN.js} +3 -3
- package/dist/{chunk-SRELHCMG.js → chunk-KLNKDX6A.js} +4 -4
- package/dist/{chunk-LOVDB4C6.js → chunk-KYPXKV64.js} +3 -3
- package/dist/{chunk-UTRKBUCB.js → chunk-NXUIWD6K.js} +3 -3
- package/dist/{chunk-VU7FDTWV.js → chunk-OXX3QF24.js} +2 -2
- package/dist/{chunk-7HK5ZLOE.js → chunk-PCU455MX.js} +2 -2
- package/dist/{chunk-A4GWYETB.js → chunk-POLELLNM.js} +3 -3
- package/dist/{chunk-AS7N27JK.js → chunk-PU2254N2.js} +3 -3
- package/dist/{chunk-WNPF2I25.js → chunk-QMXSLHZP.js} +2 -2
- package/dist/{chunk-PGHN5UTM.js → chunk-R7HPHMRZ.js} +3 -3
- package/dist/{chunk-VUBLUTMU.js → chunk-RE7POFGI.js} +2 -2
- package/dist/{chunk-KDCQJTYW.js → chunk-RJ5GULL6.js} +2 -2
- package/dist/{chunk-HRDPUTIQ.js → chunk-RL74LF47.js} +2 -2
- package/dist/{chunk-5RKYZSQ6.js → chunk-SVLUJSY7.js} +3 -3
- package/dist/{chunk-D4I3ZMN5.js → chunk-SYQR4QGK.js} +3 -3
- package/dist/{chunk-QIXNAB5K.js → chunk-TO3L4YNK.js} +1 -2
- package/dist/{chunk-P42KQKJZ.js → chunk-TWVXFKJA.js} +4 -4
- package/dist/{chunk-MA3B3IUT.js → chunk-UJWI5CBB.js} +3 -3
- package/dist/{chunk-E7J7Q7UW.js → chunk-VKBOLNYN.js} +3 -3
- package/dist/{chunk-RIEA5DOB.js → chunk-VY2L4TP6.js} +3 -3
- package/dist/{chunk-A7YY7IDA.js → chunk-W46L2BXT.js} +2 -2
- package/dist/{chunk-VISMEWYP.js → chunk-XUVPQDXW.js} +4 -4
- package/dist/{chunk-ZU2AQQB5.js → chunk-Z5VSUOEE.js} +2 -2
- package/dist/{chunk-EOHPASDV.js → chunk-ZVZAIIB5.js} +3 -3
- package/dist/cli.js +516 -179
- package/dist/index.d.ts +18 -1
- package/dist/index.js +239 -70
- package/dist/queries/affected.js +3 -3
- package/dist/queries/bottlenecks.js +3 -3
- package/dist/queries/by-kind.js +3 -3
- package/dist/queries/call-graph.js +3 -3
- package/dist/queries/change-surface.js +3 -3
- package/dist/queries/code.js +3 -3
- package/dist/queries/complexity-hotspots.js +3 -3
- package/dist/queries/complexity.js +3 -3
- package/dist/queries/convergence.js +3 -3
- package/dist/queries/coupling.js +3 -3
- package/dist/queries/cycles.js +3 -3
- package/dist/queries/dataflow.js +3 -3
- package/dist/queries/dead.js +4 -4
- package/dist/queries/deep-chains.js +3 -3
- package/dist/queries/deps.js +3 -3
- package/dist/queries/diff-impact.js +2 -2
- package/dist/queries/drift.js +3 -3
- package/dist/queries/extract-candidates.js +3 -3
- package/dist/queries/fan.js +3 -3
- package/dist/queries/health.js +14 -14
- package/dist/queries/hierarchy.js +3 -3
- package/dist/queries/hotspots.js +3 -3
- package/dist/queries/imports.js +3 -3
- package/dist/queries/index.js +44 -44
- package/dist/queries/isolated.js +4 -4
- package/dist/queries/members.js +3 -3
- package/dist/queries/methods.js +3 -3
- package/dist/queries/outline.js +3 -3
- package/dist/queries/passthrough-candidates.js +3 -3
- package/dist/queries/redundant-reexports.js +4 -4
- package/dist/queries/refs.js +3 -3
- package/dist/queries/similar-chains.js +3 -3
- package/dist/queries/similar-files.js +3 -3
- package/dist/queries/similar-signatures.js +3 -3
- package/dist/queries/similar.js +3 -3
- package/dist/queries/slice.js +3 -3
- package/dist/queries/stale-abstractions.js +3 -3
- package/dist/queries/surface.js +3 -3
- package/dist/queries/symbols.js +3 -3
- package/dist/queries/system.js +3 -3
- package/dist/queries/trace.js +3 -3
- package/dist/queries/wrapper-candidates.js +3 -3
- package/dist/reindex-worker.js +149 -7
- package/package.json +7 -1
package/dist/cli.js
CHANGED
|
@@ -115,7 +115,7 @@ var ScipDatabase = class {
|
|
|
115
115
|
// src/gitignore-filter.ts
|
|
116
116
|
import ignore from "ignore";
|
|
117
117
|
import { readFileSync, existsSync } from "fs";
|
|
118
|
-
import { join,
|
|
118
|
+
import { dirname, isAbsolute, join, relative, resolve } from "path";
|
|
119
119
|
function createGitignoreFilter(projectRoot) {
|
|
120
120
|
const ig = ignore();
|
|
121
121
|
let loaded = false;
|
|
@@ -132,8 +132,8 @@ function createGitignoreFilter(projectRoot) {
|
|
|
132
132
|
ig.add(DEFAULT_IGNORES);
|
|
133
133
|
}
|
|
134
134
|
return {
|
|
135
|
-
isIgnored: (relativePath) => ig
|
|
136
|
-
filter: (paths) => paths.filter((p) => !ig
|
|
135
|
+
isIgnored: (relativePath) => safeIgnores(ig, projectRoot, relativePath),
|
|
136
|
+
filter: (paths) => paths.filter((p) => !safeIgnores(ig, projectRoot, p))
|
|
137
137
|
};
|
|
138
138
|
}
|
|
139
139
|
function findGitignoreFiles(projectRoot) {
|
|
@@ -213,10 +213,35 @@ Thumbs.db
|
|
|
213
213
|
# Type definitions (often noise in queries)
|
|
214
214
|
*.d.ts
|
|
215
215
|
`;
|
|
216
|
+
function safeIgnores(ig, projectRoot, inputPath) {
|
|
217
|
+
const relativePath = normalizeForIgnore(projectRoot, inputPath);
|
|
218
|
+
if (!relativePath) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
return ig.ignores(relativePath);
|
|
223
|
+
} catch {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function normalizeForIgnore(projectRoot, inputPath) {
|
|
228
|
+
if (!inputPath || inputPath === ".") {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
if (!isAbsolute(inputPath) && !inputPath.startsWith("..")) {
|
|
232
|
+
return inputPath.replaceAll("\\", "/");
|
|
233
|
+
}
|
|
234
|
+
const absolutePath = isAbsolute(inputPath) ? inputPath : resolve(projectRoot, inputPath);
|
|
235
|
+
const relativePath = relative(projectRoot, absolutePath).replaceAll("\\", "/");
|
|
236
|
+
if (!relativePath || relativePath === "." || relativePath.startsWith("..")) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
return relativePath;
|
|
240
|
+
}
|
|
216
241
|
|
|
217
242
|
// src/config.ts
|
|
218
243
|
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
|
|
219
|
-
import { join as join2, resolve } from "path";
|
|
244
|
+
import { join as join2, resolve as resolve2 } from "path";
|
|
220
245
|
import { createHash } from "crypto";
|
|
221
246
|
import { homedir } from "os";
|
|
222
247
|
var CONFIG_FILENAME = ".scipquery.json";
|
|
@@ -247,10 +272,10 @@ function resolveWatchConfig(config) {
|
|
|
247
272
|
function resolveCacheDir(projectRoot, config) {
|
|
248
273
|
const envOverride = process.env["SCIP_QUERY_CACHE_DIR"];
|
|
249
274
|
if (envOverride) return ensureDir(envOverride);
|
|
250
|
-
if (config?.dbPath) return ensureDir(
|
|
275
|
+
if (config?.dbPath) return ensureDir(resolve2(projectRoot, config.dbPath));
|
|
251
276
|
const xdgCache = process.env["XDG_CACHE_HOME"];
|
|
252
277
|
const cacheBase = xdgCache || join2(homedir(), ".cache");
|
|
253
|
-
const projectHash = createHash("sha256").update(
|
|
278
|
+
const projectHash = createHash("sha256").update(resolve2(projectRoot)).digest("hex").slice(0, 12);
|
|
254
279
|
const dir = join2(cacheBase, "scip-query", "projects", projectHash);
|
|
255
280
|
return ensureDir(dir);
|
|
256
281
|
}
|
|
@@ -286,8 +311,8 @@ function ensureDir(dir) {
|
|
|
286
311
|
|
|
287
312
|
// src/reindex/index.ts
|
|
288
313
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
289
|
-
import { existsSync as existsSync6, renameSync } from "fs";
|
|
290
|
-
import { join as join6 } from "path";
|
|
314
|
+
import { existsSync as existsSync6, renameSync, rmSync } from "fs";
|
|
315
|
+
import { basename, dirname as dirname2, extname as extname2, join as join6 } from "path";
|
|
291
316
|
|
|
292
317
|
// src/scip-cli.ts
|
|
293
318
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
@@ -968,6 +993,128 @@ function resolveDotnetProject(projectRoot, suffixes) {
|
|
|
968
993
|
return null;
|
|
969
994
|
}
|
|
970
995
|
|
|
996
|
+
// src/reindex/merge.ts
|
|
997
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
998
|
+
import { create } from "@bufbuild/protobuf";
|
|
999
|
+
import {
|
|
1000
|
+
deserializeSCIP,
|
|
1001
|
+
serializeSCIP,
|
|
1002
|
+
DocumentSchema,
|
|
1003
|
+
IndexSchema,
|
|
1004
|
+
SymbolInformationSchema
|
|
1005
|
+
} from "@c4312/scip";
|
|
1006
|
+
function mergeScipIndexes(indexes) {
|
|
1007
|
+
if (indexes.length === 0) {
|
|
1008
|
+
throw new Error("Cannot merge zero SCIP indexes");
|
|
1009
|
+
}
|
|
1010
|
+
if (indexes.length === 1) {
|
|
1011
|
+
return indexes[0];
|
|
1012
|
+
}
|
|
1013
|
+
const metadata = mergeMetadata(indexes);
|
|
1014
|
+
const documents = mergeDocuments(indexes.flatMap((index) => index.documents ?? []));
|
|
1015
|
+
const externalSymbols = mergeSymbolInfos(indexes.flatMap((index) => index.externalSymbols ?? []));
|
|
1016
|
+
return create(IndexSchema, {
|
|
1017
|
+
metadata,
|
|
1018
|
+
documents,
|
|
1019
|
+
externalSymbols
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
function mergeScipFiles(inputPaths, outputPath) {
|
|
1023
|
+
if (inputPaths.length === 0) {
|
|
1024
|
+
throw new Error("Cannot merge zero SCIP files");
|
|
1025
|
+
}
|
|
1026
|
+
const indexes = inputPaths.map((path2) => deserializeSCIP(readFileSync3(path2)));
|
|
1027
|
+
const merged = mergeScipIndexes(indexes);
|
|
1028
|
+
writeFileSync2(outputPath, Buffer.from(serializeSCIP(merged)));
|
|
1029
|
+
return {
|
|
1030
|
+
documentCount: merged.documents.length,
|
|
1031
|
+
externalSymbolCount: merged.externalSymbols.length,
|
|
1032
|
+
inputCount: inputPaths.length
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
function mergeMetadata(indexes) {
|
|
1036
|
+
const first = indexes[0]?.metadata;
|
|
1037
|
+
if (!first) {
|
|
1038
|
+
return void 0;
|
|
1039
|
+
}
|
|
1040
|
+
const expectedProjectRoot = first.projectRoot;
|
|
1041
|
+
for (const index of indexes.slice(1)) {
|
|
1042
|
+
const actualProjectRoot = index.metadata?.projectRoot;
|
|
1043
|
+
if (expectedProjectRoot && actualProjectRoot && actualProjectRoot !== expectedProjectRoot) {
|
|
1044
|
+
throw new Error(
|
|
1045
|
+
`Cannot merge SCIP indexes with different project roots: ${expectedProjectRoot} vs ${actualProjectRoot}`
|
|
1046
|
+
);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
return first;
|
|
1050
|
+
}
|
|
1051
|
+
function mergeDocuments(documents) {
|
|
1052
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
1053
|
+
for (const document of documents) {
|
|
1054
|
+
const existing = byPath.get(document.relativePath);
|
|
1055
|
+
if (!existing) {
|
|
1056
|
+
byPath.set(document.relativePath, document);
|
|
1057
|
+
continue;
|
|
1058
|
+
}
|
|
1059
|
+
byPath.set(document.relativePath, create(DocumentSchema, {
|
|
1060
|
+
language: existing.language || document.language,
|
|
1061
|
+
relativePath: existing.relativePath || document.relativePath,
|
|
1062
|
+
occurrences: [...existing.occurrences, ...document.occurrences],
|
|
1063
|
+
symbols: mergeSymbolInfos([...existing.symbols, ...document.symbols]),
|
|
1064
|
+
text: chooseText(existing.text, document.text),
|
|
1065
|
+
positionEncoding: existing.positionEncoding || document.positionEncoding
|
|
1066
|
+
}));
|
|
1067
|
+
}
|
|
1068
|
+
return [...byPath.values()];
|
|
1069
|
+
}
|
|
1070
|
+
function mergeSymbolInfos(symbols2) {
|
|
1071
|
+
const bySymbol = /* @__PURE__ */ new Map();
|
|
1072
|
+
for (const symbol of symbols2) {
|
|
1073
|
+
const existing = bySymbol.get(symbol.symbol);
|
|
1074
|
+
if (!existing) {
|
|
1075
|
+
bySymbol.set(symbol.symbol, symbol);
|
|
1076
|
+
continue;
|
|
1077
|
+
}
|
|
1078
|
+
bySymbol.set(symbol.symbol, create(SymbolInformationSchema, {
|
|
1079
|
+
symbol: existing.symbol,
|
|
1080
|
+
documentation: uniqueStrings([...existing.documentation, ...symbol.documentation]),
|
|
1081
|
+
relationships: mergeRelationships([...existing.relationships, ...symbol.relationships]),
|
|
1082
|
+
kind: existing.kind || symbol.kind,
|
|
1083
|
+
displayName: existing.displayName || symbol.displayName,
|
|
1084
|
+
enclosingSymbol: existing.enclosingSymbol || symbol.enclosingSymbol,
|
|
1085
|
+
signatureDocumentation: existing.signatureDocumentation ?? symbol.signatureDocumentation
|
|
1086
|
+
}));
|
|
1087
|
+
}
|
|
1088
|
+
return [...bySymbol.values()];
|
|
1089
|
+
}
|
|
1090
|
+
function mergeRelationships(relationships) {
|
|
1091
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1092
|
+
const merged = [];
|
|
1093
|
+
for (const relationship of relationships) {
|
|
1094
|
+
const key = [
|
|
1095
|
+
relationship.symbol,
|
|
1096
|
+
relationship.isReference ? "1" : "0",
|
|
1097
|
+
relationship.isImplementation ? "1" : "0",
|
|
1098
|
+
relationship.isTypeDefinition ? "1" : "0",
|
|
1099
|
+
relationship.isDefinition ? "1" : "0"
|
|
1100
|
+
].join("|");
|
|
1101
|
+
if (seen.has(key)) {
|
|
1102
|
+
continue;
|
|
1103
|
+
}
|
|
1104
|
+
seen.add(key);
|
|
1105
|
+
merged.push(relationship);
|
|
1106
|
+
}
|
|
1107
|
+
return merged;
|
|
1108
|
+
}
|
|
1109
|
+
function chooseText(left, right) {
|
|
1110
|
+
if (!left) return right;
|
|
1111
|
+
if (!right) return left;
|
|
1112
|
+
return left.length >= right.length ? left : right;
|
|
1113
|
+
}
|
|
1114
|
+
function uniqueStrings(values) {
|
|
1115
|
+
return [...new Set(values)];
|
|
1116
|
+
}
|
|
1117
|
+
|
|
971
1118
|
// src/reindex/index.ts
|
|
972
1119
|
async function reindex(opts) {
|
|
973
1120
|
const {
|
|
@@ -1003,7 +1150,11 @@ async function reindex(opts) {
|
|
|
1003
1150
|
...process.env,
|
|
1004
1151
|
NODE_OPTIONS: `--max-old-space-size=${maxHeapMb}`
|
|
1005
1152
|
};
|
|
1006
|
-
|
|
1153
|
+
const languageOutputs = languages.map((language, index) => ({
|
|
1154
|
+
language,
|
|
1155
|
+
scipPath: languages.length > 1 ? tempScipPath(outputScip, language, index) : outputScip
|
|
1156
|
+
}));
|
|
1157
|
+
for (const { language: lang, scipPath } of languageOutputs) {
|
|
1007
1158
|
const config = getIndexerConfig(lang);
|
|
1008
1159
|
const binaryLabel = describeIndexerBinary(config);
|
|
1009
1160
|
const projectLocalBinary = resolveProjectLocalIndexerBinary(config, projectRoot);
|
|
@@ -1033,7 +1184,7 @@ async function reindex(opts) {
|
|
|
1033
1184
|
const indexerEnv = getIndexerExecutionEnv(config, env, resolvedBinary);
|
|
1034
1185
|
const { binary, args } = config.indexArgs({
|
|
1035
1186
|
projectRoot,
|
|
1036
|
-
outputPath:
|
|
1187
|
+
outputPath: scipPath,
|
|
1037
1188
|
pnpmWorkspaces: opts.pnpmWorkspaces,
|
|
1038
1189
|
indexerBinary: resolvedBinary
|
|
1039
1190
|
});
|
|
@@ -1048,10 +1199,15 @@ async function reindex(opts) {
|
|
|
1048
1199
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1049
1200
|
throw new Error(
|
|
1050
1201
|
`Failed to index ${lang} with ${resolvedBinary}: ${msg}
|
|
1051
|
-
Make sure ${binaryLabel} is installed and available on PATH
|
|
1202
|
+
Make sure ${binaryLabel} is installed and available on PATH.`,
|
|
1203
|
+
{ cause: err }
|
|
1052
1204
|
);
|
|
1053
1205
|
}
|
|
1054
|
-
moveDefaultOutputIfNeeded(config, projectRoot,
|
|
1206
|
+
moveDefaultOutputIfNeeded(config, projectRoot, scipPath);
|
|
1207
|
+
}
|
|
1208
|
+
if (languageOutputs.length > 1) {
|
|
1209
|
+
onStatus(`Merging ${languageOutputs.length} language indexes...`);
|
|
1210
|
+
mergeScipFiles(languageOutputs.map((entry) => entry.scipPath), outputScip);
|
|
1055
1211
|
}
|
|
1056
1212
|
onStatus("Converting to SQLite...");
|
|
1057
1213
|
if (!existsSync6(outputScip)) {
|
|
@@ -1065,7 +1221,13 @@ Make sure ${binaryLabel} is installed and available on PATH.`
|
|
|
1065
1221
|
});
|
|
1066
1222
|
} catch (err) {
|
|
1067
1223
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1068
|
-
throw new Error(`Failed to convert SCIP index to SQLite: ${msg}
|
|
1224
|
+
throw new Error(`Failed to convert SCIP index to SQLite: ${msg}`, { cause: err });
|
|
1225
|
+
} finally {
|
|
1226
|
+
for (const { scipPath } of languageOutputs) {
|
|
1227
|
+
if (scipPath !== outputScip) {
|
|
1228
|
+
rmSync(scipPath, { force: true });
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1069
1231
|
}
|
|
1070
1232
|
const durationMs = Date.now() - start;
|
|
1071
1233
|
onStatus(`Done in ${(durationMs / 1e3).toFixed(1)}s`);
|
|
@@ -1080,11 +1242,16 @@ function moveDefaultOutputIfNeeded(config, projectRoot, outputScip) {
|
|
|
1080
1242
|
renameSync(defaultOutputPath, outputScip);
|
|
1081
1243
|
}
|
|
1082
1244
|
}
|
|
1245
|
+
function tempScipPath(outputScip, language, index) {
|
|
1246
|
+
const extension = extname2(outputScip) || ".scip";
|
|
1247
|
+
const stem = basename(outputScip, extension);
|
|
1248
|
+
return join6(dirname2(outputScip), `${stem}.${index + 1}.${language}${extension}`);
|
|
1249
|
+
}
|
|
1083
1250
|
|
|
1084
1251
|
// src/watch.ts
|
|
1085
1252
|
import { watch } from "fs";
|
|
1086
1253
|
import { existsSync as existsSync7, renameSync as renameSync2 } from "fs";
|
|
1087
|
-
import { join as join7, relative } from "path";
|
|
1254
|
+
import { join as join7, relative as relative2 } from "path";
|
|
1088
1255
|
import { fork } from "child_process";
|
|
1089
1256
|
import ignore2 from "ignore";
|
|
1090
1257
|
var Watcher = class {
|
|
@@ -1162,7 +1329,7 @@ var Watcher = class {
|
|
|
1162
1329
|
}
|
|
1163
1330
|
// ── Internal ─────────────────────────────────────────────
|
|
1164
1331
|
handleFileChange(filename) {
|
|
1165
|
-
const rel =
|
|
1332
|
+
const rel = relative2(this.projectRoot, join7(this.projectRoot, filename));
|
|
1166
1333
|
if (this.gitignoreFilter.isIgnored(rel)) return;
|
|
1167
1334
|
if (this.extraIgnore.ignores(rel)) return;
|
|
1168
1335
|
if (filename.endsWith("index.db") || filename.endsWith("index.scip") || filename.endsWith("index.db.tmp") || filename.endsWith(".scipquery.json")) {
|
|
@@ -1243,10 +1410,10 @@ var Watcher = class {
|
|
|
1243
1410
|
* Writes to index.db.tmp, then atomically renames to index.db.
|
|
1244
1411
|
*/
|
|
1245
1412
|
runReindex() {
|
|
1246
|
-
return new Promise((
|
|
1413
|
+
return new Promise((resolve5, reject) => {
|
|
1247
1414
|
const start = Date.now();
|
|
1248
1415
|
const tmpDb = this.indexPaths.dbPath + ".tmp";
|
|
1249
|
-
const tmpScip =
|
|
1416
|
+
const tmpScip = tempScipPath2(this.indexPaths.indexPath);
|
|
1250
1417
|
const child = fork(
|
|
1251
1418
|
new URL("./reindex-worker.js", import.meta.url).pathname,
|
|
1252
1419
|
[],
|
|
@@ -1271,7 +1438,7 @@ var Watcher = class {
|
|
|
1271
1438
|
if (existsSync7(tmpScip)) {
|
|
1272
1439
|
renameSync2(tmpScip, this.indexPaths.indexPath);
|
|
1273
1440
|
}
|
|
1274
|
-
|
|
1441
|
+
resolve5(Date.now() - start);
|
|
1275
1442
|
} catch (err) {
|
|
1276
1443
|
reject(new Error(`Atomic swap failed: ${err}`));
|
|
1277
1444
|
}
|
|
@@ -1287,7 +1454,7 @@ var Watcher = class {
|
|
|
1287
1454
|
this.onStatus(status);
|
|
1288
1455
|
}
|
|
1289
1456
|
};
|
|
1290
|
-
function
|
|
1457
|
+
function tempScipPath2(indexPath) {
|
|
1291
1458
|
return indexPath.endsWith(".scip") ? indexPath.slice(0, -".scip".length) + ".tmp.scip" : indexPath + ".tmp.scip";
|
|
1292
1459
|
}
|
|
1293
1460
|
|
|
@@ -1323,7 +1490,7 @@ function files(db, pattern) {
|
|
|
1323
1490
|
}
|
|
1324
1491
|
|
|
1325
1492
|
// src/query-support.ts
|
|
1326
|
-
import { basename as
|
|
1493
|
+
import { basename as basename3 } from "path";
|
|
1327
1494
|
|
|
1328
1495
|
// src/source-analysis.ts
|
|
1329
1496
|
import {
|
|
@@ -1331,12 +1498,12 @@ import {
|
|
|
1331
1498
|
readFileSync as readFileSync4
|
|
1332
1499
|
} from "fs";
|
|
1333
1500
|
import {
|
|
1334
|
-
basename,
|
|
1335
|
-
dirname as
|
|
1336
|
-
extname as
|
|
1501
|
+
basename as basename2,
|
|
1502
|
+
dirname as dirname3,
|
|
1503
|
+
extname as extname3,
|
|
1337
1504
|
join as join8,
|
|
1338
|
-
relative as
|
|
1339
|
-
resolve as
|
|
1505
|
+
relative as relative3,
|
|
1506
|
+
resolve as resolve3
|
|
1340
1507
|
} from "path";
|
|
1341
1508
|
var SOURCE_IMPORT_CACHE = /* @__PURE__ */ new WeakMap();
|
|
1342
1509
|
var SOURCE_EXPORT_CACHE = /* @__PURE__ */ new WeakMap();
|
|
@@ -1679,7 +1846,7 @@ function parseRubyImports(db, importerPath, source) {
|
|
|
1679
1846
|
return statements;
|
|
1680
1847
|
}
|
|
1681
1848
|
function rubyConstantName(specifier) {
|
|
1682
|
-
return
|
|
1849
|
+
return basename2(specifier).replace(/\.[^.]+$/, "").split("_").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
1683
1850
|
}
|
|
1684
1851
|
function parseCLikeImports(db, importerPath, source) {
|
|
1685
1852
|
const statements = [];
|
|
@@ -1688,7 +1855,7 @@ function parseCLikeImports(db, importerPath, source) {
|
|
|
1688
1855
|
const full = match[0];
|
|
1689
1856
|
if (!specifier || !full || typeof match.index !== "number") continue;
|
|
1690
1857
|
const body = buildUsageBody(source, match.index, match.index + full.length);
|
|
1691
|
-
const localName =
|
|
1858
|
+
const localName = basename2(specifier).replace(/\.[^.]+$/, "");
|
|
1692
1859
|
statements.push({
|
|
1693
1860
|
importedName: specifier,
|
|
1694
1861
|
localName,
|
|
@@ -2163,16 +2330,16 @@ function resolveJavaScriptImportPath(db, importerPath, specifier) {
|
|
|
2163
2330
|
if (!specifier.startsWith(".") && !specifier.startsWith("/")) {
|
|
2164
2331
|
return null;
|
|
2165
2332
|
}
|
|
2166
|
-
const importerDir =
|
|
2167
|
-
const absolute =
|
|
2333
|
+
const importerDir = dirname3(join8(db.config.projectRoot, importerPath));
|
|
2334
|
+
const absolute = resolve3(importerDir, specifier);
|
|
2168
2335
|
const indexedPaths = getIndexedPaths(db);
|
|
2169
2336
|
for (const candidate of candidateImportPaths(absolute)) {
|
|
2170
|
-
const relativeCandidate = normalizePath(
|
|
2337
|
+
const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
|
|
2171
2338
|
if (indexedPaths.has(relativeCandidate) || existsSync8(candidate)) {
|
|
2172
2339
|
return relativeCandidate;
|
|
2173
2340
|
}
|
|
2174
2341
|
}
|
|
2175
|
-
return normalizePath(
|
|
2342
|
+
return normalizePath(relative3(db.config.projectRoot, absolute));
|
|
2176
2343
|
}
|
|
2177
2344
|
function resolvePythonImportPath(db, importerPath, specifier) {
|
|
2178
2345
|
const indexedPaths = getIndexedPaths(db);
|
|
@@ -2182,16 +2349,16 @@ function resolvePythonImportPath(db, importerPath, specifier) {
|
|
|
2182
2349
|
if (!match) return null;
|
|
2183
2350
|
const dots = match[1].length;
|
|
2184
2351
|
const remainder = match[2].replace(/^\./, "");
|
|
2185
|
-
let baseDir =
|
|
2352
|
+
let baseDir = dirname3(join8(db.config.projectRoot, importerPath));
|
|
2186
2353
|
for (let i = 1; i < dots; i++) {
|
|
2187
|
-
baseDir =
|
|
2354
|
+
baseDir = dirname3(baseDir);
|
|
2188
2355
|
}
|
|
2189
|
-
basePath = remainder ?
|
|
2356
|
+
basePath = remainder ? resolve3(baseDir, remainder.replace(/\./g, "/")) : baseDir;
|
|
2190
2357
|
} else {
|
|
2191
|
-
basePath =
|
|
2358
|
+
basePath = resolve3(db.config.projectRoot, specifier.replace(/\./g, "/"));
|
|
2192
2359
|
}
|
|
2193
2360
|
for (const candidate of pythonCandidateImportPaths(basePath)) {
|
|
2194
|
-
const relativeCandidate = normalizePath(
|
|
2361
|
+
const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
|
|
2195
2362
|
if (indexedPaths.has(relativeCandidate) || existsSync8(candidate)) {
|
|
2196
2363
|
return relativeCandidate;
|
|
2197
2364
|
}
|
|
@@ -2204,17 +2371,17 @@ function resolveRustImportPath(db, importerPath, specifier) {
|
|
|
2204
2371
|
if (!normalizedSpecifier.startsWith("crate::") && !normalizedSpecifier.startsWith("self::") && !normalizedSpecifier.startsWith("super::")) {
|
|
2205
2372
|
return null;
|
|
2206
2373
|
}
|
|
2207
|
-
const importerDir =
|
|
2374
|
+
const importerDir = dirname3(join8(db.config.projectRoot, importerPath));
|
|
2208
2375
|
let basePath;
|
|
2209
2376
|
if (normalizedSpecifier.startsWith("crate::")) {
|
|
2210
|
-
basePath =
|
|
2377
|
+
basePath = resolve3(db.config.projectRoot, "src", normalizedSpecifier.slice("crate::".length).replace(/::/g, "/"));
|
|
2211
2378
|
} else if (normalizedSpecifier.startsWith("self::")) {
|
|
2212
|
-
basePath =
|
|
2379
|
+
basePath = resolve3(importerDir, normalizedSpecifier.slice("self::".length).replace(/::/g, "/"));
|
|
2213
2380
|
} else {
|
|
2214
|
-
basePath =
|
|
2381
|
+
basePath = resolve3(dirname3(importerDir), normalizedSpecifier.slice("super::".length).replace(/::/g, "/"));
|
|
2215
2382
|
}
|
|
2216
2383
|
for (const candidate of rustCandidateImportPaths(basePath)) {
|
|
2217
|
-
const relativeCandidate = normalizePath(
|
|
2384
|
+
const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
|
|
2218
2385
|
if (getIndexedPaths(db).has(relativeCandidate) || existsSync8(candidate)) {
|
|
2219
2386
|
return relativeCandidate;
|
|
2220
2387
|
}
|
|
@@ -2222,10 +2389,10 @@ function resolveRustImportPath(db, importerPath, specifier) {
|
|
|
2222
2389
|
return null;
|
|
2223
2390
|
}
|
|
2224
2391
|
function resolveRubyImportPath(db, importerPath, specifier) {
|
|
2225
|
-
const importerDir =
|
|
2226
|
-
const absolute =
|
|
2392
|
+
const importerDir = dirname3(join8(db.config.projectRoot, importerPath));
|
|
2393
|
+
const absolute = resolve3(importerDir, specifier);
|
|
2227
2394
|
for (const candidate of rubyCandidateImportPaths(absolute)) {
|
|
2228
|
-
const relativeCandidate = normalizePath(
|
|
2395
|
+
const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
|
|
2229
2396
|
if (getIndexedPaths(db).has(relativeCandidate) || existsSync8(candidate)) {
|
|
2230
2397
|
return relativeCandidate;
|
|
2231
2398
|
}
|
|
@@ -2234,15 +2401,15 @@ function resolveRubyImportPath(db, importerPath, specifier) {
|
|
|
2234
2401
|
}
|
|
2235
2402
|
function resolveCLikeImportPath(db, importerPath, specifier) {
|
|
2236
2403
|
const indexedPaths = getIndexedPaths(db);
|
|
2237
|
-
const importerDir =
|
|
2404
|
+
const importerDir = dirname3(join8(db.config.projectRoot, importerPath));
|
|
2238
2405
|
const candidates = [
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2406
|
+
resolve3(importerDir, specifier),
|
|
2407
|
+
resolve3(db.config.projectRoot, specifier),
|
|
2408
|
+
resolve3(db.config.projectRoot, "include", specifier),
|
|
2409
|
+
resolve3(db.config.projectRoot, "src", specifier)
|
|
2243
2410
|
];
|
|
2244
2411
|
for (const candidate of candidates) {
|
|
2245
|
-
const relativeCandidate = normalizePath(
|
|
2412
|
+
const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
|
|
2246
2413
|
if (indexedPaths.has(relativeCandidate) || existsSync8(candidate)) {
|
|
2247
2414
|
return relativeCandidate;
|
|
2248
2415
|
}
|
|
@@ -2260,10 +2427,10 @@ function resolveQualifiedImportPath(db, specifier, extensions) {
|
|
|
2260
2427
|
if (exact) return exact;
|
|
2261
2428
|
}
|
|
2262
2429
|
for (const ext of extensions) {
|
|
2263
|
-
const basenameMatch = [...indexedPaths].find((relativePath) =>
|
|
2430
|
+
const basenameMatch = [...indexedPaths].find((relativePath) => basename2(relativePath) === `${basenameOnly}${ext}`);
|
|
2264
2431
|
if (basenameMatch) return basenameMatch;
|
|
2265
2432
|
}
|
|
2266
|
-
const folderMatches = [...indexedPaths].filter((relativePath) => extensions.includes(
|
|
2433
|
+
const folderMatches = [...indexedPaths].filter((relativePath) => extensions.includes(extname3(relativePath).toLowerCase())).filter((relativePath) => relativePath.includes(`/${pathified}/`) || relativePath.includes(`/${basenameOnly}/`)).sort((left, right) => left.localeCompare(right));
|
|
2267
2434
|
if (folderMatches.length === 1) {
|
|
2268
2435
|
return folderMatches[0];
|
|
2269
2436
|
}
|
|
@@ -2280,10 +2447,10 @@ function resolveDartImportPath(db, importerPath, specifier) {
|
|
|
2280
2447
|
if (indexedPaths.has(candidate)) return candidate;
|
|
2281
2448
|
return null;
|
|
2282
2449
|
}
|
|
2283
|
-
const importerDir =
|
|
2284
|
-
const absolute =
|
|
2450
|
+
const importerDir = dirname3(join8(db.config.projectRoot, importerPath));
|
|
2451
|
+
const absolute = resolve3(importerDir, specifier);
|
|
2285
2452
|
for (const candidate of dartCandidateImportPaths(absolute)) {
|
|
2286
|
-
const relativeCandidate = normalizePath(
|
|
2453
|
+
const relativeCandidate = normalizePath(relative3(db.config.projectRoot, candidate));
|
|
2287
2454
|
if (indexedPaths.has(relativeCandidate) || existsSync8(candidate)) {
|
|
2288
2455
|
return relativeCandidate;
|
|
2289
2456
|
}
|
|
@@ -2291,7 +2458,7 @@ function resolveDartImportPath(db, importerPath, specifier) {
|
|
|
2291
2458
|
return null;
|
|
2292
2459
|
}
|
|
2293
2460
|
function pythonCandidateImportPaths(basePath) {
|
|
2294
|
-
const ext =
|
|
2461
|
+
const ext = extname3(basePath);
|
|
2295
2462
|
if (PYTHON_SOURCE_EXTENSIONS.includes(ext)) {
|
|
2296
2463
|
return [basePath];
|
|
2297
2464
|
}
|
|
@@ -2303,7 +2470,7 @@ function pythonCandidateImportPaths(basePath) {
|
|
|
2303
2470
|
];
|
|
2304
2471
|
}
|
|
2305
2472
|
function rustCandidateImportPaths(basePath) {
|
|
2306
|
-
const ext =
|
|
2473
|
+
const ext = extname3(basePath);
|
|
2307
2474
|
if (RUST_SOURCE_EXTENSIONS.includes(ext)) {
|
|
2308
2475
|
return [basePath];
|
|
2309
2476
|
}
|
|
@@ -2313,7 +2480,7 @@ function rustCandidateImportPaths(basePath) {
|
|
|
2313
2480
|
];
|
|
2314
2481
|
}
|
|
2315
2482
|
function rubyCandidateImportPaths(basePath) {
|
|
2316
|
-
const ext =
|
|
2483
|
+
const ext = extname3(basePath);
|
|
2317
2484
|
if (RUBY_SOURCE_EXTENSIONS.includes(ext)) {
|
|
2318
2485
|
return [basePath];
|
|
2319
2486
|
}
|
|
@@ -2323,14 +2490,14 @@ function rubyCandidateImportPaths(basePath) {
|
|
|
2323
2490
|
];
|
|
2324
2491
|
}
|
|
2325
2492
|
function dartCandidateImportPaths(basePath) {
|
|
2326
|
-
const ext =
|
|
2493
|
+
const ext = extname3(basePath);
|
|
2327
2494
|
if (DART_SOURCE_EXTENSIONS.includes(ext)) {
|
|
2328
2495
|
return [basePath];
|
|
2329
2496
|
}
|
|
2330
2497
|
return [`${basePath}.dart`, basePath];
|
|
2331
2498
|
}
|
|
2332
2499
|
function candidateImportPaths(absolute) {
|
|
2333
|
-
const ext =
|
|
2500
|
+
const ext = extname3(absolute);
|
|
2334
2501
|
const candidates = /* @__PURE__ */ new Set();
|
|
2335
2502
|
if (ext) {
|
|
2336
2503
|
candidates.add(absolute);
|
|
@@ -2373,34 +2540,34 @@ function normalizePath(path2) {
|
|
|
2373
2540
|
return path2.replace(/\\/g, "/");
|
|
2374
2541
|
}
|
|
2375
2542
|
function isJavaScriptSourcePath(relativePath) {
|
|
2376
|
-
return SOURCE_EXTENSIONS.includes(
|
|
2543
|
+
return SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
|
|
2377
2544
|
}
|
|
2378
2545
|
function isPythonSourcePath(relativePath) {
|
|
2379
|
-
return PYTHON_SOURCE_EXTENSIONS.includes(
|
|
2546
|
+
return PYTHON_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
|
|
2380
2547
|
}
|
|
2381
2548
|
function isJvmSourcePath(relativePath) {
|
|
2382
|
-
return JVM_SOURCE_EXTENSIONS.includes(
|
|
2549
|
+
return JVM_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
|
|
2383
2550
|
}
|
|
2384
2551
|
function isRustSourcePath(relativePath) {
|
|
2385
|
-
return RUST_SOURCE_EXTENSIONS.includes(
|
|
2552
|
+
return RUST_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
|
|
2386
2553
|
}
|
|
2387
2554
|
function isRubySourcePath(relativePath) {
|
|
2388
|
-
return RUBY_SOURCE_EXTENSIONS.includes(
|
|
2555
|
+
return RUBY_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
|
|
2389
2556
|
}
|
|
2390
2557
|
function isCLikeSourcePath(relativePath) {
|
|
2391
|
-
return C_LIKE_SOURCE_EXTENSIONS.includes(
|
|
2558
|
+
return C_LIKE_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
|
|
2392
2559
|
}
|
|
2393
2560
|
function isDotNetSourcePath(relativePath) {
|
|
2394
|
-
return DOTNET_SOURCE_EXTENSIONS.includes(
|
|
2561
|
+
return DOTNET_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
|
|
2395
2562
|
}
|
|
2396
2563
|
function isVisualBasicSourcePath(relativePath) {
|
|
2397
|
-
return
|
|
2564
|
+
return extname3(relativePath).toLowerCase() === ".vb";
|
|
2398
2565
|
}
|
|
2399
2566
|
function isDartSourcePath(relativePath) {
|
|
2400
|
-
return DART_SOURCE_EXTENSIONS.includes(
|
|
2567
|
+
return DART_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
|
|
2401
2568
|
}
|
|
2402
2569
|
function isPhpSourcePath(relativePath) {
|
|
2403
|
-
return PHP_SOURCE_EXTENSIONS.includes(
|
|
2570
|
+
return PHP_SOURCE_EXTENSIONS.includes(extname3(relativePath).toLowerCase());
|
|
2404
2571
|
}
|
|
2405
2572
|
function extensionFamilyFor(relativePath) {
|
|
2406
2573
|
if (isJvmSourcePath(relativePath)) return JVM_SOURCE_EXTENSIONS;
|
|
@@ -2526,7 +2693,6 @@ function parseDescriptors(input) {
|
|
|
2526
2693
|
const closingTick = input.indexOf("`", i + 1);
|
|
2527
2694
|
if (closingTick === -1) {
|
|
2528
2695
|
name = input.slice(i + 1);
|
|
2529
|
-
i = input.length;
|
|
2530
2696
|
descriptors.push({ name, suffix: "term" });
|
|
2531
2697
|
break;
|
|
2532
2698
|
}
|
|
@@ -2707,6 +2873,10 @@ function buildFileDepGraph(db, scope) {
|
|
|
2707
2873
|
return graph;
|
|
2708
2874
|
}
|
|
2709
2875
|
function findFirstSymbolMatch(db, symbolPattern) {
|
|
2876
|
+
const exact = findExactSymbolMatch(db, symbolPattern.trim());
|
|
2877
|
+
if (exact) {
|
|
2878
|
+
return exact;
|
|
2879
|
+
}
|
|
2710
2880
|
const fileLineMatch = symbolPattern.match(/^(.+):(\d+)-(\d+)$/);
|
|
2711
2881
|
if (fileLineMatch) {
|
|
2712
2882
|
const [, filePath, startStr, endStr] = fileLineMatch;
|
|
@@ -2744,19 +2914,16 @@ function findFirstSymbolMatch(db, symbolPattern) {
|
|
|
2744
2914
|
);
|
|
2745
2915
|
}
|
|
2746
2916
|
if (row && !db.isIgnored(row.relative_path)) {
|
|
2747
|
-
return
|
|
2748
|
-
symbolId: row.id,
|
|
2749
|
-
symbol: row.symbol,
|
|
2750
|
-
documentId: row.document_id,
|
|
2751
|
-
startLine: row.start_line,
|
|
2752
|
-
endLine: row.end_line,
|
|
2753
|
-
relativePath: row.relative_path
|
|
2754
|
-
};
|
|
2917
|
+
return hydrateSymbolMatch(db, row);
|
|
2755
2918
|
}
|
|
2756
2919
|
}
|
|
2757
2920
|
const cleaned = normalizeLookupPattern(symbolPattern);
|
|
2758
2921
|
const tokens = lookupTokens(symbolPattern);
|
|
2759
2922
|
const candidates = getSymbolLookupCandidates(db, tokens);
|
|
2923
|
+
const direct = findDirectSymbolCandidate(candidates, symbolPattern, cleaned);
|
|
2924
|
+
if (direct && !db.isIgnored(direct.relative_path)) {
|
|
2925
|
+
return hydrateSymbolMatch(db, direct);
|
|
2926
|
+
}
|
|
2760
2927
|
let best = null;
|
|
2761
2928
|
for (const row of candidates) {
|
|
2762
2929
|
if (db.isIgnored(row.relative_path)) continue;
|
|
@@ -2767,14 +2934,7 @@ function findFirstSymbolMatch(db, symbolPattern) {
|
|
|
2767
2934
|
}
|
|
2768
2935
|
}
|
|
2769
2936
|
if (best) {
|
|
2770
|
-
return
|
|
2771
|
-
symbolId: best.row.id,
|
|
2772
|
-
symbol: best.row.symbol,
|
|
2773
|
-
documentId: best.row.document_id,
|
|
2774
|
-
startLine: best.row.start_line,
|
|
2775
|
-
endLine: best.row.end_line,
|
|
2776
|
-
relativePath: best.row.relative_path
|
|
2777
|
-
};
|
|
2937
|
+
return hydrateSymbolMatch(db, best.row);
|
|
2778
2938
|
}
|
|
2779
2939
|
return null;
|
|
2780
2940
|
}
|
|
@@ -2793,14 +2953,7 @@ function findExactSymbolMatch(db, symbol) {
|
|
|
2793
2953
|
if (!row || db.isIgnored(row.relative_path)) {
|
|
2794
2954
|
return null;
|
|
2795
2955
|
}
|
|
2796
|
-
return
|
|
2797
|
-
symbolId: row.id,
|
|
2798
|
-
symbol: row.symbol,
|
|
2799
|
-
documentId: row.document_id,
|
|
2800
|
-
startLine: row.start_line,
|
|
2801
|
-
endLine: row.end_line,
|
|
2802
|
-
relativePath: row.relative_path
|
|
2803
|
-
};
|
|
2956
|
+
return hydrateSymbolMatch(db, row);
|
|
2804
2957
|
}
|
|
2805
2958
|
function resolveIndexedFile(db, filePattern) {
|
|
2806
2959
|
return resolveDocumentCandidates(db, filePattern, { allowMultiple: false })[0]?.relativePath ?? null;
|
|
@@ -2941,27 +3094,10 @@ function getCalleeRowsForSymbol(db, symbol, opts = {}) {
|
|
|
2941
3094
|
});
|
|
2942
3095
|
}
|
|
2943
3096
|
const sourceFallback = getSourceBackedCalleeRows(db, symbol, opts.limit);
|
|
2944
|
-
if (sourceFallback.length
|
|
2945
|
-
return primary;
|
|
2946
|
-
}
|
|
2947
|
-
if (primary.length === 0) {
|
|
3097
|
+
if (sourceFallback.length > 0) {
|
|
2948
3098
|
return applyLimit(sourceFallback, opts.limit);
|
|
2949
3099
|
}
|
|
2950
|
-
|
|
2951
|
-
const seen = new Set(merged.map((row) => `${row.symbol}|${row.file}`));
|
|
2952
|
-
const preferredFallbackLeaves = new Set(
|
|
2953
|
-
sourceFallback.filter((row) => !isLikelyTestPath(row.file)).map((row) => leafName(row.symbol))
|
|
2954
|
-
);
|
|
2955
|
-
for (const row of primary) {
|
|
2956
|
-
if (isLikelyTestPath(row.file) && preferredFallbackLeaves.has(leafName(row.symbol))) {
|
|
2957
|
-
continue;
|
|
2958
|
-
}
|
|
2959
|
-
const key = `${row.symbol}|${row.file}`;
|
|
2960
|
-
if (seen.has(key)) continue;
|
|
2961
|
-
seen.add(key);
|
|
2962
|
-
merged.push(row);
|
|
2963
|
-
}
|
|
2964
|
-
return applyLimit(merged, opts.limit);
|
|
3100
|
+
return primary;
|
|
2965
3101
|
}
|
|
2966
3102
|
function getCallerRowsForSymbol(db, symbol, opts = {}) {
|
|
2967
3103
|
const match = getFullSymbolMatch(db, symbol);
|
|
@@ -2994,11 +3130,13 @@ function getCallerRowsForSymbol(db, symbol, opts = {}) {
|
|
|
2994
3130
|
...getGenericSourceCallerRows(db, match, opts.limit)
|
|
2995
3131
|
]);
|
|
2996
3132
|
if (sourceFallback.length === 0) {
|
|
2997
|
-
return primary;
|
|
3133
|
+
return dedupeCallerRows(primary);
|
|
2998
3134
|
}
|
|
2999
3135
|
const merged = [...sourceFallback];
|
|
3136
|
+
const fallbackFiles = new Set(sourceFallback.map((row) => row.file));
|
|
3000
3137
|
const seen = new Set(merged.map((row) => `${row.symbol}|${row.file}`));
|
|
3001
3138
|
for (const row of primary) {
|
|
3139
|
+
if (fallbackFiles.has(row.file)) continue;
|
|
3002
3140
|
const key = `${row.symbol}|${row.file}`;
|
|
3003
3141
|
if (seen.has(key)) continue;
|
|
3004
3142
|
if (isFunctionLikeSymbol(row.symbol) || merged.length === 0) {
|
|
@@ -3379,14 +3517,7 @@ function getFullSymbolMatch(db, symbol) {
|
|
|
3379
3517
|
if (!row) {
|
|
3380
3518
|
return null;
|
|
3381
3519
|
}
|
|
3382
|
-
return
|
|
3383
|
-
symbolId: row.id,
|
|
3384
|
-
documentId: row.document_id,
|
|
3385
|
-
startLine: row.start_line,
|
|
3386
|
-
endLine: row.end_line,
|
|
3387
|
-
symbol: row.symbol,
|
|
3388
|
-
relativePath: row.relative_path
|
|
3389
|
-
};
|
|
3520
|
+
return hydrateSymbolMatch(db, row);
|
|
3390
3521
|
}
|
|
3391
3522
|
function getDefinitionsForFile(db, relativePath) {
|
|
3392
3523
|
let cache = FILE_DEFINITION_CACHE.get(db);
|
|
@@ -3406,6 +3537,7 @@ function getDefinitionsForFile(db, relativePath) {
|
|
|
3406
3537
|
der.start_line,
|
|
3407
3538
|
der.end_line,
|
|
3408
3539
|
d.relative_path,
|
|
3540
|
+
gs.display_name,
|
|
3409
3541
|
gs.kind,
|
|
3410
3542
|
gs.documentation,
|
|
3411
3543
|
gs.enclosing_symbol
|
|
@@ -3425,6 +3557,7 @@ function getDefinitionsForFile(db, relativePath) {
|
|
|
3425
3557
|
MIN(c.start_line) AS start_line,
|
|
3426
3558
|
MAX(c.end_line) AS end_line,
|
|
3427
3559
|
d.relative_path,
|
|
3560
|
+
gs.display_name,
|
|
3428
3561
|
gs.kind,
|
|
3429
3562
|
gs.documentation,
|
|
3430
3563
|
gs.enclosing_symbol
|
|
@@ -3439,23 +3572,222 @@ function getDefinitionsForFile(db, relativePath) {
|
|
|
3439
3572
|
ORDER BY start_line, end_line`,
|
|
3440
3573
|
relativePath
|
|
3441
3574
|
);
|
|
3442
|
-
const definitions = (
|
|
3575
|
+
const definitions = correctDefinitionRangesFromSource(
|
|
3576
|
+
db,
|
|
3577
|
+
relativePath,
|
|
3578
|
+
(primary.length > 0 ? primary : fallback).map((row) => ({
|
|
3579
|
+
symbolId: row.id,
|
|
3580
|
+
symbol: row.symbol,
|
|
3581
|
+
documentId: row.document_id,
|
|
3582
|
+
startLine: row.start_line,
|
|
3583
|
+
endLine: row.end_line,
|
|
3584
|
+
relativePath: row.relative_path,
|
|
3585
|
+
leaf: leafName(row.symbol),
|
|
3586
|
+
parentTypeName: parentTypeName(row.symbol),
|
|
3587
|
+
isFunctionLike: isFunctionLikeSymbol(row.symbol),
|
|
3588
|
+
isTypeLike: leafSuffix(row.symbol) === "type",
|
|
3589
|
+
kind: row.kind ?? null,
|
|
3590
|
+
documentation: row.documentation ?? null,
|
|
3591
|
+
enclosingSymbol: row.enclosing_symbol ?? null
|
|
3592
|
+
}))
|
|
3593
|
+
);
|
|
3594
|
+
cache.set(relativePath, definitions);
|
|
3595
|
+
return definitions;
|
|
3596
|
+
}
|
|
3597
|
+
function hydrateSymbolMatch(db, row) {
|
|
3598
|
+
const corrected = getDefinitionsForFile(db, row.relative_path).find((definition) => definition.symbolId === row.id);
|
|
3599
|
+
if (corrected) {
|
|
3600
|
+
return {
|
|
3601
|
+
symbolId: corrected.symbolId,
|
|
3602
|
+
symbol: corrected.symbol,
|
|
3603
|
+
documentId: corrected.documentId,
|
|
3604
|
+
startLine: corrected.startLine,
|
|
3605
|
+
endLine: corrected.endLine,
|
|
3606
|
+
relativePath: corrected.relativePath
|
|
3607
|
+
};
|
|
3608
|
+
}
|
|
3609
|
+
return {
|
|
3443
3610
|
symbolId: row.id,
|
|
3444
3611
|
symbol: row.symbol,
|
|
3445
3612
|
documentId: row.document_id,
|
|
3446
3613
|
startLine: row.start_line,
|
|
3447
3614
|
endLine: row.end_line,
|
|
3448
|
-
relativePath: row.relative_path
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3615
|
+
relativePath: row.relative_path
|
|
3616
|
+
};
|
|
3617
|
+
}
|
|
3618
|
+
function findDirectSymbolCandidate(candidates, symbolPattern, cleanedPattern) {
|
|
3619
|
+
const trimmed = symbolPattern.trim();
|
|
3620
|
+
const directMatches = candidates.filter((row) => {
|
|
3621
|
+
const short = shortenSymbol(row.symbol);
|
|
3622
|
+
const display = (row.display_name ?? "").trim();
|
|
3623
|
+
return row.symbol === trimmed || short === trimmed || short === cleanedPattern || display === trimmed || display === cleanedPattern || `${display}()` === trimmed || row.relative_path === trimmed;
|
|
3624
|
+
});
|
|
3625
|
+
if (directMatches.length === 0) {
|
|
3626
|
+
return null;
|
|
3627
|
+
}
|
|
3628
|
+
directMatches.sort(
|
|
3629
|
+
(left, right) => left.end_line - left.start_line - (right.end_line - right.start_line) || left.relative_path.localeCompare(right.relative_path) || left.symbol.localeCompare(right.symbol)
|
|
3630
|
+
);
|
|
3631
|
+
return directMatches[0] ?? null;
|
|
3632
|
+
}
|
|
3633
|
+
function correctDefinitionRangesFromSource(db, relativePath, definitions) {
|
|
3634
|
+
const source = getSourceText(db, relativePath);
|
|
3635
|
+
if (!source) {
|
|
3636
|
+
return definitions;
|
|
3637
|
+
}
|
|
3638
|
+
const lines = source.split(/\r?\n/);
|
|
3639
|
+
const correctedStarts = /* @__PURE__ */ new Map();
|
|
3640
|
+
for (const definition of definitions) {
|
|
3641
|
+
correctedStarts.set(
|
|
3642
|
+
definition.symbolId,
|
|
3643
|
+
resolveCallableDefinitionStartLine(lines, definition)
|
|
3644
|
+
);
|
|
3645
|
+
}
|
|
3646
|
+
const correctedRanges = /* @__PURE__ */ new Map();
|
|
3647
|
+
const callableDefinitions = definitions.filter((definition) => isCallableDefinition(definition.symbol)).map((definition) => ({
|
|
3648
|
+
definition,
|
|
3649
|
+
startLine: correctedStarts.get(definition.symbolId) ?? definition.startLine
|
|
3650
|
+
})).sort(
|
|
3651
|
+
(left, right) => left.startLine - right.startLine || left.definition.startLine - right.definition.startLine || left.definition.symbol.localeCompare(right.definition.symbol)
|
|
3652
|
+
);
|
|
3653
|
+
for (let index = 0; index < callableDefinitions.length; index += 1) {
|
|
3654
|
+
const current = callableDefinitions[index];
|
|
3655
|
+
const next = callableDefinitions[index + 1];
|
|
3656
|
+
const maxEndLine = next ? Math.max(current.startLine, next.startLine - 1) : lines.length - 1;
|
|
3657
|
+
correctedRanges.set(current.definition.symbolId, {
|
|
3658
|
+
startLine: current.startLine,
|
|
3659
|
+
endLine: resolveCallableDefinitionEndLine(
|
|
3660
|
+
lines,
|
|
3661
|
+
current.definition,
|
|
3662
|
+
current.startLine,
|
|
3663
|
+
maxEndLine
|
|
3664
|
+
)
|
|
3665
|
+
});
|
|
3666
|
+
}
|
|
3667
|
+
return definitions.map((definition) => {
|
|
3668
|
+
const corrected = correctedRanges.get(definition.symbolId);
|
|
3669
|
+
if (!corrected) {
|
|
3670
|
+
return definition;
|
|
3671
|
+
}
|
|
3672
|
+
return {
|
|
3673
|
+
...definition,
|
|
3674
|
+
startLine: corrected.startLine,
|
|
3675
|
+
endLine: corrected.endLine
|
|
3676
|
+
};
|
|
3677
|
+
});
|
|
3678
|
+
}
|
|
3679
|
+
function resolveCallableDefinitionStartLine(lines, definition) {
|
|
3680
|
+
if (!isCallableDefinition(definition.symbol)) {
|
|
3681
|
+
return definition.startLine;
|
|
3682
|
+
}
|
|
3683
|
+
const escapedLeaf = escapeRegex2(definition.leaf);
|
|
3684
|
+
const strongPatterns = [
|
|
3685
|
+
new RegExp(`\\b(?:function|def|fn)\\s+${escapedLeaf}\\b`),
|
|
3686
|
+
new RegExp(`\\b${escapedLeaf}\\b\\s*[:=]\\s*(?:async\\s*)?(?:function\\b|\\()`)
|
|
3687
|
+
];
|
|
3688
|
+
const fallbackPatterns = [
|
|
3689
|
+
new RegExp(`\\b${escapedLeaf}\\b\\s*\\(`)
|
|
3690
|
+
];
|
|
3691
|
+
return findNearestMatchingLine(
|
|
3692
|
+
lines,
|
|
3693
|
+
[...strongPatterns, ...fallbackPatterns],
|
|
3694
|
+
definition.startLine,
|
|
3695
|
+
definition.endLine
|
|
3696
|
+
);
|
|
3697
|
+
}
|
|
3698
|
+
function findNearestMatchingLine(lines, patterns, preferredStartLine, preferredEndLine) {
|
|
3699
|
+
const windowStart = Math.max(0, preferredStartLine - 40);
|
|
3700
|
+
const windowEnd = Math.min(lines.length - 1, Math.max(preferredEndLine + 40, preferredStartLine + 5));
|
|
3701
|
+
const windowMatch = matchNearestLine(lines, patterns, preferredStartLine, windowStart, windowEnd);
|
|
3702
|
+
if (windowMatch !== null) {
|
|
3703
|
+
return windowMatch;
|
|
3704
|
+
}
|
|
3705
|
+
const fullMatch = matchNearestLine(lines, patterns, preferredStartLine, 0, lines.length - 1);
|
|
3706
|
+
return fullMatch ?? Math.max(0, Math.min(preferredStartLine, lines.length - 1));
|
|
3707
|
+
}
|
|
3708
|
+
function matchNearestLine(lines, patterns, preferredLine, startLine, endLine) {
|
|
3709
|
+
let best = null;
|
|
3710
|
+
for (let lineIndex = startLine; lineIndex <= endLine; lineIndex += 1) {
|
|
3711
|
+
const line = lines[lineIndex] ?? "";
|
|
3712
|
+
if (!patterns.some((pattern) => pattern.test(line))) continue;
|
|
3713
|
+
const distance = Math.abs(lineIndex - preferredLine);
|
|
3714
|
+
if (!best || distance < best.distance) {
|
|
3715
|
+
best = { line: lineIndex, distance };
|
|
3716
|
+
}
|
|
3717
|
+
}
|
|
3718
|
+
return best?.line ?? null;
|
|
3719
|
+
}
|
|
3720
|
+
function resolveCallableDefinitionEndLine(lines, definition, startLine, maxEndLine) {
|
|
3721
|
+
const boundedEndLine = Math.max(startLine, Math.min(lines.length - 1, maxEndLine));
|
|
3722
|
+
const fallbackEndLine = Math.max(startLine, Math.min(boundedEndLine, definition.endLine));
|
|
3723
|
+
let braceDepth = 0;
|
|
3724
|
+
let parenDepth = 0;
|
|
3725
|
+
let sawOpeningBrace = false;
|
|
3726
|
+
for (let lineIndex = startLine; lineIndex <= boundedEndLine; lineIndex += 1) {
|
|
3727
|
+
const masked = maskStructuralLine(lines[lineIndex] ?? "");
|
|
3728
|
+
for (const char of masked) {
|
|
3729
|
+
if (char === "{") {
|
|
3730
|
+
braceDepth += 1;
|
|
3731
|
+
sawOpeningBrace = true;
|
|
3732
|
+
} else if (char === "}") {
|
|
3733
|
+
braceDepth = Math.max(0, braceDepth - 1);
|
|
3734
|
+
} else if (char === "(") {
|
|
3735
|
+
parenDepth += 1;
|
|
3736
|
+
} else if (char === ")") {
|
|
3737
|
+
parenDepth = Math.max(0, parenDepth - 1);
|
|
3738
|
+
}
|
|
3739
|
+
}
|
|
3740
|
+
if (sawOpeningBrace && braceDepth === 0) {
|
|
3741
|
+
return lineIndex;
|
|
3742
|
+
}
|
|
3743
|
+
if (!sawOpeningBrace && parenDepth === 0 && lineIndex >= fallbackEndLine) {
|
|
3744
|
+
return lineIndex;
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
return fallbackEndLine;
|
|
3748
|
+
}
|
|
3749
|
+
function maskStructuralLine(line) {
|
|
3750
|
+
let masked = "";
|
|
3751
|
+
let quote = null;
|
|
3752
|
+
let escaping = false;
|
|
3753
|
+
for (let index = 0; index < line.length; index += 1) {
|
|
3754
|
+
const char = line[index];
|
|
3755
|
+
const next = line[index + 1];
|
|
3756
|
+
if (!quote && char === "/" && next === "/") {
|
|
3757
|
+
masked += " ".repeat(line.length - index);
|
|
3758
|
+
break;
|
|
3759
|
+
}
|
|
3760
|
+
if (quote) {
|
|
3761
|
+
if (escaping) {
|
|
3762
|
+
escaping = false;
|
|
3763
|
+
masked += " ";
|
|
3764
|
+
continue;
|
|
3765
|
+
}
|
|
3766
|
+
if (char === "\\") {
|
|
3767
|
+
escaping = true;
|
|
3768
|
+
masked += " ";
|
|
3769
|
+
continue;
|
|
3770
|
+
}
|
|
3771
|
+
if (char === quote) {
|
|
3772
|
+
quote = null;
|
|
3773
|
+
}
|
|
3774
|
+
masked += " ";
|
|
3775
|
+
continue;
|
|
3776
|
+
}
|
|
3777
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
3778
|
+
quote = char;
|
|
3779
|
+
masked += " ";
|
|
3780
|
+
continue;
|
|
3781
|
+
}
|
|
3782
|
+
masked += char;
|
|
3783
|
+
}
|
|
3784
|
+
return masked;
|
|
3785
|
+
}
|
|
3786
|
+
function isCallableDefinition(symbol) {
|
|
3787
|
+
return symbol.includes("().");
|
|
3788
|
+
}
|
|
3789
|
+
function escapeRegex2(value) {
|
|
3790
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3459
3791
|
}
|
|
3460
3792
|
function getAllDefinitions(db, opts = {}) {
|
|
3461
3793
|
const { scope } = opts;
|
|
@@ -4008,8 +4340,8 @@ function resolveDocumentCandidates(db, filePattern, opts) {
|
|
|
4008
4340
|
}
|
|
4009
4341
|
function scoreDocumentPath(relativePath, rawPattern) {
|
|
4010
4342
|
const normalizedPath = normalizeLookupPath(relativePath);
|
|
4011
|
-
const pathBase =
|
|
4012
|
-
const patternBase =
|
|
4343
|
+
const pathBase = basename3(normalizedPath);
|
|
4344
|
+
const patternBase = basename3(rawPattern);
|
|
4013
4345
|
let score = 0;
|
|
4014
4346
|
if (normalizedPath === rawPattern) score += 1200;
|
|
4015
4347
|
if (normalizedPath.endsWith(`/${rawPattern}`)) score += 1100;
|
|
@@ -4055,7 +4387,7 @@ function symbols(db, filePattern) {
|
|
|
4055
4387
|
}
|
|
4056
4388
|
|
|
4057
4389
|
// src/queries/methods.ts
|
|
4058
|
-
import { basename as
|
|
4390
|
+
import { basename as basename4 } from "path";
|
|
4059
4391
|
function methods(db, className) {
|
|
4060
4392
|
const classMatch = findFirstSymbolMatch(db, className);
|
|
4061
4393
|
if (!classMatch) {
|
|
@@ -4064,7 +4396,7 @@ function methods(db, className) {
|
|
|
4064
4396
|
const ownerName = leafName(classMatch.symbol);
|
|
4065
4397
|
const definitions = getDefinitionsForFile(db, classMatch.relativePath).filter((definition) => isCallableSymbol(definition.symbol));
|
|
4066
4398
|
const directMethods = definitions.filter((definition) => definition.parentTypeName === ownerName || definition.symbol.includes(ownerName));
|
|
4067
|
-
const fileScopedMethods = directMethods.length > 0 ? directMethods : stripExtension(
|
|
4399
|
+
const fileScopedMethods = directMethods.length > 0 ? directMethods : stripExtension(basename4(classMatch.relativePath)) === ownerName ? definitions.filter((definition) => definition.symbol.includes("<invalid-global-code>")) : [];
|
|
4068
4400
|
return fileScopedMethods.map((definition) => ({
|
|
4069
4401
|
startLine: definition.startLine,
|
|
4070
4402
|
endLine: definition.endLine,
|
|
@@ -4448,11 +4780,11 @@ function isWorkerEntrySurface(path2) {
|
|
|
4448
4780
|
function isStructuralEntrySurface(path2) {
|
|
4449
4781
|
const normalized = normalizePath2(path2);
|
|
4450
4782
|
const segments = normalized.split("/");
|
|
4451
|
-
const
|
|
4452
|
-
if (
|
|
4783
|
+
const basename6 = segments[segments.length - 1] ?? normalized;
|
|
4784
|
+
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") {
|
|
4453
4785
|
return true;
|
|
4454
4786
|
}
|
|
4455
|
-
if (
|
|
4787
|
+
if (basename6 === "index.ts" || basename6 === "index.js") {
|
|
4456
4788
|
return segments.length <= 2;
|
|
4457
4789
|
}
|
|
4458
4790
|
return normalized.endsWith("/mod.rs") || normalized.endsWith("/__init__.py");
|
|
@@ -5526,7 +5858,7 @@ function similar(db, symbolPattern, opts = {}) {
|
|
|
5526
5858
|
const results = [];
|
|
5527
5859
|
for (const candidate of candidates) {
|
|
5528
5860
|
if (candidate.callees.size < 3) continue;
|
|
5529
|
-
const { similarity, significantShared
|
|
5861
|
+
const { similarity, significantShared } = weightedSimilarity(
|
|
5530
5862
|
target.callees,
|
|
5531
5863
|
candidate.callees,
|
|
5532
5864
|
idfWeights
|
|
@@ -5763,11 +6095,11 @@ function definitionSnippet(db, relativePath, startLine, endLine, leaf) {
|
|
|
5763
6095
|
return lines.slice(startLine, endLine + 1).join("\n");
|
|
5764
6096
|
}
|
|
5765
6097
|
const markerPatterns = [
|
|
5766
|
-
new RegExp(`\\bdef\\s+${
|
|
5767
|
-
new RegExp(`\\bfun\\s+${
|
|
5768
|
-
new RegExp(`\\bfn\\s+${
|
|
5769
|
-
new RegExp(`\\bfunction\\s+${
|
|
5770
|
-
new RegExp(`\\b${
|
|
6098
|
+
new RegExp(`\\bdef\\s+${escapeRegex3(leaf)}\\b`),
|
|
6099
|
+
new RegExp(`\\bfun\\s+${escapeRegex3(leaf)}\\b`),
|
|
6100
|
+
new RegExp(`\\bfn\\s+${escapeRegex3(leaf)}\\b`),
|
|
6101
|
+
new RegExp(`\\bfunction\\s+${escapeRegex3(leaf)}\\b`),
|
|
6102
|
+
new RegExp(`\\b${escapeRegex3(leaf)}\\s*\\(`)
|
|
5771
6103
|
];
|
|
5772
6104
|
const definitionStart = lines.findIndex((line) => markerPatterns.some((pattern) => pattern.test(line)));
|
|
5773
6105
|
if (definitionStart >= 0) {
|
|
@@ -5829,7 +6161,7 @@ function splitIdentifier(value) {
|
|
|
5829
6161
|
function looksLikeDefinitionBoundary(line) {
|
|
5830
6162
|
return /^\s*(?:def|fun|fn|function|class|trait|module|object|enum|interface|public|private|protected)\b/.test(line);
|
|
5831
6163
|
}
|
|
5832
|
-
function
|
|
6164
|
+
function escapeRegex3(value) {
|
|
5833
6165
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5834
6166
|
}
|
|
5835
6167
|
|
|
@@ -5955,8 +6287,8 @@ function similarChains(db, opts = {}) {
|
|
|
5955
6287
|
}
|
|
5956
6288
|
const structuralNames = ["index.ts", "index.js", "cli.ts", "main.ts", "health.ts", "health.js"];
|
|
5957
6289
|
for (const node of nodeFreq.keys()) {
|
|
5958
|
-
const
|
|
5959
|
-
if (structuralNames.includes(
|
|
6290
|
+
const basename6 = node.split("/").pop() ?? "";
|
|
6291
|
+
if (structuralNames.includes(basename6)) infraNodes.add(node);
|
|
5960
6292
|
}
|
|
5961
6293
|
const filteredChains = [];
|
|
5962
6294
|
for (const chain of rawChains) {
|
|
@@ -6130,8 +6462,8 @@ function extractCandidates(db, opts = {}) {
|
|
|
6130
6462
|
const results = [];
|
|
6131
6463
|
for (const sym of symbols2) {
|
|
6132
6464
|
if (db.isIgnored(sym.relativePath)) continue;
|
|
6133
|
-
const
|
|
6134
|
-
if (
|
|
6465
|
+
const basename6 = sym.relativePath.split("/").pop() ?? "";
|
|
6466
|
+
if (basename6.includes("types")) continue;
|
|
6135
6467
|
const calleeChunks = getCalleeRowsForSymbol(db, sym);
|
|
6136
6468
|
const calleeSet = new Set(calleeChunks.map((c) => c.symbol));
|
|
6137
6469
|
if (calleeSet.size < minCallees) continue;
|
|
@@ -6643,21 +6975,21 @@ function isLikelyTypeOnlyDep(dep) {
|
|
|
6643
6975
|
function shouldSkipDriftFile(filePath) {
|
|
6644
6976
|
return isStructuralRole(path.basename(filePath)) || isTestLikePath(filePath);
|
|
6645
6977
|
}
|
|
6646
|
-
function isStructuralRole(
|
|
6647
|
-
if (
|
|
6648
|
-
if (
|
|
6649
|
-
if (
|
|
6650
|
-
if (
|
|
6978
|
+
function isStructuralRole(basename6) {
|
|
6979
|
+
if (basename6 === "index.ts" || basename6 === "index.js") return true;
|
|
6980
|
+
if (basename6 === "cli.ts" || basename6 === "main.ts" || basename6 === "main.rs") return true;
|
|
6981
|
+
if (basename6.includes("worker.") || basename6.includes("postinstall.")) return true;
|
|
6982
|
+
if (basename6 === "health.ts" || basename6 === "health.js") return true;
|
|
6651
6983
|
return false;
|
|
6652
6984
|
}
|
|
6653
6985
|
function isTestLikePath(filePath) {
|
|
6654
6986
|
const normalized = filePath.replace(/\\/g, "/");
|
|
6655
|
-
const
|
|
6656
|
-
return normalized.includes("/__tests__/") || normalized.includes("/tests/") || normalized.includes("/test/") || /\.(test|spec)\.[A-Za-z0-9]+$/.test(
|
|
6987
|
+
const basename6 = path.basename(normalized);
|
|
6988
|
+
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);
|
|
6657
6989
|
}
|
|
6658
6990
|
|
|
6659
6991
|
// src/queries/wrapper-candidates.ts
|
|
6660
|
-
import { basename as
|
|
6992
|
+
import { basename as basename5, extname as extname4 } from "path";
|
|
6661
6993
|
function wrapperCandidates(db, opts) {
|
|
6662
6994
|
const { scope, maxLoc = 15, limit = 30 } = opts ?? {};
|
|
6663
6995
|
const reverseFanIn = buildReverseFileFanIn(buildFileDepGraph(db, scope));
|
|
@@ -6665,10 +6997,10 @@ function wrapperCandidates(db, opts) {
|
|
|
6665
6997
|
const results = [];
|
|
6666
6998
|
for (const symbol of symbols2) {
|
|
6667
6999
|
if (db.isIgnored(symbol.relativePath) || !isFunctionLikeSymbol(symbol.symbol)) continue;
|
|
6668
|
-
const symbolStem =
|
|
7000
|
+
const symbolStem = basename5(symbol.relativePath, extname4(symbol.relativePath));
|
|
6669
7001
|
const callerRows = dedupeRows(
|
|
6670
7002
|
getCallerRowsForSymbol(db, symbol, { limit: 200 }).filter((row) => row.file !== symbol.relativePath)
|
|
6671
|
-
).filter((row) =>
|
|
7003
|
+
).filter((row) => basename5(row.file, extname4(row.file)) !== symbolStem);
|
|
6672
7004
|
if (callerRows.length !== 1) continue;
|
|
6673
7005
|
const caller = callerRows[0];
|
|
6674
7006
|
const callerDefinition = getDefinitionsForFile(db, caller.file).find((definition) => definition.symbol === caller.symbol);
|
|
@@ -6690,7 +7022,7 @@ function wrapperCandidates(db, opts) {
|
|
|
6690
7022
|
endLine: symbol.endLine,
|
|
6691
7023
|
loc: definitionLoc2(symbol),
|
|
6692
7024
|
singleCaller: caller.symbol,
|
|
6693
|
-
singleCallerShort: useDefinitionFanIn ? shortenSymbol(caller.symbol) :
|
|
7025
|
+
singleCallerShort: useDefinitionFanIn ? shortenSymbol(caller.symbol) : basename5(caller.file),
|
|
6694
7026
|
callerFanIn
|
|
6695
7027
|
});
|
|
6696
7028
|
}
|
|
@@ -6745,11 +7077,11 @@ function fallbackCallerFanIn(db, reverseFanIn, callerFile) {
|
|
|
6745
7077
|
if (direct > 0) {
|
|
6746
7078
|
return direct;
|
|
6747
7079
|
}
|
|
6748
|
-
const stem =
|
|
7080
|
+
const stem = basename5(callerFile, extname4(callerFile));
|
|
6749
7081
|
let best = 0;
|
|
6750
7082
|
for (const [file, fanIn2] of reverseFanIn) {
|
|
6751
7083
|
if (file === callerFile) continue;
|
|
6752
|
-
if (
|
|
7084
|
+
if (basename5(file, extname4(file)) !== stem) continue;
|
|
6753
7085
|
if (fanIn2 > best) {
|
|
6754
7086
|
best = fanIn2;
|
|
6755
7087
|
}
|
|
@@ -6830,8 +7162,8 @@ function getFilesWithFunctions(db, scope) {
|
|
|
6830
7162
|
return new Set(getScopedDefinitions4(db, scope).filter((definition) => definition.isFunctionLike).map((definition) => definition.relativePath));
|
|
6831
7163
|
}
|
|
6832
7164
|
function isTrueStaleAbstraction(row, filesWithFunctions) {
|
|
6833
|
-
const
|
|
6834
|
-
const isTypeFile =
|
|
7165
|
+
const basename6 = row.file.split("/").pop() ?? "";
|
|
7166
|
+
const isTypeFile = basename6.includes("types") || row.file.includes("/types/");
|
|
6835
7167
|
if (isTypeFile && row.consumers > 0) {
|
|
6836
7168
|
return false;
|
|
6837
7169
|
}
|
|
@@ -7820,17 +8152,17 @@ function normalizeSignature(raw) {
|
|
|
7820
8152
|
}
|
|
7821
8153
|
function normalizeSourceSignature(raw, leaf) {
|
|
7822
8154
|
if (!raw || !raw.trim()) return null;
|
|
7823
|
-
|
|
8155
|
+
const declaration = raw.replace(/\s+/g, " ").trim();
|
|
7824
8156
|
const parenIdx = declaration.indexOf("(");
|
|
7825
8157
|
if (parenIdx === -1) return null;
|
|
7826
8158
|
let prefix = declaration.slice(0, parenIdx);
|
|
7827
|
-
const leafPattern = new RegExp(`\\b${
|
|
8159
|
+
const leafPattern = new RegExp(`\\b${escapeRegex4(leaf)}\\b`, "i");
|
|
7828
8160
|
const leafMatch = leafPattern.exec(prefix);
|
|
7829
8161
|
if (leafMatch && typeof leafMatch.index === "number") {
|
|
7830
8162
|
prefix = prefix.slice(0, leafMatch.index);
|
|
7831
8163
|
}
|
|
7832
8164
|
prefix = prefix.replace(/\b(public|private|protected|internal|final|static|abstract|sealed|virtual|override|async|suspend|inline|constexpr|consteval|constinit|const|pub|fn|function|def|sub|friend|shared|readonly|new|open|partial|export)\b/gi, " ").replace(/\s+/g, " ").trim();
|
|
7833
|
-
|
|
8165
|
+
const suffix = declaration.slice(parenIdx).replace(/\s*\{[\s\S]*$/, "").replace(/\s*=>[\s\S]*$/, "").replace(/\)\s*=\s*[\s\S]*$/, ")").replace(/\s+throws\s+[^={]+$/i, "").replace(/\s+where\s+.+$/i, "").replace(/\s+/g, " ").trim();
|
|
7834
8166
|
if (!suffix.startsWith("(")) {
|
|
7835
8167
|
return null;
|
|
7836
8168
|
}
|
|
@@ -7838,7 +8170,7 @@ function normalizeSourceSignature(raw, leaf) {
|
|
|
7838
8170
|
return normalized.length >= 3 ? normalized : null;
|
|
7839
8171
|
}
|
|
7840
8172
|
function declarationStartLines(lines, startLine, endLine, leaf) {
|
|
7841
|
-
const escapedLeaf =
|
|
8173
|
+
const escapedLeaf = escapeRegex4(leaf);
|
|
7842
8174
|
const callablePattern = new RegExp(`\\b${escapedLeaf}\\b\\s*\\(`, "i");
|
|
7843
8175
|
const rubyPattern = new RegExp(`\\bdef\\s+${escapedLeaf}\\b`, "i");
|
|
7844
8176
|
const candidates = [];
|
|
@@ -7869,7 +8201,7 @@ function parenBalance(value) {
|
|
|
7869
8201
|
}
|
|
7870
8202
|
return balance;
|
|
7871
8203
|
}
|
|
7872
|
-
function
|
|
8204
|
+
function escapeRegex4(value) {
|
|
7873
8205
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7874
8206
|
}
|
|
7875
8207
|
|
|
@@ -7881,7 +8213,7 @@ import {
|
|
|
7881
8213
|
readlinkSync,
|
|
7882
8214
|
unlinkSync
|
|
7883
8215
|
} from "fs";
|
|
7884
|
-
import { join as join11, dirname as
|
|
8216
|
+
import { join as join11, dirname as dirname4, resolve as resolve4 } from "path";
|
|
7885
8217
|
import { homedir as homedir2, platform as platform3 } from "os";
|
|
7886
8218
|
import { fileURLToPath } from "url";
|
|
7887
8219
|
var IS_WINDOWS3 = platform3() === "win32";
|
|
@@ -7896,7 +8228,7 @@ function installSkills(opts = {}) {
|
|
|
7896
8228
|
const log = opts.quiet ? () => {
|
|
7897
8229
|
} : console.log;
|
|
7898
8230
|
const thisFile = fileURLToPath(import.meta.url);
|
|
7899
|
-
const skillsSource =
|
|
8231
|
+
const skillsSource = resolve4(dirname4(thisFile), "..", "skills");
|
|
7900
8232
|
const targets = [
|
|
7901
8233
|
join11(homedir2(), ".claude", "skills"),
|
|
7902
8234
|
join11(homedir2(), ".codex", "skills")
|
|
@@ -7907,7 +8239,7 @@ function installSkills(opts = {}) {
|
|
|
7907
8239
|
alreadyLinked: []
|
|
7908
8240
|
};
|
|
7909
8241
|
for (const targetDir of targets) {
|
|
7910
|
-
const parentDir =
|
|
8242
|
+
const parentDir = dirname4(targetDir);
|
|
7911
8243
|
if (!existsSync9(parentDir)) {
|
|
7912
8244
|
continue;
|
|
7913
8245
|
}
|
|
@@ -7923,7 +8255,7 @@ function installSkills(opts = {}) {
|
|
|
7923
8255
|
if (existsSync9(target)) {
|
|
7924
8256
|
try {
|
|
7925
8257
|
const existing = readlinkSync(target);
|
|
7926
|
-
if (
|
|
8258
|
+
if (resolve4(existing) === resolve4(source)) {
|
|
7927
8259
|
result.alreadyLinked.push(`${toolName}/${skill}`);
|
|
7928
8260
|
log(` ok: ${skill} \u2192 ${toolName} (already linked)`);
|
|
7929
8261
|
continue;
|
|
@@ -8050,11 +8382,15 @@ var queries = {
|
|
|
8050
8382
|
program.name("scip-query").description("Language-agnostic code intelligence CLI powered by SCIP indexes").version(cliVersion);
|
|
8051
8383
|
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) => {
|
|
8052
8384
|
const projectRoot = resolveProjectRoot();
|
|
8385
|
+
const config = loadProjectConfig(projectRoot);
|
|
8386
|
+
const paths = resolveIndexPaths(projectRoot, config);
|
|
8053
8387
|
try {
|
|
8054
8388
|
const result = await reindex({
|
|
8055
8389
|
projectRoot,
|
|
8056
|
-
languages: opts.language.length > 0 ? opts.language :
|
|
8057
|
-
|
|
8390
|
+
languages: opts.language.length > 0 ? opts.language : config.languages,
|
|
8391
|
+
outputScip: paths.indexPath,
|
|
8392
|
+
outputDb: paths.dbPath,
|
|
8393
|
+
pnpmWorkspaces: opts.pnpmWorkspaces || config.indexer?.typescript?.pnpmWorkspaces
|
|
8058
8394
|
});
|
|
8059
8395
|
console.log(`Indexed ${result.languages.join(", ")} in ${(result.durationMs / 1e3).toFixed(1)}s`);
|
|
8060
8396
|
} catch (err) {
|
|
@@ -9039,6 +9375,7 @@ program.command("watch").description("Watch for file changes and reindex automat
|
|
|
9039
9375
|
const watcher = new Watcher({
|
|
9040
9376
|
projectRoot,
|
|
9041
9377
|
config,
|
|
9378
|
+
languages: config.languages,
|
|
9042
9379
|
onStatus: (status) => {
|
|
9043
9380
|
process.stdout.write(`\r\x1B[K${formatStatus(status)}`);
|
|
9044
9381
|
},
|