scip-query 0.2.1 → 0.3.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/dist/chunk-26DOJ63W.js +161 -0
- package/dist/{chunk-PMJKOXOT.js → chunk-4JCSOF2O.js} +2 -2
- package/dist/{chunk-AKMBBKWV.js → chunk-5OMVSV6E.js} +12 -4
- package/dist/{chunk-R56FJU3E.js → chunk-7KIMF5PV.js} +2 -2
- package/dist/chunk-AXQKUYKF.js +1442 -0
- package/dist/chunk-C7H5WBTJ.js +46 -0
- package/dist/{chunk-AMNISGYR.js → chunk-CHDJXYBG.js} +2 -27
- package/dist/{chunk-GPJVPT3U.js → chunk-CPVAQJEC.js} +12 -4
- package/dist/{chunk-75RQSBTK.js → chunk-DH7G3DDV.js} +2 -2
- package/dist/{chunk-LTJC5ZQL.js → chunk-EOROMIFO.js} +13 -5
- package/dist/{chunk-BFLULBEU.js → chunk-EPWLXXBL.js} +2 -2
- package/dist/{chunk-MVH45PYK.js → chunk-FYYOWQXK.js} +12 -12
- package/dist/chunk-GEXE2T6I.js +87 -0
- package/dist/{chunk-IXPHLF6K.js → chunk-GJDHTTR2.js} +10 -3
- package/dist/chunk-GSH2FPKV.js +87 -0
- package/dist/{chunk-4ACRRQC4.js → chunk-HJZUSUPU.js} +2 -2
- package/dist/{chunk-M3NPW3FC.js → chunk-HLKAFWWJ.js} +81 -2
- package/dist/{chunk-CU62ZDHI.js → chunk-HLUS2HEB.js} +2 -2
- package/dist/{chunk-RFMT7UAZ.js → chunk-J3JSOSUO.js} +8 -5
- package/dist/{chunk-HDSRORNV.js → chunk-KKCHYLVI.js} +17 -11
- package/dist/{chunk-Y3M323OX.js → chunk-LFJQVJYJ.js} +2 -2
- package/dist/{chunk-6WVR5K46.js → chunk-LQJUPXQY.js} +3 -3
- package/dist/{chunk-ITZ3DDOG.js → chunk-MPGIHELS.js} +18 -3
- package/dist/{chunk-N4C3H7LH.js → chunk-NFS5W3PP.js} +2 -2
- package/dist/{chunk-Y4JFVQ7C.js → chunk-O7Q7FDUJ.js} +21 -13
- package/dist/{chunk-H6WCPKCX.js → chunk-OIDHN6GD.js} +2 -2
- package/dist/{chunk-4BQFSNFI.js → chunk-P3E6L7KW.js} +2 -2
- package/dist/{chunk-ORINICIZ.js → chunk-P4WO3BBW.js} +2 -2
- package/dist/{chunk-M4QGEKKD.js → chunk-SMDCNPMK.js} +8 -3
- package/dist/{chunk-6QSHLFSL.js → chunk-UGQKAVCD.js} +2 -2
- package/dist/{chunk-HMYJJ3HY.js → chunk-UQEQ6AHX.js} +3 -3
- package/dist/{chunk-WVK7AASK.js → chunk-VIYSWZCO.js} +2 -2
- package/dist/chunk-VJJKSGIX.js +121 -0
- package/dist/{chunk-N5KEREIA.js → chunk-VT4JBH6L.js} +19 -7
- package/dist/{chunk-R2I3M5B4.js → chunk-WGAD3GNR.js} +2 -2
- package/dist/{chunk-IJKLB2JW.js → chunk-YDBXNPYU.js} +2 -2
- package/dist/{chunk-VO4QI3LS.js → chunk-YY4QGUQ5.js} +2 -2
- package/dist/{chunk-3566TKJ5.js → chunk-ZEUCXQBN.js} +2 -2
- package/dist/cli.js +2314 -1352
- package/dist/{db-BHYam4BK.d.ts → db-ShvwGDKf.d.ts} +6 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +38 -38
- package/dist/queries/affected.d.ts +1 -1
- package/dist/queries/affected.js +2 -2
- package/dist/queries/bottlenecks.d.ts +1 -1
- package/dist/queries/by-kind.d.ts +1 -1
- package/dist/queries/by-kind.js +1 -1
- package/dist/queries/call-graph.d.ts +1 -1
- package/dist/queries/call-graph.js +2 -2
- package/dist/queries/change-surface.d.ts +1 -1
- package/dist/queries/change-surface.js +2 -1
- package/dist/queries/code.d.ts +1 -1
- package/dist/queries/code.js +2 -2
- package/dist/queries/complexity-hotspots.d.ts +1 -1
- package/dist/queries/complexity-hotspots.js +2 -2
- package/dist/queries/complexity.d.ts +1 -1
- package/dist/queries/complexity.js +2 -2
- package/dist/queries/convergence.d.ts +1 -1
- package/dist/queries/convergence.js +2 -2
- package/dist/queries/coupling.d.ts +1 -1
- package/dist/queries/coupling.js +3 -1
- package/dist/queries/cycles.d.ts +1 -1
- package/dist/queries/cycles.js +2 -2
- package/dist/queries/dataflow.d.ts +1 -1
- package/dist/queries/dataflow.js +2 -2
- package/dist/queries/dead.d.ts +1 -1
- package/dist/queries/dead.js +3 -3
- package/dist/queries/deep-chains.d.ts +1 -1
- package/dist/queries/deep-chains.js +2 -2
- package/dist/queries/deps.d.ts +1 -1
- package/dist/queries/deps.js +3 -1
- package/dist/queries/diff-impact.d.ts +1 -1
- package/dist/queries/doc-coverage.d.ts +1 -1
- package/dist/queries/drift.d.ts +1 -1
- package/dist/queries/drift.js +2 -2
- package/dist/queries/extract-candidates.d.ts +1 -1
- package/dist/queries/extract-candidates.js +2 -2
- package/dist/queries/fan.d.ts +1 -1
- package/dist/queries/fan.js +2 -1
- package/dist/queries/files.d.ts +1 -1
- package/dist/queries/health.d.ts +1 -1
- package/dist/queries/health.js +13 -13
- package/dist/queries/hierarchy.d.ts +1 -1
- package/dist/queries/hierarchy.js +2 -2
- package/dist/queries/hotspots.d.ts +1 -1
- package/dist/queries/imports.d.ts +1 -1
- package/dist/queries/imports.js +2 -2
- package/dist/queries/index.d.ts +1 -1
- package/dist/queries/index.js +38 -38
- package/dist/queries/isolated.d.ts +1 -1
- package/dist/queries/isolated.js +3 -3
- package/dist/queries/members.d.ts +1 -1
- package/dist/queries/members.js +2 -2
- package/dist/queries/methods.d.ts +1 -1
- package/dist/queries/outline.d.ts +1 -1
- package/dist/queries/outline.js +2 -1
- package/dist/queries/passthrough-candidates.d.ts +1 -1
- package/dist/queries/passthrough-candidates.js +2 -2
- package/dist/queries/redundant-reexports.d.ts +1 -1
- package/dist/queries/redundant-reexports.js +3 -3
- package/dist/queries/refs.d.ts +1 -1
- package/dist/queries/refs.js +3 -1
- package/dist/queries/similar-chains.d.ts +1 -1
- package/dist/queries/similar-chains.js +2 -2
- package/dist/queries/similar-files.d.ts +1 -1
- package/dist/queries/similar-files.js +2 -2
- package/dist/queries/similar-signatures.d.ts +1 -1
- package/dist/queries/similar.d.ts +1 -1
- package/dist/queries/similar.js +2 -2
- package/dist/queries/slice.d.ts +2 -2
- package/dist/queries/slice.js +2 -2
- package/dist/queries/stale-abstractions.d.ts +1 -1
- package/dist/queries/stale-abstractions.js +2 -2
- package/dist/queries/stats.d.ts +1 -1
- package/dist/queries/surface.d.ts +1 -1
- package/dist/queries/surface.js +2 -1
- package/dist/queries/symbols.d.ts +1 -1
- package/dist/queries/symbols.js +2 -1
- package/dist/queries/system.d.ts +1 -1
- package/dist/queries/system.js +2 -1
- package/dist/queries/trace.d.ts +1 -1
- package/dist/queries/trace.js +2 -2
- package/dist/queries/wrapper-candidates.d.ts +1 -1
- package/dist/queries/wrapper-candidates.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-34JPTNRN.js +0 -601
- package/dist/chunk-7JFZSOJ7.js +0 -103
- package/dist/chunk-DY4AFG2W.js +0 -48
- package/dist/chunk-LLMPAG56.js +0 -221
- package/dist/chunk-NVIIM34O.js +0 -65
- package/dist/chunk-YAFWL3RA.js +0 -55
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { program } from "commander";
|
|
5
|
-
import {
|
|
5
|
+
import { createRequire } from "module";
|
|
6
|
+
import { existsSync as existsSync8, realpathSync } from "fs";
|
|
6
7
|
import { join as join10 } from "path";
|
|
7
8
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8
9
|
|
|
@@ -1037,431 +1038,1052 @@ function files(db, pattern) {
|
|
|
1037
1038
|
return rows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({ relativePath: r.relative_path }));
|
|
1038
1039
|
}
|
|
1039
1040
|
|
|
1040
|
-
// src/
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1041
|
+
// src/query-support.ts
|
|
1042
|
+
import { basename } from "path";
|
|
1043
|
+
|
|
1044
|
+
// src/source-analysis.ts
|
|
1045
|
+
import {
|
|
1046
|
+
existsSync as existsSync6,
|
|
1047
|
+
readFileSync as readFileSync4
|
|
1048
|
+
} from "fs";
|
|
1049
|
+
import {
|
|
1050
|
+
dirname as dirname2,
|
|
1051
|
+
extname,
|
|
1052
|
+
join as join6,
|
|
1053
|
+
relative as relative2,
|
|
1054
|
+
resolve as resolve2
|
|
1055
|
+
} from "path";
|
|
1056
|
+
var SOURCE_IMPORT_CACHE = /* @__PURE__ */ new WeakMap();
|
|
1057
|
+
var SOURCE_TEXT_CACHE = /* @__PURE__ */ new WeakMap();
|
|
1058
|
+
var SOURCE_CALL_CACHE = /* @__PURE__ */ new WeakMap();
|
|
1059
|
+
var SOURCE_BINDING_CACHE = /* @__PURE__ */ new WeakMap();
|
|
1060
|
+
var INDEXED_PATH_CACHE = /* @__PURE__ */ new WeakMap();
|
|
1061
|
+
var SOURCE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
1062
|
+
var PYTHON_SOURCE_EXTENSIONS = [".py", ".pyi"];
|
|
1063
|
+
function getSourceImports(db, relativePath) {
|
|
1064
|
+
const cache = getCachedMap(SOURCE_IMPORT_CACHE, db);
|
|
1065
|
+
const normalized = normalizePath(relativePath);
|
|
1066
|
+
const cached = cache.get(normalized);
|
|
1067
|
+
if (cached) {
|
|
1068
|
+
return cached;
|
|
1052
1069
|
}
|
|
1053
|
-
const
|
|
1054
|
-
if (
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
manager: parts[1] ?? "",
|
|
1058
|
-
packageName: parts[2] ?? "",
|
|
1059
|
-
version: "",
|
|
1060
|
-
descriptors: [],
|
|
1061
|
-
raw
|
|
1062
|
-
};
|
|
1070
|
+
const fullPath = join6(db.config.projectRoot, normalized);
|
|
1071
|
+
if (!existsSync6(fullPath)) {
|
|
1072
|
+
cache.set(normalized, []);
|
|
1073
|
+
return [];
|
|
1063
1074
|
}
|
|
1064
|
-
const
|
|
1065
|
-
const
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1075
|
+
const source = readFileSync4(fullPath, "utf-8");
|
|
1076
|
+
const parsed = isPythonSourcePath(normalized) ? parsePythonImports(db, normalized, source) : parseJavaScriptImports(db, normalized, source);
|
|
1077
|
+
cache.set(normalized, parsed);
|
|
1078
|
+
return parsed;
|
|
1079
|
+
}
|
|
1080
|
+
function getSourceCalls(db, relativePath, opts = {}) {
|
|
1081
|
+
const normalized = normalizePath(relativePath);
|
|
1082
|
+
if (!isPythonSourcePath(normalized) && !isJavaScriptSourcePath(normalized)) {
|
|
1083
|
+
return [];
|
|
1084
|
+
}
|
|
1085
|
+
const cache = getCachedMap(SOURCE_CALL_CACHE, db);
|
|
1086
|
+
const key = `${normalized}:${opts.startLine ?? 0}:${opts.endLine ?? Number.MAX_SAFE_INTEGER}`;
|
|
1087
|
+
const cached = cache.get(key);
|
|
1088
|
+
if (cached) {
|
|
1089
|
+
return cached;
|
|
1090
|
+
}
|
|
1091
|
+
const source = getSourceText(db, normalized);
|
|
1092
|
+
if (!source) {
|
|
1093
|
+
cache.set(key, []);
|
|
1094
|
+
return [];
|
|
1095
|
+
}
|
|
1096
|
+
const lines = source.split("\n");
|
|
1097
|
+
const startLine = Math.max(0, opts.startLine ?? 0);
|
|
1098
|
+
const endLine = Math.min(lines.length - 1, opts.endLine ?? lines.length - 1);
|
|
1099
|
+
const scopedLines = lines.slice(startLine, endLine + 1);
|
|
1100
|
+
const calls = isPythonSourcePath(normalized) ? parsePythonCalls(scopedLines, startLine) : parseJavaScriptCalls(scopedLines, startLine);
|
|
1101
|
+
cache.set(key, calls);
|
|
1102
|
+
return calls;
|
|
1103
|
+
}
|
|
1104
|
+
function getSourceConstructorBindings(db, relativePath, opts = {}) {
|
|
1105
|
+
const normalized = normalizePath(relativePath);
|
|
1106
|
+
if (!isPythonSourcePath(normalized) && !isJavaScriptSourcePath(normalized)) {
|
|
1107
|
+
return [];
|
|
1108
|
+
}
|
|
1109
|
+
const cache = getCachedMap(SOURCE_BINDING_CACHE, db);
|
|
1110
|
+
const key = `${normalized}:${opts.startLine ?? 0}:${opts.endLine ?? Number.MAX_SAFE_INTEGER}`;
|
|
1111
|
+
const cached = cache.get(key);
|
|
1112
|
+
if (cached) {
|
|
1113
|
+
return cached;
|
|
1114
|
+
}
|
|
1115
|
+
const source = getSourceText(db, normalized);
|
|
1116
|
+
if (!source) {
|
|
1117
|
+
cache.set(key, []);
|
|
1118
|
+
return [];
|
|
1119
|
+
}
|
|
1120
|
+
const lines = source.split("\n");
|
|
1121
|
+
const startLine = Math.max(0, opts.startLine ?? 0);
|
|
1122
|
+
const endLine = Math.min(lines.length - 1, opts.endLine ?? lines.length - 1);
|
|
1123
|
+
const scopedLines = lines.slice(startLine, endLine + 1);
|
|
1124
|
+
const bindings = isPythonSourcePath(normalized) ? parsePythonConstructorBindings(scopedLines) : parseJavaScriptConstructorBindings(scopedLines);
|
|
1125
|
+
cache.set(key, bindings);
|
|
1126
|
+
return bindings;
|
|
1127
|
+
}
|
|
1128
|
+
function findIdentifierLines(db, relativePath, identifier, opts = {}) {
|
|
1129
|
+
if (!identifier) {
|
|
1130
|
+
return [];
|
|
1131
|
+
}
|
|
1132
|
+
const source = getSourceText(db, normalizePath(relativePath));
|
|
1133
|
+
if (!source) {
|
|
1134
|
+
return [];
|
|
1135
|
+
}
|
|
1136
|
+
const lines = stripCommentsAndStrings(source).split("\n");
|
|
1137
|
+
const regex = new RegExp(`\\b${escapeRegex(identifier)}\\b`);
|
|
1138
|
+
const results = [];
|
|
1139
|
+
for (let line = 0; line < lines.length; line++) {
|
|
1140
|
+
if (typeof opts.excludeStartLine === "number" && typeof opts.excludeEndLine === "number" && line >= opts.excludeStartLine && line <= opts.excludeEndLine) {
|
|
1141
|
+
continue;
|
|
1076
1142
|
}
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
if (spaceIdx === -1) {
|
|
1080
|
-
packageName = restAfterManager;
|
|
1081
|
-
restAfterManager = "";
|
|
1082
|
-
} else {
|
|
1083
|
-
packageName = restAfterManager.slice(0, spaceIdx);
|
|
1084
|
-
restAfterManager = restAfterManager.slice(spaceIdx + 1);
|
|
1143
|
+
if (regex.test(lines[line] ?? "")) {
|
|
1144
|
+
results.push(line);
|
|
1085
1145
|
}
|
|
1086
1146
|
}
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1147
|
+
return results;
|
|
1148
|
+
}
|
|
1149
|
+
function parseJavaScriptImports(db, importerPath, source) {
|
|
1150
|
+
return parseJavaScriptImportStatements(source).flatMap((statement) => parseJavaScriptImportStatement(
|
|
1151
|
+
db,
|
|
1152
|
+
importerPath,
|
|
1153
|
+
statement.clause,
|
|
1154
|
+
statement.specifier,
|
|
1155
|
+
statement.start,
|
|
1156
|
+
statement.end,
|
|
1157
|
+
source
|
|
1158
|
+
));
|
|
1159
|
+
}
|
|
1160
|
+
function parseJavaScriptImportStatements(source) {
|
|
1161
|
+
const statements = [];
|
|
1162
|
+
const importFromRegex = /^[ \t]*import\s+([\s\S]*?)\s+from\s+['"]([^'"]+)['"]\s*;?/gm;
|
|
1163
|
+
for (const match of source.matchAll(importFromRegex)) {
|
|
1164
|
+
const full = match[0];
|
|
1165
|
+
const clause = match[1];
|
|
1166
|
+
const specifier = match[2];
|
|
1167
|
+
if (!full || !specifier || typeof match.index !== "number") continue;
|
|
1168
|
+
statements.push({
|
|
1169
|
+
clause,
|
|
1170
|
+
specifier,
|
|
1171
|
+
start: match.index,
|
|
1172
|
+
end: match.index + full.length
|
|
1173
|
+
});
|
|
1095
1174
|
}
|
|
1096
|
-
const
|
|
1097
|
-
|
|
1175
|
+
const sideEffectRegex = /^[ \t]*import\s+['"]([^'"]+)['"]\s*;?/gm;
|
|
1176
|
+
for (const match of source.matchAll(sideEffectRegex)) {
|
|
1177
|
+
const full = match[0];
|
|
1178
|
+
const specifier = match[1];
|
|
1179
|
+
if (!full || !specifier || typeof match.index !== "number") continue;
|
|
1180
|
+
statements.push({
|
|
1181
|
+
clause: null,
|
|
1182
|
+
specifier,
|
|
1183
|
+
start: match.index,
|
|
1184
|
+
end: match.index + full.length
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
return statements.sort((a, b) => a.start - b.start);
|
|
1098
1188
|
}
|
|
1099
|
-
function
|
|
1100
|
-
const
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1189
|
+
function parseJavaScriptImportStatement(db, importerPath, clause, specifier, start, end, source) {
|
|
1190
|
+
const resolvedSource = resolveImportPath(db, importerPath, specifier);
|
|
1191
|
+
const body = buildUsageBody(source, start, end);
|
|
1192
|
+
if (!clause) {
|
|
1193
|
+
return [{
|
|
1194
|
+
importedName: "*",
|
|
1195
|
+
localName: null,
|
|
1196
|
+
sourcePath: resolvedSource,
|
|
1197
|
+
kind: "side-effect",
|
|
1198
|
+
used: true,
|
|
1199
|
+
usedMembers: []
|
|
1200
|
+
}];
|
|
1201
|
+
}
|
|
1202
|
+
const bindings = parseImportClause(clause).map((binding) => ({
|
|
1203
|
+
...binding,
|
|
1204
|
+
sourcePath: resolvedSource
|
|
1205
|
+
}));
|
|
1206
|
+
return bindings.map((binding) => {
|
|
1207
|
+
if (binding.kind === "namespace") {
|
|
1208
|
+
const usedMembers = collectNamespaceMembers(body, binding.localName);
|
|
1209
|
+
return {
|
|
1210
|
+
...binding,
|
|
1211
|
+
used: usedMembers.length > 0 || hasIdentifierUsage(body, binding.localName),
|
|
1212
|
+
usedMembers
|
|
1213
|
+
};
|
|
1112
1214
|
}
|
|
1113
|
-
if (
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1215
|
+
if (binding.kind === "side-effect") {
|
|
1216
|
+
return { ...binding, used: true, usedMembers: [] };
|
|
1217
|
+
}
|
|
1218
|
+
return {
|
|
1219
|
+
...binding,
|
|
1220
|
+
used: binding.localName ? hasIdentifierUsage(body, binding.localName) : false,
|
|
1221
|
+
usedMembers: []
|
|
1222
|
+
};
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
function parsePythonCalls(lines, baseLine) {
|
|
1226
|
+
const calls = [];
|
|
1227
|
+
const controlKeywords = /* @__PURE__ */ new Set([
|
|
1228
|
+
"if",
|
|
1229
|
+
"for",
|
|
1230
|
+
"while",
|
|
1231
|
+
"with",
|
|
1232
|
+
"except",
|
|
1233
|
+
"elif",
|
|
1234
|
+
"return",
|
|
1235
|
+
"yield",
|
|
1236
|
+
"assert",
|
|
1237
|
+
"raise",
|
|
1238
|
+
"lambda",
|
|
1239
|
+
"class",
|
|
1240
|
+
"def"
|
|
1241
|
+
]);
|
|
1242
|
+
for (let index = 0; index < lines.length; index++) {
|
|
1243
|
+
const rawLine = lines[index] ?? "";
|
|
1244
|
+
const stripped = stripCommentsAndStrings(rawLine);
|
|
1245
|
+
if (!stripped.trim()) continue;
|
|
1246
|
+
const attributeMatches = [...stripped.matchAll(/\b([A-Za-z_][\w]*)\s*\.\s*([A-Za-z_][\w]*)\s*\(/g)];
|
|
1247
|
+
const attributeRanges = attributeMatches.map((match) => ({
|
|
1248
|
+
start: match.index ?? -1,
|
|
1249
|
+
end: (match.index ?? -1) + match[0].length
|
|
1250
|
+
}));
|
|
1251
|
+
for (const match of attributeMatches) {
|
|
1252
|
+
const receiverName = match[1];
|
|
1253
|
+
const calleeName = match[2];
|
|
1254
|
+
if (!receiverName || !calleeName) continue;
|
|
1255
|
+
calls.push({
|
|
1256
|
+
receiverName,
|
|
1257
|
+
calleeName,
|
|
1258
|
+
line: baseLine + index
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
for (const match of stripped.matchAll(/\b([A-Za-z_][\w]*)\s*\(/g)) {
|
|
1262
|
+
const calleeName = match[1];
|
|
1263
|
+
const start = match.index ?? -1;
|
|
1264
|
+
if (!calleeName || start < 0) continue;
|
|
1265
|
+
if (controlKeywords.has(calleeName)) continue;
|
|
1266
|
+
if (attributeRanges.some((range) => start >= range.start && start < range.end)) continue;
|
|
1267
|
+
const prefix = stripped.slice(0, start).trimEnd();
|
|
1268
|
+
if (prefix.endsWith("def") || prefix.endsWith("class") || prefix.endsWith("async def")) {
|
|
1118
1269
|
continue;
|
|
1119
1270
|
}
|
|
1271
|
+
calls.push({
|
|
1272
|
+
receiverName: null,
|
|
1273
|
+
calleeName,
|
|
1274
|
+
line: baseLine + index
|
|
1275
|
+
});
|
|
1120
1276
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1277
|
+
}
|
|
1278
|
+
return calls;
|
|
1279
|
+
}
|
|
1280
|
+
function parseJavaScriptCalls(lines, baseLine) {
|
|
1281
|
+
const calls = [];
|
|
1282
|
+
const controlKeywords = /* @__PURE__ */ new Set([
|
|
1283
|
+
"if",
|
|
1284
|
+
"for",
|
|
1285
|
+
"while",
|
|
1286
|
+
"switch",
|
|
1287
|
+
"catch",
|
|
1288
|
+
"function",
|
|
1289
|
+
"class",
|
|
1290
|
+
"return",
|
|
1291
|
+
"typeof",
|
|
1292
|
+
"import"
|
|
1293
|
+
]);
|
|
1294
|
+
for (let index = 0; index < lines.length; index++) {
|
|
1295
|
+
const stripped = stripCommentsAndStrings(lines[index] ?? "");
|
|
1296
|
+
if (!stripped.trim()) continue;
|
|
1297
|
+
const attributeMatches = [
|
|
1298
|
+
...stripped.matchAll(/\b([A-Za-z_$][\w$]*)\s*(?:\?\.|\.)\s*([A-Za-z_$][\w$]*)\s*\(/g),
|
|
1299
|
+
...stripped.matchAll(/\bthis\s*(?:\?\.|\.)\s*([A-Za-z_$][\w$]*)\s*\(/g)
|
|
1300
|
+
];
|
|
1301
|
+
const attributeRanges = attributeMatches.map((match) => ({
|
|
1302
|
+
start: match.index ?? -1,
|
|
1303
|
+
end: (match.index ?? -1) + match[0].length
|
|
1304
|
+
}));
|
|
1305
|
+
for (const match of attributeMatches) {
|
|
1306
|
+
const receiverName = match[2] ? match[1] : "this";
|
|
1307
|
+
const calleeName = match[2] ?? match[1];
|
|
1308
|
+
if (!receiverName || !calleeName) continue;
|
|
1309
|
+
calls.push({
|
|
1310
|
+
receiverName,
|
|
1311
|
+
calleeName,
|
|
1312
|
+
line: baseLine + index
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
for (const match of stripped.matchAll(/\b([A-Za-z_$][\w$]*)\s*\(/g)) {
|
|
1316
|
+
const calleeName = match[1];
|
|
1317
|
+
const start = match.index ?? -1;
|
|
1318
|
+
if (!calleeName || start < 0) continue;
|
|
1319
|
+
if (controlKeywords.has(calleeName)) continue;
|
|
1320
|
+
if (attributeRanges.some((range) => start >= range.start && start < range.end)) continue;
|
|
1321
|
+
const prefix = stripped.slice(0, start).trimEnd();
|
|
1322
|
+
if (prefix.endsWith("function") || prefix.endsWith("class") || prefix.endsWith("new")) {
|
|
1323
|
+
continue;
|
|
1136
1324
|
}
|
|
1137
|
-
|
|
1325
|
+
calls.push({
|
|
1326
|
+
receiverName: null,
|
|
1327
|
+
calleeName,
|
|
1328
|
+
line: baseLine + index
|
|
1329
|
+
});
|
|
1138
1330
|
}
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1331
|
+
}
|
|
1332
|
+
return calls;
|
|
1333
|
+
}
|
|
1334
|
+
function parsePythonConstructorBindings(lines) {
|
|
1335
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
1336
|
+
for (const rawLine of lines) {
|
|
1337
|
+
const stripped = stripCommentsAndStrings(rawLine);
|
|
1338
|
+
const constructorMatch = stripped.match(/\b([A-Za-z_][\w]*)\s*=\s*([A-Z][\w]*)\s*\(/);
|
|
1339
|
+
if (constructorMatch?.[1] && constructorMatch[2]) {
|
|
1340
|
+
bindings.set(constructorMatch[1], constructorMatch[2]);
|
|
1142
1341
|
}
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
}
|
|
1156
|
-
} else {
|
|
1157
|
-
const suffix = SUFFIX_MAP[char];
|
|
1158
|
-
if (suffix) {
|
|
1159
|
-
descriptors.push({ name, suffix });
|
|
1160
|
-
i += 1;
|
|
1161
|
-
} else {
|
|
1162
|
-
i += 1;
|
|
1342
|
+
}
|
|
1343
|
+
return [...bindings.entries()].map(([localName, typeName]) => ({ localName, typeName }));
|
|
1344
|
+
}
|
|
1345
|
+
function parseJavaScriptConstructorBindings(lines) {
|
|
1346
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
1347
|
+
for (const rawLine of lines) {
|
|
1348
|
+
const stripped = stripCommentsAndStrings(rawLine);
|
|
1349
|
+
for (const match of stripped.matchAll(/\b([A-Za-z_$][\w$]*)\s*:\s*([A-Z][A-Za-z0-9_$]*)\b/g)) {
|
|
1350
|
+
const localName = match[1];
|
|
1351
|
+
const typeName = match[2];
|
|
1352
|
+
if (localName && typeName) {
|
|
1353
|
+
bindings.set(localName, typeName);
|
|
1163
1354
|
}
|
|
1164
1355
|
}
|
|
1356
|
+
const constructorMatch = stripped.match(/\b(?:const|let|var)?\s*([A-Za-z_$][\w$]*)\s*(?::\s*[A-Z][A-Za-z0-9_$<> ,]*)?\s*=\s*new\s+([A-Z][A-Za-z0-9_$]*)\s*\(/);
|
|
1357
|
+
if (constructorMatch?.[1] && constructorMatch[2]) {
|
|
1358
|
+
bindings.set(constructorMatch[1], constructorMatch[2]);
|
|
1359
|
+
}
|
|
1165
1360
|
}
|
|
1166
|
-
return
|
|
1361
|
+
return [...bindings.entries()].map(([localName, typeName]) => ({ localName, typeName }));
|
|
1167
1362
|
}
|
|
1168
|
-
function
|
|
1169
|
-
return
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1363
|
+
function parsePythonImports(db, importerPath, source) {
|
|
1364
|
+
return collectPythonImportStatements(source).flatMap(
|
|
1365
|
+
(statement) => parsePythonImportStatement(db, importerPath, statement, source)
|
|
1366
|
+
);
|
|
1367
|
+
}
|
|
1368
|
+
function collectPythonImportStatements(source) {
|
|
1369
|
+
const lines = source.split("\n");
|
|
1370
|
+
const statements = [];
|
|
1371
|
+
let offset = 0;
|
|
1372
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
1373
|
+
const line = lines[lineIndex];
|
|
1374
|
+
const trimmed = line.trimStart();
|
|
1375
|
+
const lineStart = offset;
|
|
1376
|
+
offset += line.length + 1;
|
|
1377
|
+
if (!trimmed.startsWith("import ") && !trimmed.startsWith("from ")) {
|
|
1378
|
+
continue;
|
|
1183
1379
|
}
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1380
|
+
let statement = line;
|
|
1381
|
+
let statementEnd = lineStart + line.length;
|
|
1382
|
+
let balance = pythonParenBalance(line);
|
|
1383
|
+
while (lineIndex + 1 < lines.length && (balance > 0 || statement.trimEnd().endsWith("\\"))) {
|
|
1384
|
+
lineIndex++;
|
|
1385
|
+
const nextLine = lines[lineIndex];
|
|
1386
|
+
statement += `
|
|
1387
|
+
${nextLine}`;
|
|
1388
|
+
statementEnd += 1 + nextLine.length;
|
|
1389
|
+
balance += pythonParenBalance(nextLine);
|
|
1390
|
+
offset += nextLine.length + 1;
|
|
1391
|
+
}
|
|
1392
|
+
const parsed = parsePythonStatementHeader(statement);
|
|
1393
|
+
if (parsed) {
|
|
1394
|
+
statements.push({
|
|
1395
|
+
...parsed,
|
|
1396
|
+
start: lineStart,
|
|
1397
|
+
end: statementEnd
|
|
1398
|
+
});
|
|
1189
1399
|
}
|
|
1190
1400
|
}
|
|
1191
|
-
return
|
|
1401
|
+
return statements;
|
|
1192
1402
|
}
|
|
1193
|
-
function
|
|
1194
|
-
const
|
|
1195
|
-
if ("
|
|
1196
|
-
return
|
|
1403
|
+
function parsePythonStatementHeader(statement) {
|
|
1404
|
+
const normalized = statement.replace(/\\\s*\n/g, " ").trim();
|
|
1405
|
+
if (normalized.startsWith("import ")) {
|
|
1406
|
+
return {
|
|
1407
|
+
kind: "import",
|
|
1408
|
+
module: null,
|
|
1409
|
+
clause: normalized.slice("import ".length).trim()
|
|
1410
|
+
};
|
|
1197
1411
|
}
|
|
1198
|
-
const
|
|
1199
|
-
if (
|
|
1200
|
-
const last = sym.descriptors[sym.descriptors.length - 1];
|
|
1201
|
-
return last.name;
|
|
1202
|
-
}
|
|
1203
|
-
function leafSuffix(raw) {
|
|
1204
|
-
const parsed = parseSymbol(raw);
|
|
1205
|
-
if ("kind" in parsed && parsed.kind === "local") {
|
|
1412
|
+
const fromMatch = normalized.match(/^from\s+([.\w]+)\s+import\s+([\s\S]+)$/);
|
|
1413
|
+
if (!fromMatch) {
|
|
1206
1414
|
return null;
|
|
1207
1415
|
}
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1416
|
+
let clause = fromMatch[2].trim();
|
|
1417
|
+
if (clause.startsWith("(") && clause.endsWith(")")) {
|
|
1418
|
+
clause = clause.slice(1, -1).trim();
|
|
1419
|
+
}
|
|
1420
|
+
return {
|
|
1421
|
+
kind: "from",
|
|
1422
|
+
module: fromMatch[1],
|
|
1423
|
+
clause
|
|
1424
|
+
};
|
|
1211
1425
|
}
|
|
1212
|
-
function
|
|
1213
|
-
const
|
|
1214
|
-
|
|
1426
|
+
function parsePythonImportStatement(db, importerPath, statement, source) {
|
|
1427
|
+
const body = buildUsageBody(source, statement.start, statement.end);
|
|
1428
|
+
const normalizedClause = statement.clause.replace(/\n/g, " ").trim();
|
|
1429
|
+
if (statement.kind === "import") {
|
|
1430
|
+
return splitTopLevel(normalizedClause).flatMap((entry) => {
|
|
1431
|
+
const cleaned = entry.trim().replace(/,$/, "");
|
|
1432
|
+
if (!cleaned) return [];
|
|
1433
|
+
const [moduleName, alias] = cleaned.split(/\s+as\s+/);
|
|
1434
|
+
const importedName = moduleName.trim();
|
|
1435
|
+
const localName = (alias ?? importedName.split(".")[0] ?? importedName).trim();
|
|
1436
|
+
const sourcePath2 = resolvePythonImportPath(db, importerPath, importedName);
|
|
1437
|
+
const usedMembers = collectNamespaceMembers(body, localName);
|
|
1438
|
+
return [{
|
|
1439
|
+
importedName,
|
|
1440
|
+
localName,
|
|
1441
|
+
sourcePath: sourcePath2,
|
|
1442
|
+
kind: "namespace",
|
|
1443
|
+
used: hasIdentifierUsage(body, localName) || usedMembers.length > 0,
|
|
1444
|
+
usedMembers
|
|
1445
|
+
}];
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
const sourcePath = statement.module ? resolvePythonImportPath(db, importerPath, statement.module) : null;
|
|
1449
|
+
const results = [];
|
|
1450
|
+
for (const entry of splitTopLevel(normalizedClause)) {
|
|
1451
|
+
const cleaned = entry.trim().replace(/,$/, "");
|
|
1452
|
+
if (!cleaned) continue;
|
|
1453
|
+
if (cleaned === "*") {
|
|
1454
|
+
results.push({
|
|
1455
|
+
importedName: "*",
|
|
1456
|
+
localName: null,
|
|
1457
|
+
sourcePath,
|
|
1458
|
+
kind: "side-effect",
|
|
1459
|
+
used: true,
|
|
1460
|
+
usedMembers: []
|
|
1461
|
+
});
|
|
1462
|
+
continue;
|
|
1463
|
+
}
|
|
1464
|
+
const [importedName, alias] = cleaned.split(/\s+as\s+/);
|
|
1465
|
+
const localName = (alias ?? importedName).trim();
|
|
1466
|
+
results.push({
|
|
1467
|
+
importedName: importedName.trim(),
|
|
1468
|
+
localName,
|
|
1469
|
+
sourcePath,
|
|
1470
|
+
kind: "named",
|
|
1471
|
+
used: hasIdentifierUsage(body, localName),
|
|
1472
|
+
usedMembers: []
|
|
1473
|
+
});
|
|
1474
|
+
}
|
|
1475
|
+
return results;
|
|
1215
1476
|
}
|
|
1216
|
-
function
|
|
1217
|
-
|
|
1477
|
+
function parseImportClause(clause) {
|
|
1478
|
+
const trimmed = clause.trim().replace(/^type\s+/, "");
|
|
1479
|
+
const [first, second] = splitImportClause(trimmed);
|
|
1480
|
+
const entries = [];
|
|
1481
|
+
if (first) {
|
|
1482
|
+
entries.push(...parseImportBinding(first));
|
|
1483
|
+
}
|
|
1484
|
+
if (second) {
|
|
1485
|
+
entries.push(...parseImportBinding(second));
|
|
1486
|
+
}
|
|
1487
|
+
return entries;
|
|
1218
1488
|
}
|
|
1219
|
-
function
|
|
1220
|
-
const
|
|
1221
|
-
|
|
1222
|
-
if ("
|
|
1223
|
-
|
|
1489
|
+
function parseImportBinding(binding) {
|
|
1490
|
+
const trimmed = binding.trim();
|
|
1491
|
+
if (!trimmed) return [];
|
|
1492
|
+
if (trimmed.startsWith("{")) {
|
|
1493
|
+
const inner = trimmed.slice(1, -1).trim();
|
|
1494
|
+
if (!inner) return [];
|
|
1495
|
+
return splitTopLevel(inner).map((entry) => {
|
|
1496
|
+
const cleaned = entry.trim().replace(/^type\s+/, "");
|
|
1497
|
+
const [importedName, alias] = cleaned.split(/\s+as\s+/);
|
|
1498
|
+
return {
|
|
1499
|
+
importedName: importedName.trim(),
|
|
1500
|
+
localName: (alias ?? importedName).trim(),
|
|
1501
|
+
kind: "named"
|
|
1502
|
+
};
|
|
1503
|
+
});
|
|
1224
1504
|
}
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1505
|
+
if (trimmed.startsWith("* as ")) {
|
|
1506
|
+
return [{
|
|
1507
|
+
importedName: "*",
|
|
1508
|
+
localName: trimmed.slice(5).trim(),
|
|
1509
|
+
kind: "namespace"
|
|
1510
|
+
}];
|
|
1229
1511
|
}
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1512
|
+
return [{
|
|
1513
|
+
importedName: "default",
|
|
1514
|
+
localName: trimmed,
|
|
1515
|
+
kind: "default"
|
|
1516
|
+
}];
|
|
1517
|
+
}
|
|
1518
|
+
function splitImportClause(clause) {
|
|
1519
|
+
let depth = 0;
|
|
1520
|
+
for (let i = 0; i < clause.length; i++) {
|
|
1521
|
+
const char = clause[i];
|
|
1522
|
+
if (char === "{") depth++;
|
|
1523
|
+
if (char === "}") depth--;
|
|
1524
|
+
if (char === "," && depth === 0) {
|
|
1525
|
+
return [clause.slice(0, i).trim(), clause.slice(i + 1).trim()];
|
|
1235
1526
|
}
|
|
1236
1527
|
}
|
|
1237
|
-
return
|
|
1528
|
+
return [clause.trim(), null];
|
|
1238
1529
|
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1530
|
+
function splitTopLevel(input) {
|
|
1531
|
+
const parts = [];
|
|
1532
|
+
let depth = 0;
|
|
1533
|
+
let start = 0;
|
|
1534
|
+
for (let i = 0; i < input.length; i++) {
|
|
1535
|
+
const char = input[i];
|
|
1536
|
+
if (char === "{" || char === "[" || char === "(") depth++;
|
|
1537
|
+
if (char === "}" || char === "]" || char === ")") depth--;
|
|
1538
|
+
if (char === "," && depth === 0) {
|
|
1539
|
+
parts.push(input.slice(start, i));
|
|
1540
|
+
start = i + 1;
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
parts.push(input.slice(start));
|
|
1544
|
+
return parts;
|
|
1244
1545
|
}
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
const rows = db.all(
|
|
1249
|
-
`SELECT
|
|
1250
|
-
der.start_line,
|
|
1251
|
-
der.end_line,
|
|
1252
|
-
REPLACE(SUBSTR(gs.documentation, INSTR(gs.documentation, '|') + 1), char(10), ' ') AS sig,
|
|
1253
|
-
gs.symbol,
|
|
1254
|
-
d.relative_path
|
|
1255
|
-
FROM defn_enclosing_ranges der
|
|
1256
|
-
JOIN global_symbols gs ON der.symbol_id = gs.id
|
|
1257
|
-
JOIN documents d ON der.document_id = d.id
|
|
1258
|
-
WHERE d.relative_path LIKE ?
|
|
1259
|
-
AND ${db.localSymbolPredicate}
|
|
1260
|
-
${db.symbolNoise}
|
|
1261
|
-
ORDER BY der.start_line`,
|
|
1262
|
-
`%${filePattern}%`
|
|
1263
|
-
);
|
|
1264
|
-
return rows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({
|
|
1265
|
-
startLine: r.start_line,
|
|
1266
|
-
endLine: r.end_line,
|
|
1267
|
-
symbol: r.symbol,
|
|
1268
|
-
shortName: shortenSymbol(r.symbol),
|
|
1269
|
-
signature: cleanSignature(r.sig)
|
|
1270
|
-
}));
|
|
1546
|
+
function buildUsageBody(source, start, end) {
|
|
1547
|
+
const masked = `${source.slice(0, start)}${" ".repeat(end - start)}${source.slice(end)}`;
|
|
1548
|
+
return stripCommentsAndStrings(masked);
|
|
1271
1549
|
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
function methods(db, className) {
|
|
1275
|
-
const rows = db.all(
|
|
1276
|
-
`SELECT der.start_line, der.end_line, gs.symbol
|
|
1277
|
-
FROM global_symbols gs
|
|
1278
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
1279
|
-
WHERE gs.symbol LIKE ?
|
|
1280
|
-
AND ${db.localSymbolPredicate}
|
|
1281
|
-
AND gs.symbol LIKE '%().%'
|
|
1282
|
-
${db.symbolNoise}
|
|
1283
|
-
ORDER BY der.start_line`,
|
|
1284
|
-
`%${className}#%`
|
|
1285
|
-
);
|
|
1286
|
-
return rows.map((r) => ({
|
|
1287
|
-
startLine: r.start_line,
|
|
1288
|
-
endLine: r.end_line,
|
|
1289
|
-
name: leafName(r.symbol)
|
|
1290
|
-
}));
|
|
1550
|
+
function stripCommentsAndStrings(source) {
|
|
1551
|
+
return source.replace(/'''[\s\S]*?'''/g, maskPreservingLines).replace(/"""[\s\S]*?"""/g, maskPreservingLines).replace(/#.*$/gm, maskPreservingLines).replace(/\/\/.*$/gm, maskPreservingLines).replace(/\/\*[\s\S]*?\*\//g, maskPreservingLines).replace(/`(?:\\[\s\S]|[^`])*`/g, maskPreservingLines).replace(/'(?:\\.|[^'\\\r\n])*'/g, maskPreservingLines).replace(/"(?:\\.|[^"\\\r\n])*"/g, maskPreservingLines);
|
|
1291
1552
|
}
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
function refs(db, symbolPattern) {
|
|
1295
|
-
const rows = db.all(
|
|
1296
|
-
`SELECT DISTINCT d.relative_path, c.start_line
|
|
1297
|
-
FROM mentions m
|
|
1298
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
1299
|
-
JOIN documents d ON c.document_id = d.id
|
|
1300
|
-
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
1301
|
-
WHERE gs.symbol LIKE ?
|
|
1302
|
-
AND ${db.localSymbolPredicate}
|
|
1303
|
-
AND m.role != 1
|
|
1304
|
-
ORDER BY d.relative_path, c.start_line`,
|
|
1305
|
-
`%${symbolPattern}%`
|
|
1306
|
-
);
|
|
1307
|
-
return rows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({
|
|
1308
|
-
relativePath: r.relative_path,
|
|
1309
|
-
line: r.start_line
|
|
1310
|
-
}));
|
|
1553
|
+
function maskPreservingLines(segment) {
|
|
1554
|
+
return segment.replace(/[^\r\n]/g, " ");
|
|
1311
1555
|
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
var TEST_FILE_PATTERNS = [
|
|
1315
|
-
"%/__tests__/%",
|
|
1316
|
-
"%.test.%",
|
|
1317
|
-
"%.spec.%",
|
|
1318
|
-
"%/test/%",
|
|
1319
|
-
"%/tests/%",
|
|
1320
|
-
"%_test.%",
|
|
1321
|
-
"%_spec.%",
|
|
1322
|
-
"%/test_%.%",
|
|
1323
|
-
"%/spec_%.%"
|
|
1324
|
-
];
|
|
1325
|
-
var TEST_SUPPORT_PATH_PATTERNS = [
|
|
1326
|
-
"%/test-utils/%"
|
|
1327
|
-
];
|
|
1328
|
-
function testFileExclusionSql(alias, extraPatterns = []) {
|
|
1329
|
-
const patterns = uniquePatterns([...TEST_FILE_PATTERNS, ...extraPatterns]);
|
|
1330
|
-
return patterns.map((pattern) => `${alias}.relative_path NOT LIKE '${pattern}'`).join("\n AND ");
|
|
1556
|
+
function hasIdentifierUsage(body, identifier) {
|
|
1557
|
+
return new RegExp(`\\b${escapeRegex(identifier)}\\b`, "m").test(body);
|
|
1331
1558
|
}
|
|
1332
|
-
function
|
|
1333
|
-
const
|
|
1334
|
-
const
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
JOIN documents d1 ON c.document_id = d1.id
|
|
1341
|
-
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
1342
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
1343
|
-
JOIN documents d2 ON der.document_id = d2.id
|
|
1344
|
-
WHERE d1.id != d2.id
|
|
1345
|
-
AND m.role != 1
|
|
1346
|
-
${db.pathExclusionsFor("d1", "d2")}
|
|
1347
|
-
${scopeFilter}`
|
|
1348
|
-
);
|
|
1349
|
-
const graph = /* @__PURE__ */ new Map();
|
|
1350
|
-
for (const edge of edges) {
|
|
1351
|
-
if (db.isIgnored(edge.from_file) || db.isIgnored(edge.to_file)) continue;
|
|
1352
|
-
if (!graph.has(edge.from_file)) graph.set(edge.from_file, /* @__PURE__ */ new Set());
|
|
1353
|
-
graph.get(edge.from_file).add(edge.to_file);
|
|
1559
|
+
function collectNamespaceMembers(body, namespaceName) {
|
|
1560
|
+
const members2 = /* @__PURE__ */ new Set();
|
|
1561
|
+
const regex = new RegExp(`\\b${escapeRegex(namespaceName)}\\s*\\.\\s*([A-Za-z_$][\\w$]*)`, "g");
|
|
1562
|
+
for (const match of body.matchAll(regex)) {
|
|
1563
|
+
const member = match[1];
|
|
1564
|
+
if (member) {
|
|
1565
|
+
members2.add(member);
|
|
1566
|
+
}
|
|
1354
1567
|
}
|
|
1355
|
-
return
|
|
1568
|
+
return [...members2];
|
|
1356
1569
|
}
|
|
1357
|
-
function
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
);
|
|
1375
|
-
if (row && !db.isIgnored(row.relative_path)) {
|
|
1376
|
-
return {
|
|
1377
|
-
symbolId: row.id,
|
|
1378
|
-
symbol: row.symbol,
|
|
1379
|
-
documentId: row.document_id,
|
|
1380
|
-
startLine: row.start_line,
|
|
1381
|
-
endLine: row.end_line,
|
|
1382
|
-
relativePath: row.relative_path
|
|
1383
|
-
};
|
|
1570
|
+
function resolveImportPath(db, importerPath, specifier) {
|
|
1571
|
+
if (isPythonSourcePath(importerPath)) {
|
|
1572
|
+
return resolvePythonImportPath(db, importerPath, specifier);
|
|
1573
|
+
}
|
|
1574
|
+
return resolveJavaScriptImportPath(db, importerPath, specifier);
|
|
1575
|
+
}
|
|
1576
|
+
function resolveJavaScriptImportPath(db, importerPath, specifier) {
|
|
1577
|
+
if (!specifier.startsWith(".") && !specifier.startsWith("/")) {
|
|
1578
|
+
return null;
|
|
1579
|
+
}
|
|
1580
|
+
const importerDir = dirname2(join6(db.config.projectRoot, importerPath));
|
|
1581
|
+
const absolute = resolve2(importerDir, specifier);
|
|
1582
|
+
const indexedPaths = getIndexedPaths(db);
|
|
1583
|
+
for (const candidate of candidateImportPaths(absolute)) {
|
|
1584
|
+
const relativeCandidate = normalizePath(relative2(db.config.projectRoot, candidate));
|
|
1585
|
+
if (indexedPaths.has(relativeCandidate) || existsSync6(candidate)) {
|
|
1586
|
+
return relativeCandidate;
|
|
1384
1587
|
}
|
|
1385
1588
|
}
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
const
|
|
1393
|
-
if (
|
|
1394
|
-
|
|
1395
|
-
|
|
1589
|
+
return normalizePath(relative2(db.config.projectRoot, absolute));
|
|
1590
|
+
}
|
|
1591
|
+
function resolvePythonImportPath(db, importerPath, specifier) {
|
|
1592
|
+
const indexedPaths = getIndexedPaths(db);
|
|
1593
|
+
let basePath;
|
|
1594
|
+
if (specifier.startsWith(".")) {
|
|
1595
|
+
const match = specifier.match(/^(\.+)(.*)$/);
|
|
1596
|
+
if (!match) return null;
|
|
1597
|
+
const dots = match[1].length;
|
|
1598
|
+
const remainder = match[2].replace(/^\./, "");
|
|
1599
|
+
let baseDir = dirname2(join6(db.config.projectRoot, importerPath));
|
|
1600
|
+
for (let i = 1; i < dots; i++) {
|
|
1601
|
+
baseDir = dirname2(baseDir);
|
|
1396
1602
|
}
|
|
1603
|
+
basePath = remainder ? resolve2(baseDir, remainder.replace(/\./g, "/")) : baseDir;
|
|
1604
|
+
} else {
|
|
1605
|
+
basePath = resolve2(db.config.projectRoot, specifier.replace(/\./g, "/"));
|
|
1397
1606
|
}
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
startLine: best.row.start_line,
|
|
1404
|
-
endLine: best.row.end_line,
|
|
1405
|
-
relativePath: best.row.relative_path
|
|
1406
|
-
};
|
|
1607
|
+
for (const candidate of pythonCandidateImportPaths(basePath)) {
|
|
1608
|
+
const relativeCandidate = normalizePath(relative2(db.config.projectRoot, candidate));
|
|
1609
|
+
if (indexedPaths.has(relativeCandidate) || existsSync6(candidate)) {
|
|
1610
|
+
return relativeCandidate;
|
|
1611
|
+
}
|
|
1407
1612
|
}
|
|
1408
1613
|
return null;
|
|
1409
1614
|
}
|
|
1410
|
-
function
|
|
1411
|
-
|
|
1615
|
+
function pythonCandidateImportPaths(basePath) {
|
|
1616
|
+
const ext = extname(basePath);
|
|
1617
|
+
if (PYTHON_SOURCE_EXTENSIONS.includes(ext)) {
|
|
1618
|
+
return [basePath];
|
|
1619
|
+
}
|
|
1620
|
+
return [
|
|
1621
|
+
`${basePath}.py`,
|
|
1622
|
+
`${basePath}.pyi`,
|
|
1623
|
+
join6(basePath, "__init__.py"),
|
|
1624
|
+
join6(basePath, "__init__.pyi")
|
|
1625
|
+
];
|
|
1412
1626
|
}
|
|
1413
|
-
function
|
|
1414
|
-
const
|
|
1415
|
-
const
|
|
1416
|
-
|
|
1627
|
+
function candidateImportPaths(absolute) {
|
|
1628
|
+
const ext = extname(absolute);
|
|
1629
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
1630
|
+
if (ext) {
|
|
1631
|
+
candidates.add(absolute);
|
|
1632
|
+
for (const sourceExt of SOURCE_EXTENSIONS) {
|
|
1633
|
+
candidates.add(absolute.slice(0, -ext.length) + sourceExt);
|
|
1634
|
+
}
|
|
1635
|
+
} else {
|
|
1636
|
+
for (const sourceExt of SOURCE_EXTENSIONS) {
|
|
1637
|
+
candidates.add(`${absolute}${sourceExt}`);
|
|
1638
|
+
candidates.add(join6(absolute, `index${sourceExt}`));
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
return [...candidates];
|
|
1417
1642
|
}
|
|
1418
|
-
function
|
|
1419
|
-
const
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
JOIN documents d ON der.document_id = d.id
|
|
1431
|
-
WHERE ${tokenClauses.join("\n AND ")}
|
|
1432
|
-
${db.pathExclusionsFor("d")}
|
|
1433
|
-
LIMIT 200`,
|
|
1434
|
-
...params
|
|
1643
|
+
function getIndexedPaths(db) {
|
|
1644
|
+
const cached = INDEXED_PATH_CACHE.get(db);
|
|
1645
|
+
if (cached) {
|
|
1646
|
+
return cached;
|
|
1647
|
+
}
|
|
1648
|
+
const paths = new Set(
|
|
1649
|
+
db.all(
|
|
1650
|
+
`SELECT relative_path
|
|
1651
|
+
FROM documents
|
|
1652
|
+
WHERE 1 = 1
|
|
1653
|
+
${db.pathExclusionsFor("documents")}`
|
|
1654
|
+
).map((row) => normalizePath(row.relative_path)).filter((relativePath) => !db.isIgnored(relativePath))
|
|
1435
1655
|
);
|
|
1656
|
+
INDEXED_PATH_CACHE.set(db, paths);
|
|
1657
|
+
return paths;
|
|
1436
1658
|
}
|
|
1437
|
-
function
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
if (
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1659
|
+
function getCachedMap(cache, db) {
|
|
1660
|
+
let map = cache.get(db);
|
|
1661
|
+
if (!map) {
|
|
1662
|
+
map = /* @__PURE__ */ new Map();
|
|
1663
|
+
cache.set(db, map);
|
|
1664
|
+
}
|
|
1665
|
+
return map;
|
|
1666
|
+
}
|
|
1667
|
+
function normalizePath(path2) {
|
|
1668
|
+
return path2.replace(/\\/g, "/");
|
|
1669
|
+
}
|
|
1670
|
+
function isJavaScriptSourcePath(relativePath) {
|
|
1671
|
+
return SOURCE_EXTENSIONS.includes(extname(relativePath).toLowerCase());
|
|
1672
|
+
}
|
|
1673
|
+
function isPythonSourcePath(relativePath) {
|
|
1674
|
+
return PYTHON_SOURCE_EXTENSIONS.includes(extname(relativePath).toLowerCase());
|
|
1675
|
+
}
|
|
1676
|
+
function getSourceText(db, relativePath) {
|
|
1677
|
+
const cache = getCachedMap(SOURCE_TEXT_CACHE, db);
|
|
1678
|
+
const normalized = normalizePath(relativePath);
|
|
1679
|
+
const cached = cache.get(normalized);
|
|
1680
|
+
if (typeof cached === "string") {
|
|
1681
|
+
return cached;
|
|
1682
|
+
}
|
|
1683
|
+
const fullPath = join6(db.config.projectRoot, normalized);
|
|
1684
|
+
if (!existsSync6(fullPath)) {
|
|
1685
|
+
cache.set(normalized, "");
|
|
1686
|
+
return "";
|
|
1687
|
+
}
|
|
1688
|
+
const source = readFileSync4(fullPath, "utf-8");
|
|
1689
|
+
cache.set(normalized, source);
|
|
1690
|
+
return source;
|
|
1691
|
+
}
|
|
1692
|
+
function pythonParenBalance(value) {
|
|
1693
|
+
let balance = 0;
|
|
1694
|
+
for (const char of value) {
|
|
1695
|
+
if (char === "(") balance++;
|
|
1696
|
+
if (char === ")") balance--;
|
|
1697
|
+
}
|
|
1698
|
+
return balance;
|
|
1699
|
+
}
|
|
1700
|
+
function escapeRegex(value) {
|
|
1701
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
// src/symbol-parser.ts
|
|
1705
|
+
var SUFFIX_MAP = {
|
|
1706
|
+
"/": "namespace",
|
|
1707
|
+
"#": "type",
|
|
1708
|
+
".": "term",
|
|
1709
|
+
"[": "type-param",
|
|
1710
|
+
":": "meta",
|
|
1711
|
+
"!": "macro"
|
|
1712
|
+
};
|
|
1713
|
+
function parseSymbol(raw) {
|
|
1714
|
+
if (raw.startsWith("local ")) {
|
|
1715
|
+
return { kind: "local", id: raw.slice(6), raw };
|
|
1716
|
+
}
|
|
1717
|
+
const parts = raw.split(" ");
|
|
1718
|
+
if (parts.length < 4) {
|
|
1719
|
+
return {
|
|
1720
|
+
scheme: parts[0] ?? "",
|
|
1721
|
+
manager: parts[1] ?? "",
|
|
1722
|
+
packageName: parts[2] ?? "",
|
|
1723
|
+
version: "",
|
|
1724
|
+
descriptors: [],
|
|
1725
|
+
raw
|
|
1726
|
+
};
|
|
1727
|
+
}
|
|
1728
|
+
const scheme = parts[0];
|
|
1729
|
+
const manager = parts[1];
|
|
1730
|
+
let restAfterManager = raw.slice(scheme.length + 1 + manager.length + 1);
|
|
1731
|
+
let packageName;
|
|
1732
|
+
if (restAfterManager.startsWith("`")) {
|
|
1733
|
+
const closingTick = restAfterManager.indexOf("`", 1);
|
|
1734
|
+
if (closingTick === -1) {
|
|
1735
|
+
packageName = restAfterManager.slice(1);
|
|
1736
|
+
restAfterManager = "";
|
|
1737
|
+
} else {
|
|
1738
|
+
packageName = restAfterManager.slice(1, closingTick);
|
|
1739
|
+
restAfterManager = restAfterManager.slice(closingTick + 2);
|
|
1740
|
+
}
|
|
1741
|
+
} else {
|
|
1742
|
+
const spaceIdx = restAfterManager.indexOf(" ");
|
|
1743
|
+
if (spaceIdx === -1) {
|
|
1744
|
+
packageName = restAfterManager;
|
|
1745
|
+
restAfterManager = "";
|
|
1746
|
+
} else {
|
|
1747
|
+
packageName = restAfterManager.slice(0, spaceIdx);
|
|
1748
|
+
restAfterManager = restAfterManager.slice(spaceIdx + 1);
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
let version;
|
|
1752
|
+
const versionSpaceIdx = restAfterManager.indexOf(" ");
|
|
1753
|
+
if (versionSpaceIdx === -1) {
|
|
1754
|
+
version = restAfterManager;
|
|
1755
|
+
restAfterManager = "";
|
|
1756
|
+
} else {
|
|
1757
|
+
version = restAfterManager.slice(0, versionSpaceIdx);
|
|
1758
|
+
restAfterManager = restAfterManager.slice(versionSpaceIdx + 1);
|
|
1759
|
+
}
|
|
1760
|
+
const descriptors = parseDescriptors(restAfterManager);
|
|
1761
|
+
return { scheme, manager, packageName, version, descriptors, raw };
|
|
1762
|
+
}
|
|
1763
|
+
function parseDescriptors(input) {
|
|
1764
|
+
const descriptors = [];
|
|
1765
|
+
let i = 0;
|
|
1766
|
+
while (i < input.length) {
|
|
1767
|
+
if (input[i] === "[") {
|
|
1768
|
+
const closeBracket = input.indexOf("]", i + 1);
|
|
1769
|
+
if (closeBracket === -1) {
|
|
1770
|
+
descriptors.push({ name: input.slice(i + 1), suffix: "type-param" });
|
|
1771
|
+
break;
|
|
1772
|
+
}
|
|
1773
|
+
descriptors.push({ name: input.slice(i + 1, closeBracket), suffix: "type-param" });
|
|
1774
|
+
i = closeBracket + 1;
|
|
1775
|
+
continue;
|
|
1776
|
+
}
|
|
1777
|
+
if (input[i] === "(" && (descriptors.length === 0 || i === 0 || isSuffixChar(input[i - 1]))) {
|
|
1778
|
+
const closeParen = input.indexOf(")", i + 1);
|
|
1779
|
+
if (closeParen !== -1 && input[closeParen + 1] !== ".") {
|
|
1780
|
+
descriptors.push({ name: input.slice(i + 1, closeParen), suffix: "parameter" });
|
|
1781
|
+
i = closeParen + 1;
|
|
1782
|
+
continue;
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
let name;
|
|
1786
|
+
if (input[i] === "`") {
|
|
1787
|
+
const closingTick = input.indexOf("`", i + 1);
|
|
1788
|
+
if (closingTick === -1) {
|
|
1789
|
+
name = input.slice(i + 1);
|
|
1790
|
+
i = input.length;
|
|
1791
|
+
descriptors.push({ name, suffix: "term" });
|
|
1792
|
+
break;
|
|
1793
|
+
}
|
|
1794
|
+
name = input.slice(i + 1, closingTick);
|
|
1795
|
+
i = closingTick + 1;
|
|
1796
|
+
} else {
|
|
1797
|
+
const start = i;
|
|
1798
|
+
while (i < input.length && !isSuffixChar(input[i])) {
|
|
1799
|
+
i++;
|
|
1800
|
+
}
|
|
1801
|
+
name = input.slice(start, i);
|
|
1802
|
+
}
|
|
1803
|
+
if (i >= input.length) {
|
|
1804
|
+
if (name) descriptors.push({ name, suffix: "term" });
|
|
1805
|
+
break;
|
|
1806
|
+
}
|
|
1807
|
+
const char = input[i];
|
|
1808
|
+
if (char === "(") {
|
|
1809
|
+
const closeParen = input.indexOf(")", i + 1);
|
|
1810
|
+
if (closeParen !== -1 && input[closeParen + 1] === ".") {
|
|
1811
|
+
descriptors.push({ name, suffix: "method" });
|
|
1812
|
+
i = closeParen + 2;
|
|
1813
|
+
} else if (closeParen !== -1) {
|
|
1814
|
+
descriptors.push({ name, suffix: "method" });
|
|
1815
|
+
i = closeParen + 1;
|
|
1816
|
+
} else {
|
|
1817
|
+
descriptors.push({ name, suffix: "term" });
|
|
1818
|
+
i++;
|
|
1819
|
+
}
|
|
1820
|
+
} else {
|
|
1821
|
+
const suffix = SUFFIX_MAP[char];
|
|
1822
|
+
if (suffix) {
|
|
1823
|
+
descriptors.push({ name, suffix });
|
|
1824
|
+
i += 1;
|
|
1825
|
+
} else {
|
|
1826
|
+
i += 1;
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
return descriptors;
|
|
1831
|
+
}
|
|
1832
|
+
function isSuffixChar(c) {
|
|
1833
|
+
return c === "/" || c === "#" || c === "." || c === "(" || c === "[" || c === ":" || c === "!";
|
|
1834
|
+
}
|
|
1835
|
+
function shortenSymbol(raw) {
|
|
1836
|
+
const parsed = parseSymbol(raw);
|
|
1837
|
+
if ("kind" in parsed && parsed.kind === "local") {
|
|
1838
|
+
return `local:${parsed.id}`;
|
|
1839
|
+
}
|
|
1840
|
+
const sym = parsed;
|
|
1841
|
+
if (sym.descriptors.length === 0) return sym.raw;
|
|
1842
|
+
const parts = [];
|
|
1843
|
+
for (const desc of sym.descriptors) {
|
|
1844
|
+
let name = desc.name;
|
|
1845
|
+
if (desc.suffix === "namespace") {
|
|
1846
|
+
name = name.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/, "").replace(/\.(py|pyi)$/, "").replace(/\.(rs)$/, "").replace(/\.(java|scala|kt|kts)$/, "").replace(/\.(rb)$/, "").replace(/\.(go)$/, "").replace(/\.(cs|vb)$/, "").replace(/\.(dart)$/, "").replace(/\.(php)$/, "").replace(/\.(c|cc|cpp|cxx|h|hpp)$/, "");
|
|
1847
|
+
}
|
|
1848
|
+
if (!name) continue;
|
|
1849
|
+
if (desc.suffix === "method") {
|
|
1850
|
+
parts.push(`${name}()`);
|
|
1851
|
+
} else {
|
|
1852
|
+
parts.push(name);
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
return parts.join(":");
|
|
1856
|
+
}
|
|
1857
|
+
function leafName(raw) {
|
|
1858
|
+
const parsed = parseSymbol(raw);
|
|
1859
|
+
if ("kind" in parsed && parsed.kind === "local") {
|
|
1860
|
+
return parsed.id;
|
|
1861
|
+
}
|
|
1862
|
+
const sym = parsed;
|
|
1863
|
+
if (sym.descriptors.length === 0) return "";
|
|
1864
|
+
const last = sym.descriptors[sym.descriptors.length - 1];
|
|
1865
|
+
return last.name;
|
|
1866
|
+
}
|
|
1867
|
+
function leafSuffix(raw) {
|
|
1868
|
+
const parsed = parseSymbol(raw);
|
|
1869
|
+
if ("kind" in parsed && parsed.kind === "local") {
|
|
1870
|
+
return null;
|
|
1871
|
+
}
|
|
1872
|
+
const sym = parsed;
|
|
1873
|
+
const last = sym.descriptors[sym.descriptors.length - 1];
|
|
1874
|
+
return last?.suffix ?? null;
|
|
1875
|
+
}
|
|
1876
|
+
function isFunctionLikeSymbol(raw) {
|
|
1877
|
+
const suffix = leafSuffix(raw);
|
|
1878
|
+
return suffix === "method" || suffix === "term";
|
|
1879
|
+
}
|
|
1880
|
+
function isModuleLikeSymbol(raw) {
|
|
1881
|
+
return leafSuffix(raw) === "namespace";
|
|
1882
|
+
}
|
|
1883
|
+
function isDirectChildSymbol(parentRaw, candidateRaw) {
|
|
1884
|
+
const parent = parseSymbol(parentRaw);
|
|
1885
|
+
const candidate = parseSymbol(candidateRaw);
|
|
1886
|
+
if ("kind" in parent || "kind" in candidate) {
|
|
1887
|
+
return false;
|
|
1888
|
+
}
|
|
1889
|
+
const parentDescriptors = parent.descriptors;
|
|
1890
|
+
const candidateDescriptors = candidate.descriptors;
|
|
1891
|
+
if (candidateDescriptors.length !== parentDescriptors.length + 1) {
|
|
1892
|
+
return false;
|
|
1893
|
+
}
|
|
1894
|
+
for (let i = 0; i < parentDescriptors.length; i++) {
|
|
1895
|
+
const parentDesc = parentDescriptors[i];
|
|
1896
|
+
const candidateDesc = candidateDescriptors[i];
|
|
1897
|
+
if (parentDesc.name !== candidateDesc.name || parentDesc.suffix !== candidateDesc.suffix) {
|
|
1898
|
+
return false;
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
return true;
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
// src/query-support.ts
|
|
1905
|
+
var FILE_DEFINITION_CACHE = /* @__PURE__ */ new WeakMap();
|
|
1906
|
+
var TEST_FILE_PATTERNS = [
|
|
1907
|
+
"%/__tests__/%",
|
|
1908
|
+
"%.test.%",
|
|
1909
|
+
"%.spec.%",
|
|
1910
|
+
"%/test/%",
|
|
1911
|
+
"%/tests/%",
|
|
1912
|
+
"%_test.%",
|
|
1913
|
+
"%_spec.%",
|
|
1914
|
+
"%/test_%.%",
|
|
1915
|
+
"%/spec_%.%"
|
|
1916
|
+
];
|
|
1917
|
+
var TEST_SUPPORT_PATH_PATTERNS = [
|
|
1918
|
+
"%/test-utils/%"
|
|
1919
|
+
];
|
|
1920
|
+
function testFileExclusionSql(alias, extraPatterns = []) {
|
|
1921
|
+
const patterns = uniquePatterns([...TEST_FILE_PATTERNS, ...extraPatterns]);
|
|
1922
|
+
return patterns.map((pattern) => `${alias}.relative_path NOT LIKE '${pattern}'`).join("\n AND ");
|
|
1923
|
+
}
|
|
1924
|
+
function buildFileDepGraph(db, scope) {
|
|
1925
|
+
const scopeFilter = scope ? `AND d1.relative_path LIKE '%${scope}%'` : "";
|
|
1926
|
+
const edges = db.all(
|
|
1927
|
+
`SELECT DISTINCT
|
|
1928
|
+
d1.relative_path AS from_file,
|
|
1929
|
+
d2.relative_path AS to_file
|
|
1930
|
+
FROM mentions m
|
|
1931
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
1932
|
+
JOIN documents d1 ON c.document_id = d1.id
|
|
1933
|
+
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
1934
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
1935
|
+
JOIN documents d2 ON der.document_id = d2.id
|
|
1936
|
+
WHERE d1.id != d2.id
|
|
1937
|
+
AND m.role != 1
|
|
1938
|
+
${db.pathExclusionsFor("d1", "d2")}
|
|
1939
|
+
${scopeFilter}`
|
|
1940
|
+
);
|
|
1941
|
+
const graph = /* @__PURE__ */ new Map();
|
|
1942
|
+
for (const edge of edges) {
|
|
1943
|
+
if (db.isIgnored(edge.from_file) || db.isIgnored(edge.to_file)) continue;
|
|
1944
|
+
if (!graph.has(edge.from_file)) graph.set(edge.from_file, /* @__PURE__ */ new Set());
|
|
1945
|
+
graph.get(edge.from_file).add(edge.to_file);
|
|
1946
|
+
}
|
|
1947
|
+
return graph;
|
|
1948
|
+
}
|
|
1949
|
+
function findFirstSymbolMatch(db, symbolPattern) {
|
|
1950
|
+
const fileLineMatch = symbolPattern.match(/^(.+):(\d+)-(\d+)$/);
|
|
1951
|
+
if (fileLineMatch) {
|
|
1952
|
+
const [, filePath, startStr, endStr] = fileLineMatch;
|
|
1953
|
+
const row = db.get(
|
|
1954
|
+
`SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path
|
|
1955
|
+
FROM global_symbols gs
|
|
1956
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
1957
|
+
JOIN documents d ON der.document_id = d.id
|
|
1958
|
+
WHERE d.relative_path LIKE ?
|
|
1959
|
+
AND der.start_line <= ? AND der.end_line >= ?
|
|
1960
|
+
${db.pathExclusionsFor("d")}
|
|
1961
|
+
ORDER BY (der.end_line - der.start_line) ASC
|
|
1962
|
+
LIMIT 1`,
|
|
1963
|
+
`%${filePath}%`,
|
|
1964
|
+
parseInt(startStr, 10),
|
|
1965
|
+
parseInt(endStr, 10)
|
|
1966
|
+
);
|
|
1967
|
+
if (row && !db.isIgnored(row.relative_path)) {
|
|
1968
|
+
return {
|
|
1969
|
+
symbolId: row.id,
|
|
1970
|
+
symbol: row.symbol,
|
|
1971
|
+
documentId: row.document_id,
|
|
1972
|
+
startLine: row.start_line,
|
|
1973
|
+
endLine: row.end_line,
|
|
1974
|
+
relativePath: row.relative_path
|
|
1975
|
+
};
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
const cleaned = normalizeLookupPattern(symbolPattern);
|
|
1979
|
+
const tokens = lookupTokens(symbolPattern);
|
|
1980
|
+
const candidates = getSymbolLookupCandidates(db, tokens);
|
|
1981
|
+
let best = null;
|
|
1982
|
+
for (const row of candidates) {
|
|
1983
|
+
if (db.isIgnored(row.relative_path)) continue;
|
|
1984
|
+
const score = scoreSymbolCandidate(row, symbolPattern, cleaned, tokens);
|
|
1985
|
+
if (score <= 0) continue;
|
|
1986
|
+
if (!best || score > best.score) {
|
|
1987
|
+
best = { row, score };
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
if (best) {
|
|
1991
|
+
return {
|
|
1992
|
+
symbolId: best.row.id,
|
|
1993
|
+
symbol: best.row.symbol,
|
|
1994
|
+
documentId: best.row.document_id,
|
|
1995
|
+
startLine: best.row.start_line,
|
|
1996
|
+
endLine: best.row.end_line,
|
|
1997
|
+
relativePath: best.row.relative_path
|
|
1998
|
+
};
|
|
1999
|
+
}
|
|
2000
|
+
return null;
|
|
2001
|
+
}
|
|
2002
|
+
function findExactSymbolMatch(db, symbol) {
|
|
2003
|
+
const row = db.get(
|
|
2004
|
+
`SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path
|
|
2005
|
+
FROM global_symbols gs
|
|
2006
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
2007
|
+
JOIN documents d ON der.document_id = d.id
|
|
2008
|
+
WHERE gs.symbol = ?
|
|
2009
|
+
${db.pathExclusionsFor("d")}
|
|
2010
|
+
ORDER BY d.relative_path, der.start_line
|
|
2011
|
+
LIMIT 1`,
|
|
2012
|
+
symbol
|
|
2013
|
+
);
|
|
2014
|
+
if (!row || db.isIgnored(row.relative_path)) {
|
|
2015
|
+
return null;
|
|
2016
|
+
}
|
|
2017
|
+
return {
|
|
2018
|
+
symbolId: row.id,
|
|
2019
|
+
symbol: row.symbol,
|
|
2020
|
+
documentId: row.document_id,
|
|
2021
|
+
startLine: row.start_line,
|
|
2022
|
+
endLine: row.end_line,
|
|
2023
|
+
relativePath: row.relative_path
|
|
2024
|
+
};
|
|
2025
|
+
}
|
|
2026
|
+
function resolveIndexedFile(db, filePattern) {
|
|
2027
|
+
return resolveDocumentCandidates(db, filePattern, { allowMultiple: false })[0]?.relativePath ?? null;
|
|
2028
|
+
}
|
|
2029
|
+
function resolveIndexedPaths(db, filePattern) {
|
|
2030
|
+
return resolveDocumentCandidates(db, filePattern, { allowMultiple: true }).map((candidate) => candidate.relativePath);
|
|
2031
|
+
}
|
|
2032
|
+
function normalizeLookupPattern(symbolPattern) {
|
|
2033
|
+
return symbolPattern.trim().replace(/\(\)$/, "").replace(/\(.*$/, "");
|
|
2034
|
+
}
|
|
2035
|
+
function lookupTokens(symbolPattern) {
|
|
2036
|
+
const cleaned = normalizeLookupPattern(symbolPattern);
|
|
2037
|
+
const tokens = cleaned.split(/[^A-Za-z0-9_]+/).map((token) => token.trim()).filter((token) => token.length > 0);
|
|
2038
|
+
return tokens.length > 0 ? [...new Set(tokens)] : [cleaned];
|
|
2039
|
+
}
|
|
2040
|
+
function getSymbolLookupCandidates(db, tokens) {
|
|
2041
|
+
const tokenClauses = tokens.map(
|
|
2042
|
+
() => `(gs.symbol LIKE ? OR d.relative_path LIKE ? OR COALESCE(gs.display_name, '') LIKE ?)`
|
|
2043
|
+
);
|
|
2044
|
+
const params = tokens.flatMap((token) => {
|
|
2045
|
+
const like = `%${token}%`;
|
|
2046
|
+
return [like, like, like];
|
|
2047
|
+
});
|
|
2048
|
+
return db.all(
|
|
2049
|
+
`SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path, gs.display_name
|
|
2050
|
+
FROM global_symbols gs
|
|
2051
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
2052
|
+
JOIN documents d ON der.document_id = d.id
|
|
2053
|
+
WHERE ${tokenClauses.join("\n AND ")}
|
|
2054
|
+
${db.pathExclusionsFor("d")}
|
|
2055
|
+
LIMIT 200`,
|
|
2056
|
+
...params
|
|
2057
|
+
);
|
|
2058
|
+
}
|
|
2059
|
+
function scoreSymbolCandidate(row, originalPattern, cleanedPattern, tokens) {
|
|
2060
|
+
const original = originalPattern.toLowerCase();
|
|
2061
|
+
const cleaned = cleanedPattern.toLowerCase();
|
|
2062
|
+
const noParens = cleaned.replace(/\(\)$/, "");
|
|
2063
|
+
const raw = row.symbol.toLowerCase();
|
|
2064
|
+
const short = shortenSymbol(row.symbol).toLowerCase();
|
|
2065
|
+
const leaf = leafName(row.symbol).toLowerCase();
|
|
2066
|
+
const display = (row.display_name ?? "").toLowerCase();
|
|
2067
|
+
const path2 = row.relative_path.toLowerCase();
|
|
2068
|
+
const looksPathLike = /[/:.]/.test(cleanedPattern);
|
|
2069
|
+
let score = 0;
|
|
2070
|
+
if (raw === original || raw === cleaned) score += 1e3;
|
|
2071
|
+
if (short === original || short === cleaned) score += 950;
|
|
2072
|
+
if (path2 === original || path2 === cleaned) score += 925;
|
|
2073
|
+
if (path2.endsWith(`/${cleaned}`) || path2.endsWith(`/${original}`)) score += 875;
|
|
2074
|
+
if (display === noParens) score += 850;
|
|
2075
|
+
if (leaf === noParens) score += 825;
|
|
2076
|
+
if (`${leaf}()` === original || `${leaf}()` === cleaned) score += 820;
|
|
2077
|
+
if (short.endsWith(`:${cleaned}`) || short.endsWith(`:${noParens}`) || short.endsWith(`:${noParens}()`)) score += 800;
|
|
2078
|
+
if (raw.includes(cleaned)) score += 120;
|
|
2079
|
+
if (short.includes(cleaned)) score += 140;
|
|
2080
|
+
if (path2.includes(cleaned)) score += 140;
|
|
2081
|
+
if (display.includes(cleaned)) score += 110;
|
|
2082
|
+
if (tokens.every((token) => {
|
|
2083
|
+
const lower = token.toLowerCase();
|
|
2084
|
+
return raw.includes(lower) || short.includes(lower) || path2.includes(lower) || display.includes(lower);
|
|
2085
|
+
})) {
|
|
2086
|
+
score += 100 + tokens.length * 15;
|
|
1465
2087
|
}
|
|
1466
2088
|
if (isFunctionLikeSymbol(row.symbol) && leaf === noParens) {
|
|
1467
2089
|
score += 60;
|
|
@@ -1494,820 +2116,1055 @@ function getCalleeRowsForSymbol(db, symbol, opts = {}) {
|
|
|
1494
2116
|
${opts.limit ? "LIMIT ?" : ""}`,
|
|
1495
2117
|
...calleeQueryParams(symbol, opts.limit)
|
|
1496
2118
|
);
|
|
1497
|
-
|
|
2119
|
+
const primary = rows.filter((row) => !db.isIgnored(row.file)).map((row) => ({
|
|
1498
2120
|
symbol: row.symbol,
|
|
1499
2121
|
file: row.file,
|
|
1500
2122
|
chunkId: row.chunk_id
|
|
1501
2123
|
}));
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
symbol.documentId,
|
|
1506
|
-
symbol.startLine,
|
|
1507
|
-
symbol.endLine,
|
|
1508
|
-
symbol.symbolId
|
|
1509
|
-
];
|
|
1510
|
-
if (typeof limit === "number") {
|
|
1511
|
-
params.push(limit);
|
|
2124
|
+
const sourceFallback = getSourceBackedCalleeRows(db, symbol, opts.limit);
|
|
2125
|
+
if (sourceFallback.length === 0) {
|
|
2126
|
+
return primary;
|
|
1512
2127
|
}
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
2128
|
+
if (primary.length === 0) {
|
|
2129
|
+
return applyLimit(sourceFallback, opts.limit);
|
|
2130
|
+
}
|
|
2131
|
+
const seen = new Set(primary.map((row) => `${row.symbol}|${row.file}`));
|
|
2132
|
+
const merged = [...primary];
|
|
2133
|
+
for (const row of sourceFallback) {
|
|
2134
|
+
const key = `${row.symbol}|${row.file}`;
|
|
2135
|
+
if (seen.has(key)) continue;
|
|
2136
|
+
seen.add(key);
|
|
2137
|
+
merged.push(row);
|
|
2138
|
+
}
|
|
2139
|
+
return applyLimit(merged, opts.limit);
|
|
1517
2140
|
}
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
function trace(db, symbolPattern) {
|
|
1521
|
-
const match = findFirstSymbolMatch(db, symbolPattern);
|
|
2141
|
+
function getCallerRowsForSymbol(db, symbol, opts = {}) {
|
|
2142
|
+
const match = getFullSymbolMatch(db, symbol);
|
|
1522
2143
|
if (!match) {
|
|
1523
|
-
return
|
|
2144
|
+
return [];
|
|
1524
2145
|
}
|
|
1525
|
-
const
|
|
1526
|
-
`SELECT
|
|
1527
|
-
REPLACE(SUBSTR(gs.documentation, INSTR(gs.documentation, '|') + 1), char(10), ' ') AS sig
|
|
1528
|
-
FROM global_symbols gs
|
|
1529
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
1530
|
-
JOIN documents d ON der.document_id = d.id
|
|
1531
|
-
WHERE gs.id = ?
|
|
1532
|
-
ORDER BY d.relative_path, der.start_line
|
|
1533
|
-
LIMIT 10`,
|
|
1534
|
-
match.symbolId
|
|
1535
|
-
);
|
|
1536
|
-
const definitions = defRows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({
|
|
1537
|
-
relativePath: r.relative_path,
|
|
1538
|
-
startLine: r.start_line,
|
|
1539
|
-
endLine: r.end_line,
|
|
1540
|
-
signature: cleanSignature(r.sig)
|
|
1541
|
-
}));
|
|
1542
|
-
const refRows = db.all(
|
|
1543
|
-
`SELECT DISTINCT d.relative_path
|
|
1544
|
-
FROM mentions m
|
|
1545
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
1546
|
-
JOIN documents d ON c.document_id = d.id
|
|
1547
|
-
WHERE m.symbol_id = ?
|
|
1548
|
-
AND m.role != 1
|
|
1549
|
-
ORDER BY d.relative_path`,
|
|
1550
|
-
match.symbolId
|
|
1551
|
-
);
|
|
1552
|
-
const referencedBy = refRows.map((r) => r.relative_path).filter((p) => !db.isIgnored(p));
|
|
1553
|
-
return { definitions, referencedBy };
|
|
1554
|
-
}
|
|
1555
|
-
|
|
1556
|
-
// src/queries/deps.ts
|
|
1557
|
-
function deps(db, filePattern) {
|
|
1558
|
-
const rows = db.all(
|
|
1559
|
-
`SELECT DISTINCT d2.relative_path
|
|
1560
|
-
FROM mentions m
|
|
1561
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
1562
|
-
JOIN documents d1 ON c.document_id = d1.id
|
|
1563
|
-
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
1564
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
1565
|
-
JOIN documents d2 ON der.document_id = d2.id
|
|
1566
|
-
WHERE d1.relative_path LIKE ?
|
|
1567
|
-
AND d2.relative_path <> d1.relative_path
|
|
1568
|
-
AND ${db.localSymbolPredicate}
|
|
1569
|
-
ORDER BY d2.relative_path`,
|
|
1570
|
-
`%${filePattern}%`
|
|
1571
|
-
);
|
|
1572
|
-
return rows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({ relativePath: r.relative_path }));
|
|
1573
|
-
}
|
|
1574
|
-
function rdeps(db, filePattern) {
|
|
1575
|
-
const rows = db.all(
|
|
1576
|
-
`SELECT DISTINCT d1.relative_path
|
|
1577
|
-
FROM mentions m
|
|
1578
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
1579
|
-
JOIN documents d1 ON c.document_id = d1.id
|
|
1580
|
-
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
1581
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
1582
|
-
JOIN documents d2 ON der.document_id = d2.id
|
|
1583
|
-
WHERE d2.relative_path LIKE ?
|
|
1584
|
-
AND d1.relative_path NOT LIKE ?
|
|
1585
|
-
ORDER BY d1.relative_path`,
|
|
1586
|
-
`%${filePattern}%`,
|
|
1587
|
-
`%${filePattern}%`
|
|
1588
|
-
);
|
|
1589
|
-
return rows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({ relativePath: r.relative_path }));
|
|
1590
|
-
}
|
|
1591
|
-
|
|
1592
|
-
// src/queries/system.ts
|
|
1593
|
-
function system(db, modulePattern) {
|
|
1594
|
-
const fileRows = db.all(
|
|
1595
|
-
`SELECT relative_path FROM documents
|
|
1596
|
-
WHERE relative_path LIKE ?
|
|
1597
|
-
ORDER BY relative_path`,
|
|
1598
|
-
`%${modulePattern}%`
|
|
1599
|
-
);
|
|
1600
|
-
const files2 = fileRows.map((r) => r.relative_path).filter((p) => !db.isIgnored(p));
|
|
1601
|
-
const symbolRows = db.all(
|
|
1602
|
-
`SELECT der.start_line, der.end_line, gs.symbol,
|
|
1603
|
-
REPLACE(SUBSTR(gs.documentation, INSTR(gs.documentation, '|') + 1), char(10), ' ') AS sig
|
|
1604
|
-
FROM defn_enclosing_ranges der
|
|
1605
|
-
JOIN global_symbols gs ON der.symbol_id = gs.id
|
|
1606
|
-
JOIN documents d ON der.document_id = d.id
|
|
1607
|
-
WHERE d.relative_path LIKE ?
|
|
1608
|
-
AND ${db.localSymbolPredicate}
|
|
1609
|
-
${db.symbolNoise}
|
|
1610
|
-
AND gs.documentation IS NOT NULL
|
|
1611
|
-
ORDER BY d.relative_path, der.start_line`,
|
|
1612
|
-
`%${modulePattern}%`
|
|
1613
|
-
);
|
|
1614
|
-
const symbols2 = symbolRows.map((r) => ({
|
|
1615
|
-
startLine: r.start_line,
|
|
1616
|
-
endLine: r.end_line,
|
|
1617
|
-
symbol: r.symbol,
|
|
1618
|
-
shortName: shortenSymbol(r.symbol),
|
|
1619
|
-
signature: cleanSignature(r.sig)
|
|
1620
|
-
}));
|
|
1621
|
-
const depRows = db.all(
|
|
1622
|
-
`SELECT DISTINCT d2.relative_path
|
|
1623
|
-
FROM mentions m
|
|
1624
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
1625
|
-
JOIN documents d1 ON c.document_id = d1.id
|
|
1626
|
-
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
1627
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
1628
|
-
JOIN documents d2 ON der.document_id = d2.id
|
|
1629
|
-
WHERE d1.relative_path LIKE ?
|
|
1630
|
-
AND d2.relative_path NOT LIKE ?
|
|
1631
|
-
AND ${db.localSymbolPredicate}
|
|
1632
|
-
ORDER BY d2.relative_path`,
|
|
1633
|
-
`%${modulePattern}%`,
|
|
1634
|
-
`%${modulePattern}%`
|
|
1635
|
-
);
|
|
1636
|
-
const dependsOn = depRows.map((r) => r.relative_path).filter((p) => !db.isIgnored(p));
|
|
1637
|
-
const rdepRows = db.all(
|
|
1638
|
-
`SELECT DISTINCT d1.relative_path
|
|
1639
|
-
FROM mentions m
|
|
1640
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
1641
|
-
JOIN documents d1 ON c.document_id = d1.id
|
|
1642
|
-
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
1643
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
1644
|
-
JOIN documents d2 ON der.document_id = d2.id
|
|
1645
|
-
WHERE d2.relative_path LIKE ?
|
|
1646
|
-
AND d1.relative_path NOT LIKE ?
|
|
1647
|
-
ORDER BY d1.relative_path`,
|
|
1648
|
-
`%${modulePattern}%`,
|
|
1649
|
-
`%${modulePattern}%`
|
|
1650
|
-
);
|
|
1651
|
-
const dependedOnBy = rdepRows.map((r) => r.relative_path).filter((p) => !db.isIgnored(p));
|
|
1652
|
-
return { files: files2, symbols: symbols2, dependsOn, dependedOnBy };
|
|
1653
|
-
}
|
|
1654
|
-
|
|
1655
|
-
// src/queries/surface.ts
|
|
1656
|
-
function surface(db, modulePattern) {
|
|
1657
|
-
const rows = db.all(
|
|
1658
|
-
`SELECT DISTINCT d1.relative_path, gs.symbol
|
|
2146
|
+
const primary = db.all(
|
|
2147
|
+
`SELECT DISTINCT caller_gs.symbol AS caller_symbol, caller_d.relative_path AS caller_file
|
|
1659
2148
|
FROM mentions m
|
|
1660
2149
|
JOIN chunks c ON m.chunk_id = c.id
|
|
1661
|
-
JOIN documents
|
|
1662
|
-
JOIN
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
2150
|
+
JOIN documents ref_d ON c.document_id = ref_d.id
|
|
2151
|
+
JOIN defn_enclosing_ranges caller_der
|
|
2152
|
+
ON caller_der.document_id = ref_d.id
|
|
2153
|
+
AND c.start_line >= caller_der.start_line
|
|
2154
|
+
AND c.end_line <= caller_der.end_line
|
|
2155
|
+
JOIN global_symbols caller_gs ON caller_der.symbol_id = caller_gs.id
|
|
2156
|
+
JOIN documents caller_d ON caller_der.document_id = caller_d.id
|
|
2157
|
+
WHERE m.symbol_id = ?
|
|
1668
2158
|
AND m.role != 1
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
2159
|
+
AND caller_gs.id != ?
|
|
2160
|
+
${db.symbolNoiseFor("caller_gs")}
|
|
2161
|
+
${db.pathExclusionsFor("caller_d")}
|
|
2162
|
+
ORDER BY caller_d.relative_path
|
|
2163
|
+
${opts.limit ? "LIMIT ?" : ""}`,
|
|
2164
|
+
...callerQueryParams(match, opts.limit)
|
|
2165
|
+
).filter((row) => !db.isIgnored(row.caller_file)).map((row) => ({
|
|
2166
|
+
symbol: row.caller_symbol,
|
|
2167
|
+
file: row.caller_file
|
|
1677
2168
|
}));
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
var liveBarrelCache = /* @__PURE__ */ new WeakMap();
|
|
1682
|
-
function normalizePath(path2) {
|
|
1683
|
-
return path2.replace(/\\/g, "/");
|
|
1684
|
-
}
|
|
1685
|
-
function isBarrelFile(path2) {
|
|
1686
|
-
const normalized = normalizePath(path2);
|
|
1687
|
-
return normalized === "index.ts" || normalized === "index.js" || normalized.endsWith("/index.ts") || normalized.endsWith("/index.js") || normalized.endsWith("/mod.rs") || normalized.endsWith("/__init__.py");
|
|
1688
|
-
}
|
|
1689
|
-
function isWorkerEntrySurface(path2) {
|
|
1690
|
-
const normalized = normalizePath(path2);
|
|
1691
|
-
return /(^|\/)[^/]*worker\.(ts|js|mjs|cjs|rs|py|go)$/.test(normalized);
|
|
1692
|
-
}
|
|
1693
|
-
function isStructuralEntrySurface(path2) {
|
|
1694
|
-
const normalized = normalizePath(path2);
|
|
1695
|
-
const segments = normalized.split("/");
|
|
1696
|
-
const basename = segments[segments.length - 1] ?? normalized;
|
|
1697
|
-
if (basename === "cli.ts" || basename === "cli.js" || basename === "postinstall.ts" || basename === "postinstall.js" || basename === "main.ts" || basename === "main.js" || basename === "main.rs" || basename === "main.go" || basename === "main.py") {
|
|
1698
|
-
return true;
|
|
2169
|
+
const sourceFallback = getPythonSourceCallerRows(db, match, opts.limit);
|
|
2170
|
+
if (sourceFallback.length === 0) {
|
|
2171
|
+
return primary;
|
|
1699
2172
|
}
|
|
1700
|
-
|
|
1701
|
-
|
|
2173
|
+
const merged = sourceFallback.length > 0 ? [...sourceFallback] : [];
|
|
2174
|
+
const seen = new Set(merged.map((row) => `${row.symbol}|${row.file}`));
|
|
2175
|
+
for (const row of primary) {
|
|
2176
|
+
const key = `${row.symbol}|${row.file}`;
|
|
2177
|
+
if (seen.has(key)) continue;
|
|
2178
|
+
if (isFunctionLikeSymbol(row.symbol) || merged.length === 0) {
|
|
2179
|
+
seen.add(key);
|
|
2180
|
+
merged.push(row);
|
|
2181
|
+
}
|
|
1702
2182
|
}
|
|
1703
|
-
return
|
|
1704
|
-
}
|
|
1705
|
-
function getIndexedPaths(db) {
|
|
1706
|
-
return db.all(
|
|
1707
|
-
`SELECT d.relative_path
|
|
1708
|
-
FROM documents d
|
|
1709
|
-
WHERE 1 = 1
|
|
1710
|
-
${db.pathExclusionsFor("d")}
|
|
1711
|
-
ORDER BY d.relative_path`
|
|
1712
|
-
).map((row) => row.relative_path).filter((path2) => !db.isIgnored(path2));
|
|
2183
|
+
return applyLimit(merged, opts.limit);
|
|
1713
2184
|
}
|
|
1714
|
-
function
|
|
1715
|
-
const
|
|
1716
|
-
if (
|
|
1717
|
-
return
|
|
2185
|
+
function getSourceReferenceSites(db, symbol) {
|
|
2186
|
+
const match = getFullSymbolMatch(db, symbol);
|
|
2187
|
+
if (!match) {
|
|
2188
|
+
return [];
|
|
1718
2189
|
}
|
|
1719
|
-
const
|
|
1720
|
-
|
|
1721
|
-
|
|
2190
|
+
const identifier = leafName(match.symbol);
|
|
2191
|
+
if (!identifier || !hasUniqueLeafDefinition(db, identifier, match.symbolId)) {
|
|
2192
|
+
return [];
|
|
2193
|
+
}
|
|
2194
|
+
const documents = db.all(
|
|
2195
|
+
`SELECT relative_path
|
|
2196
|
+
FROM documents
|
|
2197
|
+
WHERE 1 = 1
|
|
2198
|
+
${db.pathExclusionsFor("documents")}
|
|
2199
|
+
ORDER BY relative_path`
|
|
1722
2200
|
);
|
|
1723
|
-
const
|
|
1724
|
-
const
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
2201
|
+
const sites = [];
|
|
2202
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2203
|
+
for (const document of documents) {
|
|
2204
|
+
if (db.isIgnored(document.relative_path)) continue;
|
|
2205
|
+
const lines = findIdentifierLines(db, document.relative_path, identifier, document.relative_path === match.relativePath ? { excludeStartLine: match.startLine, excludeEndLine: match.endLine } : {});
|
|
2206
|
+
for (const line of lines) {
|
|
2207
|
+
const enclosing = db.get(
|
|
2208
|
+
`SELECT gs.symbol
|
|
2209
|
+
FROM defn_enclosing_ranges der
|
|
2210
|
+
JOIN global_symbols gs ON der.symbol_id = gs.id
|
|
2211
|
+
JOIN documents d ON der.document_id = d.id
|
|
2212
|
+
WHERE d.relative_path = ?
|
|
2213
|
+
AND der.start_line <= ?
|
|
2214
|
+
AND der.end_line >= ?
|
|
2215
|
+
ORDER BY (der.end_line - der.start_line) ASC
|
|
2216
|
+
LIMIT 1`,
|
|
2217
|
+
document.relative_path,
|
|
2218
|
+
line,
|
|
2219
|
+
line
|
|
2220
|
+
);
|
|
2221
|
+
const key = `${document.relative_path}|${line}|${enclosing?.symbol ?? ""}`;
|
|
2222
|
+
if (seen.has(key)) continue;
|
|
2223
|
+
seen.add(key);
|
|
2224
|
+
sites.push({
|
|
2225
|
+
file: document.relative_path,
|
|
2226
|
+
line,
|
|
2227
|
+
enclosingSymbol: enclosing?.symbol ?? null
|
|
2228
|
+
});
|
|
1738
2229
|
}
|
|
1739
2230
|
}
|
|
1740
|
-
|
|
1741
|
-
return liveBarrels;
|
|
2231
|
+
return sites;
|
|
1742
2232
|
}
|
|
1743
|
-
function
|
|
1744
|
-
|
|
2233
|
+
function calleeQueryParams(symbol, limit) {
|
|
2234
|
+
const params = [
|
|
2235
|
+
symbol.documentId,
|
|
2236
|
+
symbol.startLine,
|
|
2237
|
+
symbol.endLine,
|
|
2238
|
+
symbol.symbolId
|
|
2239
|
+
];
|
|
2240
|
+
if (typeof limit === "number") {
|
|
2241
|
+
params.push(limit);
|
|
2242
|
+
}
|
|
2243
|
+
return params;
|
|
1745
2244
|
}
|
|
1746
|
-
function
|
|
1747
|
-
|
|
2245
|
+
function callerQueryParams(symbol, limit) {
|
|
2246
|
+
const params = [symbol.symbolId, symbol.symbolId];
|
|
2247
|
+
if (typeof limit === "number") {
|
|
2248
|
+
params.push(limit);
|
|
2249
|
+
}
|
|
2250
|
+
return params;
|
|
1748
2251
|
}
|
|
1749
|
-
function
|
|
1750
|
-
const
|
|
1751
|
-
|
|
2252
|
+
function getPythonSourceCalleeRows(db, symbol, limit) {
|
|
2253
|
+
const match = getFullSymbolMatch(db, symbol);
|
|
2254
|
+
if (!match || !isPythonDocument(db, match.relativePath)) {
|
|
2255
|
+
return [];
|
|
2256
|
+
}
|
|
2257
|
+
const definitions = getDefinitionsForFile(db, match.relativePath);
|
|
2258
|
+
const current = definitions.find((definition) => definition.symbolId === match.symbolId);
|
|
2259
|
+
if (!current) {
|
|
2260
|
+
return [];
|
|
2261
|
+
}
|
|
2262
|
+
const imports2 = getSourceImports(db, match.relativePath);
|
|
2263
|
+
const bindings = new Map(
|
|
2264
|
+
getSourceConstructorBindings(db, match.relativePath, {
|
|
2265
|
+
startLine: match.startLine,
|
|
2266
|
+
endLine: match.endLine
|
|
2267
|
+
}).map((binding) => [binding.localName, binding.typeName])
|
|
2268
|
+
);
|
|
2269
|
+
const rows = [];
|
|
2270
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2271
|
+
for (const call of getSourceCalls(db, match.relativePath, {
|
|
2272
|
+
startLine: match.startLine,
|
|
2273
|
+
endLine: match.endLine
|
|
2274
|
+
})) {
|
|
2275
|
+
const resolved = resolvePythonCallTarget(
|
|
2276
|
+
db,
|
|
2277
|
+
current,
|
|
2278
|
+
definitions,
|
|
2279
|
+
imports2,
|
|
2280
|
+
bindings,
|
|
2281
|
+
call.receiverName,
|
|
2282
|
+
call.calleeName
|
|
2283
|
+
);
|
|
2284
|
+
if (!resolved || resolved.symbolId === match.symbolId || db.isIgnored(resolved.relativePath)) continue;
|
|
2285
|
+
const chunkId = 1e9 + call.line;
|
|
2286
|
+
const key = `${resolved.symbol}|${resolved.relativePath}|${chunkId}`;
|
|
2287
|
+
if (seen.has(key)) continue;
|
|
2288
|
+
seen.add(key);
|
|
2289
|
+
rows.push({
|
|
2290
|
+
symbol: resolved.symbol,
|
|
2291
|
+
file: resolved.relativePath,
|
|
2292
|
+
chunkId
|
|
2293
|
+
});
|
|
2294
|
+
}
|
|
2295
|
+
return applyLimit(rows, limit);
|
|
1752
2296
|
}
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
scope,
|
|
1758
|
-
minLoc = 1,
|
|
1759
|
-
includeTests = false,
|
|
1760
|
-
skipBarrels = false,
|
|
1761
|
-
includeMembers = false
|
|
1762
|
-
} = opts;
|
|
1763
|
-
const params = [minLoc];
|
|
1764
|
-
let testFileExclusions = "";
|
|
1765
|
-
let memberExclusion = "";
|
|
1766
|
-
let barrelExclusions = "";
|
|
1767
|
-
if (scope) {
|
|
1768
|
-
params.push(`%${scope}%`);
|
|
2297
|
+
function getJavaScriptSourceCalleeRows(db, symbol, limit) {
|
|
2298
|
+
const match = getFullSymbolMatch(db, symbol);
|
|
2299
|
+
if (!match || !isJavaScriptDocument(db, match.relativePath)) {
|
|
2300
|
+
return [];
|
|
1769
2301
|
}
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
2302
|
+
const definitions = getDefinitionsForFile(db, match.relativePath);
|
|
2303
|
+
const current = definitions.find((definition) => definition.symbolId === match.symbolId);
|
|
2304
|
+
if (!current) {
|
|
2305
|
+
return [];
|
|
1774
2306
|
}
|
|
1775
|
-
|
|
1776
|
-
|
|
2307
|
+
const imports2 = getSourceImports(db, match.relativePath);
|
|
2308
|
+
const bindings = new Map(
|
|
2309
|
+
getSourceConstructorBindings(db, match.relativePath, {
|
|
2310
|
+
startLine: match.startLine,
|
|
2311
|
+
endLine: match.endLine
|
|
2312
|
+
}).map((binding) => [binding.localName, binding.typeName])
|
|
2313
|
+
);
|
|
2314
|
+
const rows = [];
|
|
2315
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2316
|
+
for (const call of getSourceCalls(db, match.relativePath, {
|
|
2317
|
+
startLine: match.startLine,
|
|
2318
|
+
endLine: match.endLine
|
|
2319
|
+
})) {
|
|
2320
|
+
const resolved = resolveJavaScriptCallTarget(
|
|
2321
|
+
db,
|
|
2322
|
+
current,
|
|
2323
|
+
definitions,
|
|
2324
|
+
imports2,
|
|
2325
|
+
bindings,
|
|
2326
|
+
call.receiverName,
|
|
2327
|
+
call.calleeName
|
|
2328
|
+
);
|
|
2329
|
+
if (!resolved || resolved.symbolId === match.symbolId || db.isIgnored(resolved.relativePath)) continue;
|
|
2330
|
+
const chunkId = 1e9 + call.line;
|
|
2331
|
+
const key = `${resolved.symbol}|${resolved.relativePath}|${chunkId}`;
|
|
2332
|
+
if (seen.has(key)) continue;
|
|
2333
|
+
seen.add(key);
|
|
2334
|
+
rows.push({
|
|
2335
|
+
symbol: resolved.symbol,
|
|
2336
|
+
file: resolved.relativePath,
|
|
2337
|
+
chunkId
|
|
2338
|
+
});
|
|
1777
2339
|
}
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
2340
|
+
return applyLimit(rows, limit);
|
|
2341
|
+
}
|
|
2342
|
+
function getSourceBackedCalleeRows(db, symbol, limit) {
|
|
2343
|
+
const match = getFullSymbolMatch(db, symbol);
|
|
2344
|
+
if (!match) {
|
|
2345
|
+
return [];
|
|
2346
|
+
}
|
|
2347
|
+
if (isPythonDocument(db, match.relativePath)) {
|
|
2348
|
+
return getPythonSourceCalleeRows(db, match, limit);
|
|
2349
|
+
}
|
|
2350
|
+
if (isJavaScriptDocument(db, match.relativePath)) {
|
|
2351
|
+
return getJavaScriptSourceCalleeRows(db, match, limit);
|
|
2352
|
+
}
|
|
2353
|
+
return [];
|
|
2354
|
+
}
|
|
2355
|
+
function getPythonSourceCallerRows(db, target, limit) {
|
|
2356
|
+
if (!isPythonDocument(db, target.relativePath)) {
|
|
2357
|
+
return [];
|
|
2358
|
+
}
|
|
2359
|
+
const rows = [];
|
|
2360
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2361
|
+
for (const candidate of getAllFunctionLikeDefinitions(db)) {
|
|
2362
|
+
if (candidate.symbolId === target.symbolId) continue;
|
|
2363
|
+
const callees = getPythonSourceCalleeRows(db, candidate);
|
|
2364
|
+
if (!callees.some((callee) => callee.symbol === target.symbol)) continue;
|
|
2365
|
+
const key = `${candidate.symbol}|${candidate.relativePath}`;
|
|
2366
|
+
if (seen.has(key) || db.isIgnored(candidate.relativePath)) continue;
|
|
2367
|
+
seen.add(key);
|
|
2368
|
+
rows.push({
|
|
2369
|
+
symbol: candidate.symbol,
|
|
2370
|
+
file: candidate.relativePath
|
|
2371
|
+
});
|
|
2372
|
+
if (typeof limit === "number" && rows.length >= limit) {
|
|
2373
|
+
break;
|
|
1783
2374
|
}
|
|
1784
2375
|
}
|
|
1785
|
-
|
|
1786
|
-
SELECT
|
|
1787
|
-
d.relative_path,
|
|
1788
|
-
der.start_line,
|
|
1789
|
-
der.end_line,
|
|
1790
|
-
(der.end_line - der.start_line + 1) AS loc,
|
|
1791
|
-
gs.symbol,
|
|
1792
|
-
(SELECT COUNT(*) FROM mentions m2
|
|
1793
|
-
JOIN chunks c2 ON m2.chunk_id = c2.id
|
|
1794
|
-
WHERE m2.symbol_id = gs.id AND m2.role != 1 AND c2.document_id = d.id
|
|
1795
|
-
) AS same_file_refs
|
|
1796
|
-
FROM global_symbols gs
|
|
1797
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
1798
|
-
JOIN documents d ON der.document_id = d.id
|
|
1799
|
-
WHERE 1 = 1
|
|
1800
|
-
${db.pathExclusionsFor("d")}
|
|
1801
|
-
${db.symbolNoiseFor("gs")}
|
|
1802
|
-
AND (der.end_line - der.start_line + 1) >= ?
|
|
1803
|
-
${scope ? "AND d.relative_path LIKE ?" : ""}
|
|
1804
|
-
${testFileExclusions}
|
|
1805
|
-
${memberExclusion}
|
|
1806
|
-
AND NOT EXISTS (
|
|
1807
|
-
SELECT 1
|
|
1808
|
-
FROM mentions ref_m
|
|
1809
|
-
JOIN chunks ref_c ON ref_m.chunk_id = ref_c.id
|
|
1810
|
-
JOIN documents ref_d ON ref_c.document_id = ref_d.id
|
|
1811
|
-
WHERE ref_m.symbol_id = gs.id
|
|
1812
|
-
AND ref_m.role != 1
|
|
1813
|
-
AND ref_d.id != d.id
|
|
1814
|
-
${barrelExclusions}
|
|
1815
|
-
)
|
|
1816
|
-
ORDER BY (der.end_line - der.start_line + 1) DESC, d.relative_path, der.start_line
|
|
1817
|
-
`;
|
|
1818
|
-
const rows = db.all(sql, ...params);
|
|
1819
|
-
let deadCodeCount = 0;
|
|
1820
|
-
let fileInternalCount = 0;
|
|
1821
|
-
let totalLoc = 0;
|
|
1822
|
-
const symbols2 = rows.filter((r) => !db.isIgnored(r.relative_path)).filter((r) => !isEntrySurface(db, r.relative_path)).map((r) => {
|
|
1823
|
-
const kind = r.same_file_refs === 0 ? "dead-code" : "file-internal";
|
|
1824
|
-
if (kind === "dead-code") deadCodeCount++;
|
|
1825
|
-
else fileInternalCount++;
|
|
1826
|
-
totalLoc += r.loc;
|
|
1827
|
-
return {
|
|
1828
|
-
relativePath: r.relative_path,
|
|
1829
|
-
startLine: r.start_line,
|
|
1830
|
-
endLine: r.end_line,
|
|
1831
|
-
loc: r.loc,
|
|
1832
|
-
symbol: r.symbol,
|
|
1833
|
-
shortName: shortenSymbol(r.symbol),
|
|
1834
|
-
sameFileRefs: r.same_file_refs,
|
|
1835
|
-
kind
|
|
1836
|
-
};
|
|
1837
|
-
});
|
|
1838
|
-
return {
|
|
1839
|
-
symbols: symbols2,
|
|
1840
|
-
totalCount: symbols2.length,
|
|
1841
|
-
deadCodeCount,
|
|
1842
|
-
fileInternalCount,
|
|
1843
|
-
totalLoc
|
|
1844
|
-
};
|
|
2376
|
+
return rows;
|
|
1845
2377
|
}
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
const
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
1859
|
-
JOIN documents ref_d ON c.document_id = ref_d.id
|
|
1860
|
-
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
1861
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
1862
|
-
JOIN documents def_d ON der.document_id = def_d.id
|
|
1863
|
-
WHERE m.role != 1
|
|
1864
|
-
${db.pathExclusionsFor("def_d")}
|
|
1865
|
-
${db.symbolNoiseFor("gs")}
|
|
1866
|
-
${scopeFilter}
|
|
1867
|
-
GROUP BY gs.id
|
|
1868
|
-
ORDER BY ref_count DESC
|
|
1869
|
-
LIMIT ?`,
|
|
1870
|
-
limit
|
|
2378
|
+
function getFullSymbolMatch(db, symbol) {
|
|
2379
|
+
if ("symbol" in symbol && "relativePath" in symbol) {
|
|
2380
|
+
return symbol;
|
|
2381
|
+
}
|
|
2382
|
+
const row = db.get(
|
|
2383
|
+
`SELECT gs.symbol, d.relative_path
|
|
2384
|
+
FROM global_symbols gs
|
|
2385
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
2386
|
+
JOIN documents d ON der.document_id = d.id
|
|
2387
|
+
WHERE gs.id = ?
|
|
2388
|
+
LIMIT 1`,
|
|
2389
|
+
symbol.symbolId
|
|
1871
2390
|
);
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
2391
|
+
if (!row) {
|
|
2392
|
+
return null;
|
|
2393
|
+
}
|
|
2394
|
+
return {
|
|
2395
|
+
...symbol,
|
|
2396
|
+
symbol: row.symbol,
|
|
2397
|
+
relativePath: row.relative_path
|
|
2398
|
+
};
|
|
1879
2399
|
}
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
}
|
|
1886
|
-
|
|
1887
|
-
dirname as dirname2,
|
|
1888
|
-
extname,
|
|
1889
|
-
join as join6,
|
|
1890
|
-
relative as relative2,
|
|
1891
|
-
resolve as resolve2
|
|
1892
|
-
} from "path";
|
|
1893
|
-
var SOURCE_IMPORT_CACHE = /* @__PURE__ */ new WeakMap();
|
|
1894
|
-
var INDEXED_PATH_CACHE = /* @__PURE__ */ new WeakMap();
|
|
1895
|
-
var SOURCE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
1896
|
-
var PYTHON_SOURCE_EXTENSIONS = [".py", ".pyi"];
|
|
1897
|
-
function getSourceImports(db, relativePath) {
|
|
1898
|
-
const cache = getCachedMap(SOURCE_IMPORT_CACHE, db);
|
|
1899
|
-
const normalized = normalizePath2(relativePath);
|
|
1900
|
-
const cached = cache.get(normalized);
|
|
2400
|
+
function getDefinitionsForFile(db, relativePath) {
|
|
2401
|
+
let cache = FILE_DEFINITION_CACHE.get(db);
|
|
2402
|
+
if (!cache) {
|
|
2403
|
+
cache = /* @__PURE__ */ new Map();
|
|
2404
|
+
FILE_DEFINITION_CACHE.set(db, cache);
|
|
2405
|
+
}
|
|
2406
|
+
const cached = cache.get(relativePath);
|
|
1901
2407
|
if (cached) {
|
|
1902
2408
|
return cached;
|
|
1903
2409
|
}
|
|
1904
|
-
const
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
2410
|
+
const definitions = db.all(
|
|
2411
|
+
`SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path
|
|
2412
|
+
FROM global_symbols gs
|
|
2413
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
2414
|
+
JOIN documents d ON der.document_id = d.id
|
|
2415
|
+
WHERE d.relative_path = ?
|
|
2416
|
+
${db.symbolNoiseFor("gs")}
|
|
2417
|
+
ORDER BY der.start_line, der.end_line`,
|
|
2418
|
+
relativePath
|
|
2419
|
+
).map((row) => ({
|
|
2420
|
+
symbolId: row.id,
|
|
2421
|
+
symbol: row.symbol,
|
|
2422
|
+
documentId: row.document_id,
|
|
2423
|
+
startLine: row.start_line,
|
|
2424
|
+
endLine: row.end_line,
|
|
2425
|
+
relativePath: row.relative_path,
|
|
2426
|
+
leaf: leafName(row.symbol),
|
|
2427
|
+
parentTypeName: parentTypeName(row.symbol),
|
|
2428
|
+
isFunctionLike: isFunctionLikeSymbol(row.symbol),
|
|
2429
|
+
isTypeLike: leafSuffix(row.symbol) === "type"
|
|
2430
|
+
}));
|
|
2431
|
+
cache.set(relativePath, definitions);
|
|
2432
|
+
return definitions;
|
|
1913
2433
|
}
|
|
1914
|
-
function
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
2434
|
+
function getAllFunctionLikeDefinitions(db) {
|
|
2435
|
+
const rows = db.all(
|
|
2436
|
+
`SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path
|
|
2437
|
+
FROM global_symbols gs
|
|
2438
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
2439
|
+
JOIN documents d ON der.document_id = d.id
|
|
2440
|
+
WHERE 1 = 1
|
|
2441
|
+
${db.pathExclusionsFor("d")}
|
|
2442
|
+
${db.symbolNoiseFor("gs")}
|
|
2443
|
+
ORDER BY d.relative_path, der.start_line`
|
|
2444
|
+
);
|
|
2445
|
+
return rows.filter((row) => !db.isIgnored(row.relative_path)).filter((row) => isFunctionLikeSymbol(row.symbol)).map((row) => ({
|
|
2446
|
+
symbolId: row.id,
|
|
2447
|
+
symbol: row.symbol,
|
|
2448
|
+
documentId: row.document_id,
|
|
2449
|
+
startLine: row.start_line,
|
|
2450
|
+
endLine: row.end_line,
|
|
2451
|
+
relativePath: row.relative_path,
|
|
2452
|
+
leaf: leafName(row.symbol),
|
|
2453
|
+
parentTypeName: parentTypeName(row.symbol),
|
|
2454
|
+
isFunctionLike: true,
|
|
2455
|
+
isTypeLike: false
|
|
2456
|
+
}));
|
|
1924
2457
|
}
|
|
1925
|
-
function
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
for (const match of source.matchAll(importFromRegex)) {
|
|
1929
|
-
const full = match[0];
|
|
1930
|
-
const clause = match[1];
|
|
1931
|
-
const specifier = match[2];
|
|
1932
|
-
if (!full || !specifier || typeof match.index !== "number") continue;
|
|
1933
|
-
statements.push({
|
|
1934
|
-
clause,
|
|
1935
|
-
specifier,
|
|
1936
|
-
start: match.index,
|
|
1937
|
-
end: match.index + full.length
|
|
1938
|
-
});
|
|
2458
|
+
function resolvePythonCallTarget(db, current, currentFileDefinitions, imports2, constructorBindings, receiverName, calleeName) {
|
|
2459
|
+
if (receiverName === "self" || receiverName === "cls") {
|
|
2460
|
+
return findDefinitionByName(currentFileDefinitions, calleeName, current.parentTypeName, ["function"]);
|
|
1939
2461
|
}
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
2462
|
+
if (receiverName) {
|
|
2463
|
+
const inferredType = constructorBindings.get(receiverName);
|
|
2464
|
+
if (inferredType) {
|
|
2465
|
+
const boundMethod = findDefinitionByName(currentFileDefinitions, calleeName, inferredType, ["function"]);
|
|
2466
|
+
if (boundMethod) {
|
|
2467
|
+
return boundMethod;
|
|
2468
|
+
}
|
|
2469
|
+
for (const entry of imports2) {
|
|
2470
|
+
if (entry.localName !== inferredType || !entry.sourcePath) continue;
|
|
2471
|
+
const importedDefinitions = getDefinitionsForFile(db, entry.sourcePath);
|
|
2472
|
+
const importedMethod = findDefinitionByName(importedDefinitions, calleeName, entry.importedName, ["function"]);
|
|
2473
|
+
if (importedMethod) {
|
|
2474
|
+
return importedMethod;
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
const localClassMethod = findDefinitionByName(currentFileDefinitions, calleeName, receiverName, ["function"]);
|
|
2479
|
+
if (localClassMethod) {
|
|
2480
|
+
return localClassMethod;
|
|
2481
|
+
}
|
|
2482
|
+
const namespaceImport = imports2.find((entry) => entry.localName === receiverName && entry.sourcePath);
|
|
2483
|
+
if (namespaceImport?.sourcePath) {
|
|
2484
|
+
const importedDefinitions = getDefinitionsForFile(db, namespaceImport.sourcePath);
|
|
2485
|
+
const importedTypeMethod = namespaceImport.kind === "named" ? findDefinitionByName(importedDefinitions, calleeName, namespaceImport.importedName, ["function"]) : null;
|
|
2486
|
+
if (importedTypeMethod) {
|
|
2487
|
+
return importedTypeMethod;
|
|
2488
|
+
}
|
|
2489
|
+
return findDefinitionByName(importedDefinitions, calleeName, null, ["function", "type"]);
|
|
2490
|
+
}
|
|
2491
|
+
return null;
|
|
1951
2492
|
}
|
|
1952
|
-
|
|
2493
|
+
const importedBinding = imports2.find((entry) => entry.localName === calleeName && entry.sourcePath);
|
|
2494
|
+
if (importedBinding?.sourcePath) {
|
|
2495
|
+
const importedDefinitions = getDefinitionsForFile(db, importedBinding.sourcePath);
|
|
2496
|
+
const importedName = importedBinding.importedName === "*" ? calleeName : importedBinding.importedName;
|
|
2497
|
+
const importedDefinition = findDefinitionByName(importedDefinitions, importedName, null, ["function", "type"]);
|
|
2498
|
+
if (importedDefinition) {
|
|
2499
|
+
return importedDefinition;
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
return findDefinitionByName(currentFileDefinitions, calleeName, null, ["function", "type"]);
|
|
1953
2503
|
}
|
|
1954
|
-
function
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
if (!clause) {
|
|
1958
|
-
return [{
|
|
1959
|
-
importedName: "*",
|
|
1960
|
-
localName: null,
|
|
1961
|
-
sourcePath: resolvedSource,
|
|
1962
|
-
kind: "side-effect",
|
|
1963
|
-
used: true,
|
|
1964
|
-
usedMembers: []
|
|
1965
|
-
}];
|
|
2504
|
+
function resolveJavaScriptCallTarget(db, current, currentFileDefinitions, imports2, constructorBindings, receiverName, calleeName) {
|
|
2505
|
+
if (receiverName === "this") {
|
|
2506
|
+
return findDefinitionByName(currentFileDefinitions, calleeName, current.parentTypeName, ["function"]);
|
|
1966
2507
|
}
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
2508
|
+
if (receiverName) {
|
|
2509
|
+
const inferredType = constructorBindings.get(receiverName);
|
|
2510
|
+
if (inferredType) {
|
|
2511
|
+
const boundMethod = findDefinitionByName(currentFileDefinitions, calleeName, inferredType, ["function"]);
|
|
2512
|
+
if (boundMethod) {
|
|
2513
|
+
return boundMethod;
|
|
2514
|
+
}
|
|
2515
|
+
for (const entry of imports2) {
|
|
2516
|
+
if (entry.localName !== inferredType || !entry.sourcePath) continue;
|
|
2517
|
+
const importedDefinitions = getDefinitionsForFile(db, entry.sourcePath);
|
|
2518
|
+
const importedMethod = findDefinitionByName(importedDefinitions, calleeName, entry.importedName, ["function"]);
|
|
2519
|
+
if (importedMethod) {
|
|
2520
|
+
return importedMethod;
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
1979
2523
|
}
|
|
1980
|
-
|
|
1981
|
-
|
|
2524
|
+
const localClassMethod = findDefinitionByName(currentFileDefinitions, calleeName, receiverName, ["function"]);
|
|
2525
|
+
if (localClassMethod) {
|
|
2526
|
+
return localClassMethod;
|
|
2527
|
+
}
|
|
2528
|
+
const namespaceImport = imports2.find((entry) => entry.localName === receiverName && entry.sourcePath);
|
|
2529
|
+
if (namespaceImport?.sourcePath) {
|
|
2530
|
+
const importedDefinitions = getDefinitionsForFile(db, namespaceImport.sourcePath);
|
|
2531
|
+
if (namespaceImport.kind === "named" || namespaceImport.kind === "default") {
|
|
2532
|
+
const importedTypeMethod = findDefinitionByName(
|
|
2533
|
+
importedDefinitions,
|
|
2534
|
+
calleeName,
|
|
2535
|
+
namespaceImport.importedName,
|
|
2536
|
+
["function"]
|
|
2537
|
+
);
|
|
2538
|
+
if (importedTypeMethod) {
|
|
2539
|
+
return importedTypeMethod;
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
return findDefinitionByName(importedDefinitions, calleeName, null, ["function", "type"]);
|
|
1982
2543
|
}
|
|
1983
|
-
return
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
2544
|
+
return null;
|
|
2545
|
+
}
|
|
2546
|
+
const importedBinding = imports2.find((entry) => entry.localName === calleeName && entry.sourcePath && entry.kind !== "namespace");
|
|
2547
|
+
if (importedBinding?.sourcePath) {
|
|
2548
|
+
const importedDefinitions = getDefinitionsForFile(db, importedBinding.sourcePath);
|
|
2549
|
+
const importedName = importedBinding.importedName === "default" ? importedBinding.localName ?? calleeName : importedBinding.importedName;
|
|
2550
|
+
const importedDefinition = findDefinitionByName(importedDefinitions, importedName, null, ["function", "type"]);
|
|
2551
|
+
if (importedDefinition) {
|
|
2552
|
+
return importedDefinition;
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
return findDefinitionByName(currentFileDefinitions, calleeName, null, ["function", "type"]);
|
|
1989
2556
|
}
|
|
1990
|
-
function
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
2557
|
+
function findDefinitionByName(definitions, leaf, parentType, preference) {
|
|
2558
|
+
const candidates = definitions.filter((definition) => definition.leaf === leaf && definition.parentTypeName === parentType);
|
|
2559
|
+
if (candidates.length === 0) {
|
|
2560
|
+
return null;
|
|
2561
|
+
}
|
|
2562
|
+
for (const preferred of preference) {
|
|
2563
|
+
const match = candidates.find((candidate) => preferred === "function" ? candidate.isFunctionLike : candidate.isTypeLike);
|
|
2564
|
+
if (match) {
|
|
2565
|
+
return match;
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
return candidates[0] ?? null;
|
|
1994
2569
|
}
|
|
1995
|
-
function
|
|
1996
|
-
const
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2570
|
+
function hasUniqueLeafDefinition(db, leaf, symbolId) {
|
|
2571
|
+
const rows = db.all(
|
|
2572
|
+
`SELECT id, symbol
|
|
2573
|
+
FROM global_symbols
|
|
2574
|
+
WHERE symbol LIKE ?
|
|
2575
|
+
LIMIT 50`,
|
|
2576
|
+
`%${leaf}%`
|
|
2577
|
+
);
|
|
2578
|
+
let count = 0;
|
|
2579
|
+
for (const row of rows) {
|
|
2580
|
+
if (leafName(row.symbol) !== leaf) continue;
|
|
2581
|
+
count++;
|
|
2582
|
+
if (count > 1 && row.id !== symbolId) {
|
|
2583
|
+
return false;
|
|
2006
2584
|
}
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2585
|
+
}
|
|
2586
|
+
return count === 1;
|
|
2587
|
+
}
|
|
2588
|
+
function parentTypeName(rawSymbol) {
|
|
2589
|
+
const parsed = parseSymbol(rawSymbol);
|
|
2590
|
+
if ("kind" in parsed) {
|
|
2591
|
+
return null;
|
|
2592
|
+
}
|
|
2593
|
+
for (let index = parsed.descriptors.length - 2; index >= 0; index--) {
|
|
2594
|
+
const descriptor = parsed.descriptors[index];
|
|
2595
|
+
if (descriptor?.suffix === "type") {
|
|
2596
|
+
return descriptor.name;
|
|
2018
2597
|
}
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2598
|
+
}
|
|
2599
|
+
return null;
|
|
2600
|
+
}
|
|
2601
|
+
function isPythonDocument(db, relativePath) {
|
|
2602
|
+
const row = db.get(
|
|
2603
|
+
`SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
|
|
2604
|
+
relativePath
|
|
2605
|
+
);
|
|
2606
|
+
return row?.language === "python" || relativePath.endsWith(".py") || relativePath.endsWith(".pyi");
|
|
2607
|
+
}
|
|
2608
|
+
function isJavaScriptDocument(db, relativePath) {
|
|
2609
|
+
const row = db.get(
|
|
2610
|
+
`SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
|
|
2611
|
+
relativePath
|
|
2612
|
+
);
|
|
2613
|
+
return row?.language === "typescript" || row?.language === "javascript" || /\.(?:[cm]?[jt]sx?)$/.test(relativePath);
|
|
2614
|
+
}
|
|
2615
|
+
function applyLimit(values, limit) {
|
|
2616
|
+
return typeof limit === "number" ? values.slice(0, limit) : values;
|
|
2617
|
+
}
|
|
2618
|
+
function resolveDocumentCandidates(db, filePattern, opts) {
|
|
2619
|
+
const normalizedPattern = normalizeLookupPath(filePattern);
|
|
2620
|
+
if (!normalizedPattern) {
|
|
2621
|
+
return [];
|
|
2622
|
+
}
|
|
2623
|
+
const rows = db.all(
|
|
2624
|
+
`SELECT relative_path
|
|
2625
|
+
FROM documents
|
|
2626
|
+
WHERE 1 = 1
|
|
2627
|
+
${db.pathExclusionsFor("documents")}
|
|
2628
|
+
ORDER BY relative_path`
|
|
2629
|
+
);
|
|
2630
|
+
const scored = rows.filter((row) => !db.isIgnored(row.relative_path)).map((row) => ({
|
|
2631
|
+
relativePath: row.relative_path,
|
|
2632
|
+
score: scoreDocumentPath(row.relative_path, normalizedPattern)
|
|
2633
|
+
})).filter((row) => row.score > 0).sort((a, b) => b.score - a.score || a.relativePath.localeCompare(b.relativePath));
|
|
2634
|
+
if (scored.length === 0) {
|
|
2635
|
+
return [];
|
|
2636
|
+
}
|
|
2637
|
+
const exactish = scored.filter((row) => row.score >= 800);
|
|
2638
|
+
if (exactish.length > 0) {
|
|
2639
|
+
return opts.allowMultiple ? exactish : [exactish[0]];
|
|
2640
|
+
}
|
|
2641
|
+
return opts.allowMultiple ? scored : [scored[0]];
|
|
2642
|
+
}
|
|
2643
|
+
function scoreDocumentPath(relativePath, rawPattern) {
|
|
2644
|
+
const normalizedPath = normalizeLookupPath(relativePath);
|
|
2645
|
+
const pathBase = basename(normalizedPath);
|
|
2646
|
+
const patternBase = basename(rawPattern);
|
|
2647
|
+
let score = 0;
|
|
2648
|
+
if (normalizedPath === rawPattern) score += 1200;
|
|
2649
|
+
if (normalizedPath.endsWith(`/${rawPattern}`)) score += 1100;
|
|
2650
|
+
if (pathBase === patternBase) score += 900;
|
|
2651
|
+
if (normalizedPath.startsWith(`${rawPattern}/`)) score += 850;
|
|
2652
|
+
if (normalizedPath.includes(rawPattern)) score += 250;
|
|
2653
|
+
return score;
|
|
2654
|
+
}
|
|
2655
|
+
function normalizeLookupPath(filePattern) {
|
|
2656
|
+
return filePattern.trim().replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "").replace(/\/+$/, "");
|
|
2657
|
+
}
|
|
2658
|
+
function uniquePatterns(patterns) {
|
|
2659
|
+
return [...new Set(patterns)];
|
|
2660
|
+
}
|
|
2661
|
+
|
|
2662
|
+
// src/queries/clean-signature.ts
|
|
2663
|
+
function cleanSignature(sig) {
|
|
2664
|
+
if (!sig || !sig.trim()) return null;
|
|
2665
|
+
return sig.replace(/^```\w*\s*/, "").replace(/\s*```$/, "").replace(/^\(method\)\s*/, "").replace(/^\(property\)\s*/, "").replace(/^\(function\)\s*/, "").replace(/^\(class\)\s*/, "").replace(/^\(interface\)\s*/, "").replace(/^\(enum\)\s*/, "").replace(/^\(type alias\)\s*/, "").replace(/^\(const\)\s*/, "").replace(/^\(var\)\s*/, "").trim() || null;
|
|
2666
|
+
}
|
|
2667
|
+
|
|
2668
|
+
// src/queries/symbols.ts
|
|
2669
|
+
function symbols(db, filePattern) {
|
|
2670
|
+
const resolvedPaths = resolveIndexedPaths(db, filePattern);
|
|
2671
|
+
if (resolvedPaths.length === 0) {
|
|
2672
|
+
return [];
|
|
2673
|
+
}
|
|
2674
|
+
const placeholders = resolvedPaths.map(() => "?").join(", ");
|
|
2675
|
+
const rows = db.all(
|
|
2676
|
+
`SELECT
|
|
2677
|
+
der.start_line,
|
|
2678
|
+
der.end_line,
|
|
2679
|
+
REPLACE(SUBSTR(gs.documentation, INSTR(gs.documentation, '|') + 1), char(10), ' ') AS sig,
|
|
2680
|
+
gs.symbol,
|
|
2681
|
+
d.relative_path
|
|
2682
|
+
FROM defn_enclosing_ranges der
|
|
2683
|
+
JOIN global_symbols gs ON der.symbol_id = gs.id
|
|
2684
|
+
JOIN documents d ON der.document_id = d.id
|
|
2685
|
+
WHERE d.relative_path IN (${placeholders})
|
|
2686
|
+
AND ${db.localSymbolPredicate}
|
|
2687
|
+
${db.symbolNoise}
|
|
2688
|
+
ORDER BY d.relative_path, der.start_line`,
|
|
2689
|
+
...resolvedPaths
|
|
2690
|
+
);
|
|
2691
|
+
return rows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({
|
|
2692
|
+
startLine: r.start_line,
|
|
2693
|
+
endLine: r.end_line,
|
|
2694
|
+
symbol: r.symbol,
|
|
2695
|
+
shortName: shortenSymbol(r.symbol),
|
|
2696
|
+
signature: cleanSignature(r.sig)
|
|
2697
|
+
}));
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
// src/queries/methods.ts
|
|
2701
|
+
function methods(db, className) {
|
|
2702
|
+
const rows = db.all(
|
|
2703
|
+
`SELECT der.start_line, der.end_line, gs.symbol
|
|
2704
|
+
FROM global_symbols gs
|
|
2705
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
2706
|
+
WHERE gs.symbol LIKE ?
|
|
2707
|
+
AND ${db.localSymbolPredicate}
|
|
2708
|
+
AND gs.symbol LIKE '%().%'
|
|
2709
|
+
${db.symbolNoise}
|
|
2710
|
+
ORDER BY der.start_line`,
|
|
2711
|
+
`%${className}#%`
|
|
2712
|
+
);
|
|
2713
|
+
return rows.map((r) => ({
|
|
2714
|
+
startLine: r.start_line,
|
|
2715
|
+
endLine: r.end_line,
|
|
2716
|
+
name: leafName(r.symbol)
|
|
2717
|
+
}));
|
|
2718
|
+
}
|
|
2719
|
+
|
|
2720
|
+
// src/queries/refs.ts
|
|
2721
|
+
function refs(db, symbolPattern) {
|
|
2722
|
+
const match = findFirstSymbolMatch(db, symbolPattern);
|
|
2723
|
+
if (match) {
|
|
2724
|
+
const sourceSites = getSourceReferenceSites(db, match).filter((site) => !db.isIgnored(site.file)).map((site) => ({
|
|
2725
|
+
relativePath: site.file,
|
|
2726
|
+
line: site.line
|
|
2727
|
+
}));
|
|
2728
|
+
if (sourceSites.length > 0) {
|
|
2729
|
+
return sourceSites;
|
|
2026
2730
|
}
|
|
2027
2731
|
}
|
|
2028
|
-
|
|
2732
|
+
const rows = db.all(
|
|
2733
|
+
`SELECT DISTINCT d.relative_path, c.start_line
|
|
2734
|
+
FROM mentions m
|
|
2735
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
2736
|
+
JOIN documents d ON c.document_id = d.id
|
|
2737
|
+
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
2738
|
+
WHERE m.symbol_id = ?
|
|
2739
|
+
AND ${db.localSymbolPredicate}
|
|
2740
|
+
AND m.role != 1
|
|
2741
|
+
ORDER BY d.relative_path, c.start_line`,
|
|
2742
|
+
match?.symbolId ?? -1
|
|
2743
|
+
);
|
|
2744
|
+
return rows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({
|
|
2745
|
+
relativePath: r.relative_path,
|
|
2746
|
+
line: r.start_line
|
|
2747
|
+
}));
|
|
2029
2748
|
}
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
clause: normalized.slice("import ".length).trim()
|
|
2037
|
-
};
|
|
2038
|
-
}
|
|
2039
|
-
const fromMatch = normalized.match(/^from\s+([.\w]+)\s+import\s+([\s\S]+)$/);
|
|
2040
|
-
if (!fromMatch) {
|
|
2041
|
-
return null;
|
|
2042
|
-
}
|
|
2043
|
-
let clause = fromMatch[2].trim();
|
|
2044
|
-
if (clause.startsWith("(") && clause.endsWith(")")) {
|
|
2045
|
-
clause = clause.slice(1, -1).trim();
|
|
2749
|
+
|
|
2750
|
+
// src/queries/trace.ts
|
|
2751
|
+
function trace(db, symbolPattern) {
|
|
2752
|
+
const match = findFirstSymbolMatch(db, symbolPattern);
|
|
2753
|
+
if (!match) {
|
|
2754
|
+
return { definitions: [], referencedBy: [] };
|
|
2046
2755
|
}
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2756
|
+
const defRows = db.all(
|
|
2757
|
+
`SELECT d.relative_path, der.start_line, der.end_line,
|
|
2758
|
+
gs.display_name,
|
|
2759
|
+
REPLACE(SUBSTR(gs.documentation, INSTR(gs.documentation, '|') + 1), char(10), ' ') AS sig
|
|
2760
|
+
FROM global_symbols gs
|
|
2761
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
2762
|
+
JOIN documents d ON der.document_id = d.id
|
|
2763
|
+
WHERE gs.id = ?
|
|
2764
|
+
ORDER BY d.relative_path, der.start_line
|
|
2765
|
+
LIMIT 10`,
|
|
2766
|
+
match.symbolId
|
|
2767
|
+
);
|
|
2768
|
+
const definitions = defRows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({
|
|
2769
|
+
relativePath: r.relative_path,
|
|
2770
|
+
startLine: r.start_line,
|
|
2771
|
+
endLine: r.end_line,
|
|
2772
|
+
signature: buildTraceSignature(r.sig, r.display_name, match.symbol)
|
|
2773
|
+
}));
|
|
2774
|
+
const sourceSites = getSourceReferenceSites(db, match);
|
|
2775
|
+
const referencedBy = sourceSites.length > 0 ? sourceSites.filter((site) => !db.isIgnored(site.file)).map((site) => ({
|
|
2776
|
+
relativePath: site.file,
|
|
2777
|
+
line: site.line,
|
|
2778
|
+
enclosingSymbol: site.enclosingSymbol,
|
|
2779
|
+
enclosingShort: site.enclosingSymbol ? shortenSymbol(site.enclosingSymbol) : "(top-level)"
|
|
2780
|
+
})) : db.all(
|
|
2781
|
+
`SELECT DISTINCT d.relative_path, c.start_line AS line,
|
|
2782
|
+
(SELECT enc_gs.symbol
|
|
2783
|
+
FROM defn_enclosing_ranges enc_der
|
|
2784
|
+
JOIN global_symbols enc_gs ON enc_der.symbol_id = enc_gs.id
|
|
2785
|
+
WHERE enc_der.document_id = d.id
|
|
2786
|
+
AND enc_der.start_line <= c.start_line
|
|
2787
|
+
AND enc_der.end_line >= c.end_line
|
|
2788
|
+
ORDER BY (enc_der.end_line - enc_der.start_line) ASC
|
|
2789
|
+
LIMIT 1
|
|
2790
|
+
) AS enclosing_symbol
|
|
2791
|
+
FROM mentions m
|
|
2792
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
2793
|
+
JOIN documents d ON c.document_id = d.id
|
|
2794
|
+
WHERE m.symbol_id = ?
|
|
2795
|
+
AND m.role != 1
|
|
2796
|
+
ORDER BY d.relative_path, c.start_line`,
|
|
2797
|
+
match.symbolId
|
|
2798
|
+
).filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({
|
|
2799
|
+
relativePath: r.relative_path,
|
|
2800
|
+
line: r.line,
|
|
2801
|
+
enclosingSymbol: r.enclosing_symbol,
|
|
2802
|
+
enclosingShort: r.enclosing_symbol ? shortenSymbol(r.enclosing_symbol) : "(top-level)"
|
|
2803
|
+
}));
|
|
2804
|
+
return { definitions, referencedBy };
|
|
2052
2805
|
}
|
|
2053
|
-
function
|
|
2054
|
-
const
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
return splitTopLevel(normalizedClause).flatMap((entry) => {
|
|
2058
|
-
const cleaned = entry.trim().replace(/,$/, "");
|
|
2059
|
-
if (!cleaned) return [];
|
|
2060
|
-
const [moduleName, alias] = cleaned.split(/\s+as\s+/);
|
|
2061
|
-
const importedName = moduleName.trim();
|
|
2062
|
-
const localName = (alias ?? importedName.split(".")[0] ?? importedName).trim();
|
|
2063
|
-
const sourcePath2 = resolvePythonImportPath(db, importerPath, importedName);
|
|
2064
|
-
const usedMembers = collectNamespaceMembers(body, localName);
|
|
2065
|
-
return [{
|
|
2066
|
-
importedName,
|
|
2067
|
-
localName,
|
|
2068
|
-
sourcePath: sourcePath2,
|
|
2069
|
-
kind: "namespace",
|
|
2070
|
-
used: hasIdentifierUsage(body, localName) || usedMembers.length > 0,
|
|
2071
|
-
usedMembers
|
|
2072
|
-
}];
|
|
2073
|
-
});
|
|
2806
|
+
function buildTraceSignature(signature, displayName, rawSymbol) {
|
|
2807
|
+
const cleaned = cleanSignature(signature);
|
|
2808
|
+
if (cleaned && !looksBogusSignature(cleaned)) {
|
|
2809
|
+
return cleaned;
|
|
2074
2810
|
}
|
|
2075
|
-
const
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
const cleaned = entry.trim().replace(/,$/, "");
|
|
2079
|
-
if (!cleaned) continue;
|
|
2080
|
-
if (cleaned === "*") {
|
|
2081
|
-
results.push({
|
|
2082
|
-
importedName: "*",
|
|
2083
|
-
localName: null,
|
|
2084
|
-
sourcePath,
|
|
2085
|
-
kind: "side-effect",
|
|
2086
|
-
used: true,
|
|
2087
|
-
usedMembers: []
|
|
2088
|
-
});
|
|
2089
|
-
continue;
|
|
2090
|
-
}
|
|
2091
|
-
const [importedName, alias] = cleaned.split(/\s+as\s+/);
|
|
2092
|
-
const localName = (alias ?? importedName).trim();
|
|
2093
|
-
results.push({
|
|
2094
|
-
importedName: importedName.trim(),
|
|
2095
|
-
localName,
|
|
2096
|
-
sourcePath,
|
|
2097
|
-
kind: "named",
|
|
2098
|
-
used: hasIdentifierUsage(body, localName),
|
|
2099
|
-
usedMembers: []
|
|
2100
|
-
});
|
|
2811
|
+
const fallback = (displayName ?? "").trim();
|
|
2812
|
+
if (fallback) {
|
|
2813
|
+
return isFunctionLikeSymbol(rawSymbol) && !fallback.endsWith("()") ? `${fallback}()` : fallback;
|
|
2101
2814
|
}
|
|
2102
|
-
return
|
|
2815
|
+
return shortenSymbol(rawSymbol);
|
|
2103
2816
|
}
|
|
2104
|
-
function
|
|
2105
|
-
|
|
2106
|
-
const [first, second] = splitImportClause(trimmed);
|
|
2107
|
-
const entries = [];
|
|
2108
|
-
if (first) {
|
|
2109
|
-
entries.push(...parseImportBinding(first));
|
|
2110
|
-
}
|
|
2111
|
-
if (second) {
|
|
2112
|
-
entries.push(...parseImportBinding(second));
|
|
2113
|
-
}
|
|
2114
|
-
return entries;
|
|
2817
|
+
function looksBogusSignature(signature) {
|
|
2818
|
+
return signature.startsWith("undefined") || signature.includes("|") || signature.includes("```");
|
|
2115
2819
|
}
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
return splitTopLevel(inner).map((entry) => {
|
|
2123
|
-
const cleaned = entry.trim().replace(/^type\s+/, "");
|
|
2124
|
-
const [importedName, alias] = cleaned.split(/\s+as\s+/);
|
|
2125
|
-
return {
|
|
2126
|
-
importedName: importedName.trim(),
|
|
2127
|
-
localName: (alias ?? importedName).trim(),
|
|
2128
|
-
kind: "named"
|
|
2129
|
-
};
|
|
2130
|
-
});
|
|
2820
|
+
|
|
2821
|
+
// src/queries/deps.ts
|
|
2822
|
+
function deps(db, filePattern) {
|
|
2823
|
+
const resolvedFile = resolveIndexedFile(db, filePattern);
|
|
2824
|
+
if (!resolvedFile) {
|
|
2825
|
+
return [];
|
|
2131
2826
|
}
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2827
|
+
const rows = db.all(
|
|
2828
|
+
`SELECT DISTINCT d2.relative_path
|
|
2829
|
+
FROM mentions m
|
|
2830
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
2831
|
+
JOIN documents d1 ON c.document_id = d1.id
|
|
2832
|
+
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
2833
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
2834
|
+
JOIN documents d2 ON der.document_id = d2.id
|
|
2835
|
+
WHERE d1.relative_path = ?
|
|
2836
|
+
AND d2.relative_path <> d1.relative_path
|
|
2837
|
+
AND ${db.localSymbolPredicate}
|
|
2838
|
+
ORDER BY d2.relative_path`,
|
|
2839
|
+
resolvedFile
|
|
2840
|
+
);
|
|
2841
|
+
return rows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({ relativePath: r.relative_path }));
|
|
2842
|
+
}
|
|
2843
|
+
function rdeps(db, filePattern) {
|
|
2844
|
+
const resolvedFile = resolveIndexedFile(db, filePattern);
|
|
2845
|
+
if (!resolvedFile) {
|
|
2846
|
+
return [];
|
|
2138
2847
|
}
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2848
|
+
const rows = db.all(
|
|
2849
|
+
`SELECT DISTINCT d1.relative_path
|
|
2850
|
+
FROM mentions m
|
|
2851
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
2852
|
+
JOIN documents d1 ON c.document_id = d1.id
|
|
2853
|
+
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
2854
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
2855
|
+
JOIN documents d2 ON der.document_id = d2.id
|
|
2856
|
+
WHERE d2.relative_path = ?
|
|
2857
|
+
AND d1.relative_path != ?
|
|
2858
|
+
ORDER BY d1.relative_path`,
|
|
2859
|
+
resolvedFile,
|
|
2860
|
+
resolvedFile
|
|
2861
|
+
);
|
|
2862
|
+
return rows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({ relativePath: r.relative_path }));
|
|
2144
2863
|
}
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
if (char === "," && depth === 0) {
|
|
2152
|
-
return [clause.slice(0, i).trim(), clause.slice(i + 1).trim()];
|
|
2153
|
-
}
|
|
2864
|
+
|
|
2865
|
+
// src/queries/system.ts
|
|
2866
|
+
function system(db, modulePattern) {
|
|
2867
|
+
const matchedPaths = resolveIndexedPaths(db, modulePattern);
|
|
2868
|
+
if (matchedPaths.length === 0) {
|
|
2869
|
+
return { files: [], symbols: [], dependsOn: [], dependedOnBy: [] };
|
|
2154
2870
|
}
|
|
2155
|
-
|
|
2871
|
+
const placeholders = matchedPaths.map(() => "?").join(", ");
|
|
2872
|
+
const fileRows = db.all(
|
|
2873
|
+
`SELECT relative_path FROM documents
|
|
2874
|
+
WHERE relative_path IN (${placeholders})
|
|
2875
|
+
ORDER BY relative_path`,
|
|
2876
|
+
...matchedPaths
|
|
2877
|
+
);
|
|
2878
|
+
const files2 = fileRows.map((r) => r.relative_path).filter((p) => !db.isIgnored(p));
|
|
2879
|
+
const symbolRows = db.all(
|
|
2880
|
+
`SELECT der.start_line, der.end_line, gs.symbol,
|
|
2881
|
+
REPLACE(SUBSTR(gs.documentation, INSTR(gs.documentation, '|') + 1), char(10), ' ') AS sig
|
|
2882
|
+
FROM defn_enclosing_ranges der
|
|
2883
|
+
JOIN global_symbols gs ON der.symbol_id = gs.id
|
|
2884
|
+
JOIN documents d ON der.document_id = d.id
|
|
2885
|
+
WHERE d.relative_path IN (${placeholders})
|
|
2886
|
+
AND ${db.localSymbolPredicate}
|
|
2887
|
+
${db.symbolNoise}
|
|
2888
|
+
AND gs.documentation IS NOT NULL
|
|
2889
|
+
ORDER BY d.relative_path, der.start_line`,
|
|
2890
|
+
...matchedPaths
|
|
2891
|
+
);
|
|
2892
|
+
const symbols2 = symbolRows.map((r) => ({
|
|
2893
|
+
startLine: r.start_line,
|
|
2894
|
+
endLine: r.end_line,
|
|
2895
|
+
symbol: r.symbol,
|
|
2896
|
+
shortName: shortenSymbol(r.symbol),
|
|
2897
|
+
signature: cleanSignature(r.sig)
|
|
2898
|
+
}));
|
|
2899
|
+
const depRows = db.all(
|
|
2900
|
+
`SELECT DISTINCT d2.relative_path
|
|
2901
|
+
FROM mentions m
|
|
2902
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
2903
|
+
JOIN documents d1 ON c.document_id = d1.id
|
|
2904
|
+
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
2905
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
2906
|
+
JOIN documents d2 ON der.document_id = d2.id
|
|
2907
|
+
WHERE d1.relative_path IN (${placeholders})
|
|
2908
|
+
AND d2.relative_path NOT IN (${placeholders})
|
|
2909
|
+
AND ${db.localSymbolPredicate}
|
|
2910
|
+
ORDER BY d2.relative_path`,
|
|
2911
|
+
...matchedPaths,
|
|
2912
|
+
...matchedPaths
|
|
2913
|
+
);
|
|
2914
|
+
const dependsOn = depRows.map((r) => r.relative_path).filter((p) => !db.isIgnored(p));
|
|
2915
|
+
const rdepRows = db.all(
|
|
2916
|
+
`SELECT DISTINCT d1.relative_path
|
|
2917
|
+
FROM mentions m
|
|
2918
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
2919
|
+
JOIN documents d1 ON c.document_id = d1.id
|
|
2920
|
+
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
2921
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
2922
|
+
JOIN documents d2 ON der.document_id = d2.id
|
|
2923
|
+
WHERE d2.relative_path IN (${placeholders})
|
|
2924
|
+
AND d1.relative_path NOT IN (${placeholders})
|
|
2925
|
+
ORDER BY d1.relative_path`,
|
|
2926
|
+
...matchedPaths,
|
|
2927
|
+
...matchedPaths
|
|
2928
|
+
);
|
|
2929
|
+
const dependedOnBy = rdepRows.map((r) => r.relative_path).filter((p) => !db.isIgnored(p));
|
|
2930
|
+
return { files: files2, symbols: symbols2, dependsOn, dependedOnBy };
|
|
2156
2931
|
}
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
if (char === "{" || char === "[" || char === "(") depth++;
|
|
2164
|
-
if (char === "}" || char === "]" || char === ")") depth--;
|
|
2165
|
-
if (char === "," && depth === 0) {
|
|
2166
|
-
parts.push(input.slice(start, i));
|
|
2167
|
-
start = i + 1;
|
|
2168
|
-
}
|
|
2932
|
+
|
|
2933
|
+
// src/queries/surface.ts
|
|
2934
|
+
function surface(db, modulePattern) {
|
|
2935
|
+
const matchedPaths = resolveIndexedPaths(db, modulePattern);
|
|
2936
|
+
if (matchedPaths.length === 0) {
|
|
2937
|
+
return [];
|
|
2169
2938
|
}
|
|
2170
|
-
|
|
2171
|
-
|
|
2939
|
+
const placeholders = matchedPaths.map(() => "?").join(", ");
|
|
2940
|
+
const rows = db.all(
|
|
2941
|
+
`SELECT DISTINCT d1.relative_path, gs.symbol
|
|
2942
|
+
FROM mentions m
|
|
2943
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
2944
|
+
JOIN documents d1 ON c.document_id = d1.id
|
|
2945
|
+
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
2946
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
2947
|
+
JOIN documents d2 ON der.document_id = d2.id
|
|
2948
|
+
WHERE d2.relative_path IN (${placeholders})
|
|
2949
|
+
AND d1.relative_path NOT IN (${placeholders})
|
|
2950
|
+
AND ${db.localSymbolPredicate}
|
|
2951
|
+
AND m.role != 1
|
|
2952
|
+
ORDER BY d1.relative_path`,
|
|
2953
|
+
...matchedPaths,
|
|
2954
|
+
...matchedPaths
|
|
2955
|
+
);
|
|
2956
|
+
return rows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({
|
|
2957
|
+
consumer: r.relative_path,
|
|
2958
|
+
symbol: r.symbol,
|
|
2959
|
+
shortName: shortenSymbol(r.symbol)
|
|
2960
|
+
}));
|
|
2172
2961
|
}
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2962
|
+
|
|
2963
|
+
// src/entry-surfaces.ts
|
|
2964
|
+
var liveBarrelCache = /* @__PURE__ */ new WeakMap();
|
|
2965
|
+
function normalizePath2(path2) {
|
|
2966
|
+
return path2.replace(/\\/g, "/");
|
|
2176
2967
|
}
|
|
2177
|
-
function
|
|
2178
|
-
|
|
2968
|
+
function isBarrelFile(path2) {
|
|
2969
|
+
const normalized = normalizePath2(path2);
|
|
2970
|
+
return normalized === "index.ts" || normalized === "index.js" || normalized.endsWith("/index.ts") || normalized.endsWith("/index.js") || normalized.endsWith("/mod.rs") || normalized.endsWith("/__init__.py");
|
|
2179
2971
|
}
|
|
2180
|
-
function
|
|
2181
|
-
|
|
2972
|
+
function isWorkerEntrySurface(path2) {
|
|
2973
|
+
const normalized = normalizePath2(path2);
|
|
2974
|
+
return /(^|\/)[^/]*worker\.(ts|js|mjs|cjs|rs|py|go)$/.test(normalized);
|
|
2182
2975
|
}
|
|
2183
|
-
function
|
|
2184
|
-
const
|
|
2185
|
-
const
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
members2.add(member);
|
|
2190
|
-
}
|
|
2976
|
+
function isStructuralEntrySurface(path2) {
|
|
2977
|
+
const normalized = normalizePath2(path2);
|
|
2978
|
+
const segments = normalized.split("/");
|
|
2979
|
+
const basename2 = segments[segments.length - 1] ?? normalized;
|
|
2980
|
+
if (basename2 === "cli.ts" || basename2 === "cli.js" || basename2 === "postinstall.ts" || basename2 === "postinstall.js" || basename2 === "main.ts" || basename2 === "main.js" || basename2 === "main.rs" || basename2 === "main.go" || basename2 === "main.py") {
|
|
2981
|
+
return true;
|
|
2191
2982
|
}
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
function resolveImportPath(db, importerPath, specifier) {
|
|
2195
|
-
if (isPythonSourcePath(importerPath)) {
|
|
2196
|
-
return resolvePythonImportPath(db, importerPath, specifier);
|
|
2983
|
+
if (basename2 === "index.ts" || basename2 === "index.js") {
|
|
2984
|
+
return segments.length <= 2;
|
|
2197
2985
|
}
|
|
2198
|
-
return
|
|
2986
|
+
return normalized.endsWith("/mod.rs") || normalized.endsWith("/__init__.py");
|
|
2199
2987
|
}
|
|
2200
|
-
function
|
|
2201
|
-
|
|
2202
|
-
|
|
2988
|
+
function getIndexedPaths2(db) {
|
|
2989
|
+
return db.all(
|
|
2990
|
+
`SELECT d.relative_path
|
|
2991
|
+
FROM documents d
|
|
2992
|
+
WHERE 1 = 1
|
|
2993
|
+
${db.pathExclusionsFor("d")}
|
|
2994
|
+
ORDER BY d.relative_path`
|
|
2995
|
+
).map((row) => row.relative_path).filter((path2) => !db.isIgnored(path2));
|
|
2996
|
+
}
|
|
2997
|
+
function getLiveBarrelPaths(db) {
|
|
2998
|
+
const cached = liveBarrelCache.get(db);
|
|
2999
|
+
if (cached) {
|
|
3000
|
+
return cached;
|
|
2203
3001
|
}
|
|
2204
|
-
const
|
|
2205
|
-
const
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
3002
|
+
const graph = buildFileDepGraph(db);
|
|
3003
|
+
const queue = getIndexedPaths2(db).filter(
|
|
3004
|
+
(path2) => isStructuralEntrySurface(path2) || isWorkerEntrySurface(path2)
|
|
3005
|
+
);
|
|
3006
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3007
|
+
const liveBarrels = /* @__PURE__ */ new Set();
|
|
3008
|
+
while (queue.length > 0) {
|
|
3009
|
+
const current = queue.shift();
|
|
3010
|
+
if (visited.has(current)) {
|
|
3011
|
+
continue;
|
|
2211
3012
|
}
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
function resolvePythonImportPath(db, importerPath, specifier) {
|
|
2216
|
-
const indexedPaths = getIndexedPaths2(db);
|
|
2217
|
-
let basePath;
|
|
2218
|
-
if (specifier.startsWith(".")) {
|
|
2219
|
-
const match = specifier.match(/^(\.+)(.*)$/);
|
|
2220
|
-
if (!match) return null;
|
|
2221
|
-
const dots = match[1].length;
|
|
2222
|
-
const remainder = match[2].replace(/^\./, "");
|
|
2223
|
-
let baseDir = dirname2(join6(db.config.projectRoot, importerPath));
|
|
2224
|
-
for (let i = 1; i < dots; i++) {
|
|
2225
|
-
baseDir = dirname2(baseDir);
|
|
3013
|
+
visited.add(current);
|
|
3014
|
+
if (isBarrelFile(current)) {
|
|
3015
|
+
liveBarrels.add(current);
|
|
2226
3016
|
}
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
for (const candidate of pythonCandidateImportPaths(basePath)) {
|
|
2232
|
-
const relativeCandidate = normalizePath2(relative2(db.config.projectRoot, candidate));
|
|
2233
|
-
if (indexedPaths.has(relativeCandidate) || existsSync6(candidate)) {
|
|
2234
|
-
return relativeCandidate;
|
|
3017
|
+
for (const dep of graph.get(current) ?? []) {
|
|
3018
|
+
if (!visited.has(dep)) {
|
|
3019
|
+
queue.push(dep);
|
|
3020
|
+
}
|
|
2235
3021
|
}
|
|
2236
3022
|
}
|
|
2237
|
-
|
|
3023
|
+
liveBarrelCache.set(db, liveBarrels);
|
|
3024
|
+
return liveBarrels;
|
|
2238
3025
|
}
|
|
2239
|
-
function
|
|
2240
|
-
|
|
2241
|
-
if (PYTHON_SOURCE_EXTENSIONS.includes(ext)) {
|
|
2242
|
-
return [basePath];
|
|
2243
|
-
}
|
|
2244
|
-
return [
|
|
2245
|
-
`${basePath}.py`,
|
|
2246
|
-
`${basePath}.pyi`,
|
|
2247
|
-
join6(basePath, "__init__.py"),
|
|
2248
|
-
join6(basePath, "__init__.pyi")
|
|
2249
|
-
];
|
|
3026
|
+
function isLiveBarrel(db, path2) {
|
|
3027
|
+
return getLiveBarrelPaths(db).has(normalizePath2(path2));
|
|
2250
3028
|
}
|
|
2251
|
-
function
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
3029
|
+
function isEntrySurface(db, path2) {
|
|
3030
|
+
return isStructuralEntrySurface(path2) || isWorkerEntrySurface(path2) || isLiveBarrel(db, path2);
|
|
3031
|
+
}
|
|
3032
|
+
function getInactiveBarrelPaths(db) {
|
|
3033
|
+
const liveBarrels = getLiveBarrelPaths(db);
|
|
3034
|
+
return getIndexedPaths2(db).filter((path2) => isBarrelFile(path2)).filter((path2) => !liveBarrels.has(path2));
|
|
3035
|
+
}
|
|
3036
|
+
|
|
3037
|
+
// src/queries/dead.ts
|
|
3038
|
+
function dead(db, opts = {}) {
|
|
3039
|
+
const {
|
|
3040
|
+
scope,
|
|
3041
|
+
minLoc = 1,
|
|
3042
|
+
includeTests = false,
|
|
3043
|
+
skipBarrels = false,
|
|
3044
|
+
includeMembers = false
|
|
3045
|
+
} = opts;
|
|
3046
|
+
const params = [minLoc];
|
|
3047
|
+
let testFileExclusions = "";
|
|
3048
|
+
let memberExclusion = "";
|
|
3049
|
+
let barrelExclusions = "";
|
|
3050
|
+
if (scope) {
|
|
3051
|
+
params.push(`%${scope}%`);
|
|
3052
|
+
}
|
|
3053
|
+
if (!includeTests) {
|
|
3054
|
+
testFileExclusions = `
|
|
3055
|
+
AND ${testFileExclusionSql("d", TEST_SUPPORT_PATH_PATTERNS)}
|
|
3056
|
+
`;
|
|
3057
|
+
}
|
|
3058
|
+
if (!includeMembers) {
|
|
3059
|
+
memberExclusion = `AND gs.symbol NOT LIKE '%#%'`;
|
|
3060
|
+
}
|
|
3061
|
+
if (skipBarrels) {
|
|
3062
|
+
const inactiveBarrelPaths = getInactiveBarrelPaths(db);
|
|
3063
|
+
if (inactiveBarrelPaths.length > 0) {
|
|
3064
|
+
barrelExclusions = `AND ref_d.relative_path NOT IN (${inactiveBarrelPaths.map(() => "?").join(", ")})`;
|
|
3065
|
+
params.push(...inactiveBarrelPaths);
|
|
2263
3066
|
}
|
|
2264
3067
|
}
|
|
2265
|
-
|
|
3068
|
+
const sql = `
|
|
3069
|
+
SELECT
|
|
3070
|
+
d.relative_path,
|
|
3071
|
+
der.start_line,
|
|
3072
|
+
der.end_line,
|
|
3073
|
+
(der.end_line - der.start_line + 1) AS loc,
|
|
3074
|
+
gs.symbol,
|
|
3075
|
+
(SELECT COUNT(*) FROM mentions m2
|
|
3076
|
+
JOIN chunks c2 ON m2.chunk_id = c2.id
|
|
3077
|
+
WHERE m2.symbol_id = gs.id AND m2.role != 1 AND c2.document_id = d.id
|
|
3078
|
+
) AS same_file_refs
|
|
3079
|
+
FROM global_symbols gs
|
|
3080
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
3081
|
+
JOIN documents d ON der.document_id = d.id
|
|
3082
|
+
WHERE 1 = 1
|
|
3083
|
+
${db.pathExclusionsFor("d")}
|
|
3084
|
+
${db.symbolNoiseFor("gs")}
|
|
3085
|
+
AND (der.end_line - der.start_line + 1) >= ?
|
|
3086
|
+
${scope ? "AND d.relative_path LIKE ?" : ""}
|
|
3087
|
+
${testFileExclusions}
|
|
3088
|
+
${memberExclusion}
|
|
3089
|
+
AND NOT EXISTS (
|
|
3090
|
+
SELECT 1
|
|
3091
|
+
FROM mentions ref_m
|
|
3092
|
+
JOIN chunks ref_c ON ref_m.chunk_id = ref_c.id
|
|
3093
|
+
JOIN documents ref_d ON ref_c.document_id = ref_d.id
|
|
3094
|
+
WHERE ref_m.symbol_id = gs.id
|
|
3095
|
+
AND ref_m.role != 1
|
|
3096
|
+
AND ref_d.id != d.id
|
|
3097
|
+
${barrelExclusions}
|
|
3098
|
+
)
|
|
3099
|
+
ORDER BY (der.end_line - der.start_line + 1) DESC, d.relative_path, der.start_line
|
|
3100
|
+
`;
|
|
3101
|
+
const rows = db.all(sql, ...params);
|
|
3102
|
+
let deadCodeCount = 0;
|
|
3103
|
+
let fileInternalCount = 0;
|
|
3104
|
+
let totalLoc = 0;
|
|
3105
|
+
const symbols2 = rows.filter((r) => !db.isIgnored(r.relative_path)).filter((r) => !isEntrySurface(db, r.relative_path)).map((r) => {
|
|
3106
|
+
const kind = r.same_file_refs === 0 ? "dead-code" : "file-internal";
|
|
3107
|
+
if (kind === "dead-code") deadCodeCount++;
|
|
3108
|
+
else fileInternalCount++;
|
|
3109
|
+
totalLoc += r.loc;
|
|
3110
|
+
return {
|
|
3111
|
+
relativePath: r.relative_path,
|
|
3112
|
+
startLine: r.start_line,
|
|
3113
|
+
endLine: r.end_line,
|
|
3114
|
+
loc: r.loc,
|
|
3115
|
+
symbol: r.symbol,
|
|
3116
|
+
shortName: shortenSymbol(r.symbol),
|
|
3117
|
+
sameFileRefs: r.same_file_refs,
|
|
3118
|
+
kind
|
|
3119
|
+
};
|
|
3120
|
+
});
|
|
3121
|
+
return {
|
|
3122
|
+
symbols: symbols2,
|
|
3123
|
+
totalCount: symbols2.length,
|
|
3124
|
+
deadCodeCount,
|
|
3125
|
+
fileInternalCount,
|
|
3126
|
+
totalLoc
|
|
3127
|
+
};
|
|
2266
3128
|
}
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
}
|
|
2272
|
-
const
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
3129
|
+
|
|
3130
|
+
// src/queries/hotspots.ts
|
|
3131
|
+
function hotspots(db, opts = {}) {
|
|
3132
|
+
const { limit = 30, scope } = opts;
|
|
3133
|
+
const scopeFilter = scope ? `AND def_d.relative_path LIKE '%${scope}%'` : "";
|
|
3134
|
+
const rows = db.all(
|
|
3135
|
+
`SELECT
|
|
3136
|
+
gs.symbol,
|
|
3137
|
+
COUNT(*) AS ref_count,
|
|
3138
|
+
COUNT(DISTINCT ref_d.id) AS file_count,
|
|
3139
|
+
def_d.relative_path AS defined_in
|
|
3140
|
+
FROM mentions m
|
|
3141
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
3142
|
+
JOIN documents ref_d ON c.document_id = ref_d.id
|
|
3143
|
+
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
3144
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
3145
|
+
JOIN documents def_d ON der.document_id = def_d.id
|
|
3146
|
+
WHERE m.role != 1
|
|
3147
|
+
${db.pathExclusionsFor("def_d")}
|
|
3148
|
+
${db.symbolNoiseFor("gs")}
|
|
3149
|
+
${scopeFilter}
|
|
3150
|
+
GROUP BY gs.id
|
|
3151
|
+
ORDER BY ref_count DESC
|
|
3152
|
+
LIMIT ?`,
|
|
3153
|
+
limit
|
|
2279
3154
|
);
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
cache.set(db, map);
|
|
2288
|
-
}
|
|
2289
|
-
return map;
|
|
2290
|
-
}
|
|
2291
|
-
function normalizePath2(path2) {
|
|
2292
|
-
return path2.replace(/\\/g, "/");
|
|
2293
|
-
}
|
|
2294
|
-
function isPythonSourcePath(relativePath) {
|
|
2295
|
-
return PYTHON_SOURCE_EXTENSIONS.includes(extname(relativePath).toLowerCase());
|
|
2296
|
-
}
|
|
2297
|
-
function pythonParenBalance(value) {
|
|
2298
|
-
let balance = 0;
|
|
2299
|
-
for (const char of value) {
|
|
2300
|
-
if (char === "(") balance++;
|
|
2301
|
-
if (char === ")") balance--;
|
|
2302
|
-
}
|
|
2303
|
-
return balance;
|
|
2304
|
-
}
|
|
2305
|
-
function escapeRegex(value) {
|
|
2306
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3155
|
+
return rows.filter((r) => !db.isIgnored(r.defined_in)).map((r) => ({
|
|
3156
|
+
symbol: r.symbol,
|
|
3157
|
+
shortName: shortenSymbol(r.symbol),
|
|
3158
|
+
refCount: r.ref_count,
|
|
3159
|
+
fileCount: r.file_count,
|
|
3160
|
+
definedIn: r.defined_in
|
|
3161
|
+
}));
|
|
2307
3162
|
}
|
|
2308
3163
|
|
|
2309
3164
|
// src/queries/imports.ts
|
|
2310
3165
|
function imports(db, filePattern) {
|
|
3166
|
+
const importer = resolveIndexedFile(db, filePattern);
|
|
3167
|
+
if (!importer) return [];
|
|
2311
3168
|
const rows = db.all(
|
|
2312
3169
|
`SELECT DISTINCT gs.symbol, def_d.relative_path AS from_file, imp_d.relative_path AS importer
|
|
2313
3170
|
FROM mentions m
|
|
@@ -2316,10 +3173,10 @@ function imports(db, filePattern) {
|
|
|
2316
3173
|
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
2317
3174
|
LEFT JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
2318
3175
|
LEFT JOIN documents def_d ON der.document_id = def_d.id
|
|
2319
|
-
WHERE imp_d.relative_path
|
|
3176
|
+
WHERE imp_d.relative_path = ?
|
|
2320
3177
|
AND m.role = 2
|
|
2321
3178
|
ORDER BY def_d.relative_path, gs.symbol`,
|
|
2322
|
-
|
|
3179
|
+
importer
|
|
2323
3180
|
);
|
|
2324
3181
|
const indexedResults = rows.filter((row) => !db.isIgnored(row.importer)).map((r) => ({
|
|
2325
3182
|
symbol: r.symbol,
|
|
@@ -2329,8 +3186,6 @@ function imports(db, filePattern) {
|
|
|
2329
3186
|
if (indexedResults.length > 0) {
|
|
2330
3187
|
return indexedResults;
|
|
2331
3188
|
}
|
|
2332
|
-
const importer = findIndexedFile(db, filePattern);
|
|
2333
|
-
if (!importer) return [];
|
|
2334
3189
|
return getSourceImports(db, importer).map((entry) => ({
|
|
2335
3190
|
symbol: renderImportSymbol(entry.importedName, entry.localName, entry.kind),
|
|
2336
3191
|
shortName: renderImportSymbol(entry.importedName, entry.localName, entry.kind),
|
|
@@ -2396,13 +3251,15 @@ function importedBy(db, symbolPattern) {
|
|
|
2396
3251
|
}));
|
|
2397
3252
|
}
|
|
2398
3253
|
function unusedImports(db, filePattern) {
|
|
3254
|
+
const importer = resolveIndexedFile(db, filePattern);
|
|
3255
|
+
if (!importer) return [];
|
|
2399
3256
|
const rows = db.all(
|
|
2400
3257
|
`SELECT gs.symbol, d.relative_path AS imported_in, d.relative_path AS importer
|
|
2401
3258
|
FROM mentions m
|
|
2402
3259
|
JOIN chunks c ON m.chunk_id = c.id
|
|
2403
3260
|
JOIN documents d ON c.document_id = d.id
|
|
2404
3261
|
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
2405
|
-
WHERE d.relative_path
|
|
3262
|
+
WHERE d.relative_path = ?
|
|
2406
3263
|
AND m.role = 2
|
|
2407
3264
|
AND NOT EXISTS (
|
|
2408
3265
|
SELECT 1
|
|
@@ -2413,7 +3270,7 @@ function unusedImports(db, filePattern) {
|
|
|
2413
3270
|
AND ref_c.document_id = d.id
|
|
2414
3271
|
)
|
|
2415
3272
|
ORDER BY d.relative_path, gs.symbol`,
|
|
2416
|
-
|
|
3273
|
+
importer
|
|
2417
3274
|
);
|
|
2418
3275
|
const indexedResults = rows.filter((row) => !db.isIgnored(row.importer)).map((r) => ({
|
|
2419
3276
|
symbol: r.symbol,
|
|
@@ -2423,28 +3280,12 @@ function unusedImports(db, filePattern) {
|
|
|
2423
3280
|
if (indexedResults.length > 0) {
|
|
2424
3281
|
return indexedResults;
|
|
2425
3282
|
}
|
|
2426
|
-
const importer = findIndexedFile(db, filePattern);
|
|
2427
|
-
if (!importer) return [];
|
|
2428
3283
|
return getSourceImports(db, importer).filter((entry) => entry.kind !== "side-effect" && !entry.used).map((entry) => ({
|
|
2429
3284
|
symbol: renderImportSymbol(entry.importedName, entry.localName, entry.kind),
|
|
2430
3285
|
shortName: renderImportSymbol(entry.importedName, entry.localName, entry.kind),
|
|
2431
3286
|
importedIn: importer
|
|
2432
3287
|
}));
|
|
2433
3288
|
}
|
|
2434
|
-
function findIndexedFile(db, filePattern) {
|
|
2435
|
-
const doc = db.get(
|
|
2436
|
-
`SELECT relative_path
|
|
2437
|
-
FROM documents
|
|
2438
|
-
WHERE relative_path LIKE ?
|
|
2439
|
-
${db.pathExclusionsFor("documents")}
|
|
2440
|
-
LIMIT 1`,
|
|
2441
|
-
`%${filePattern}%`
|
|
2442
|
-
);
|
|
2443
|
-
if (!doc || db.isIgnored(doc.relative_path)) {
|
|
2444
|
-
return null;
|
|
2445
|
-
}
|
|
2446
|
-
return doc.relative_path;
|
|
2447
|
-
}
|
|
2448
3289
|
function renderImportSymbol(importedName, localName, kind) {
|
|
2449
3290
|
if (kind === "namespace" && importedName === "*" && localName) {
|
|
2450
3291
|
return `* as ${localName}`;
|
|
@@ -2466,15 +3307,20 @@ function normalizePath3(path2) {
|
|
|
2466
3307
|
|
|
2467
3308
|
// src/queries/outline.ts
|
|
2468
3309
|
function outline(db, filePattern) {
|
|
3310
|
+
const resolvedPaths = resolveIndexedPaths(db, filePattern);
|
|
3311
|
+
if (resolvedPaths.length === 0) {
|
|
3312
|
+
return [];
|
|
3313
|
+
}
|
|
3314
|
+
const placeholders = resolvedPaths.map(() => "?").join(", ");
|
|
2469
3315
|
const rows = db.all(
|
|
2470
3316
|
`SELECT gs.symbol, gs.enclosing_symbol, der.start_line, der.end_line
|
|
2471
3317
|
FROM defn_enclosing_ranges der
|
|
2472
3318
|
JOIN global_symbols gs ON der.symbol_id = gs.id
|
|
2473
3319
|
JOIN documents d ON der.document_id = d.id
|
|
2474
|
-
WHERE d.relative_path
|
|
3320
|
+
WHERE d.relative_path IN (${placeholders})
|
|
2475
3321
|
${db.symbolNoise}
|
|
2476
|
-
ORDER BY der.start_line`,
|
|
2477
|
-
|
|
3322
|
+
ORDER BY d.relative_path, der.start_line`,
|
|
3323
|
+
...resolvedPaths
|
|
2478
3324
|
);
|
|
2479
3325
|
const nodeMap = /* @__PURE__ */ new Map();
|
|
2480
3326
|
const roots = [];
|
|
@@ -2542,6 +3388,10 @@ function fanIn(db, symbolPattern) {
|
|
|
2542
3388
|
}));
|
|
2543
3389
|
}
|
|
2544
3390
|
function fanOut(db, filePattern) {
|
|
3391
|
+
const resolvedFile = resolveIndexedFile(db, filePattern);
|
|
3392
|
+
if (!resolvedFile) {
|
|
3393
|
+
return [];
|
|
3394
|
+
}
|
|
2545
3395
|
const rows = db.all(
|
|
2546
3396
|
`SELECT d.relative_path, COUNT(DISTINCT gs.id) AS symbol_count
|
|
2547
3397
|
FROM mentions m
|
|
@@ -2550,12 +3400,12 @@ function fanOut(db, filePattern) {
|
|
|
2550
3400
|
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
2551
3401
|
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
2552
3402
|
JOIN documents def_d ON der.document_id = def_d.id
|
|
2553
|
-
WHERE d.relative_path
|
|
3403
|
+
WHERE d.relative_path = ?
|
|
2554
3404
|
AND m.role != 1
|
|
2555
3405
|
AND def_d.id != d.id
|
|
2556
3406
|
GROUP BY d.id
|
|
2557
3407
|
ORDER BY symbol_count DESC`,
|
|
2558
|
-
|
|
3408
|
+
resolvedFile
|
|
2559
3409
|
);
|
|
2560
3410
|
return rows.filter((r) => !db.isIgnored(r.relative_path)).map((r) => ({
|
|
2561
3411
|
name: r.relative_path,
|
|
@@ -2616,6 +3466,8 @@ function topFanOut(db, opts = {}) {
|
|
|
2616
3466
|
|
|
2617
3467
|
// src/queries/coupling.ts
|
|
2618
3468
|
function coupling(db, file1, file2) {
|
|
3469
|
+
const resolvedFile1 = resolveIndexedFile(db, file1) ?? file1;
|
|
3470
|
+
const resolvedFile2 = resolveIndexedFile(db, file2) ?? file2;
|
|
2619
3471
|
const row = db.get(
|
|
2620
3472
|
`SELECT COUNT(DISTINCT gs.id) AS shared
|
|
2621
3473
|
FROM global_symbols gs
|
|
@@ -2624,36 +3476,36 @@ function coupling(db, file1, file2) {
|
|
|
2624
3476
|
EXISTS (
|
|
2625
3477
|
SELECT 1 FROM defn_enclosing_ranges der
|
|
2626
3478
|
JOIN documents d ON der.document_id = d.id
|
|
2627
|
-
WHERE der.symbol_id = gs.id AND d.relative_path
|
|
3479
|
+
WHERE der.symbol_id = gs.id AND d.relative_path = ?
|
|
2628
3480
|
)
|
|
2629
3481
|
AND EXISTS (
|
|
2630
3482
|
SELECT 1 FROM mentions m
|
|
2631
3483
|
JOIN chunks c ON m.chunk_id = c.id
|
|
2632
3484
|
JOIN documents d ON c.document_id = d.id
|
|
2633
|
-
WHERE m.symbol_id = gs.id AND m.role != 1 AND d.relative_path
|
|
3485
|
+
WHERE m.symbol_id = gs.id AND m.role != 1 AND d.relative_path = ?
|
|
2634
3486
|
)
|
|
2635
3487
|
) OR (
|
|
2636
3488
|
-- Defined in file2, referenced in file1
|
|
2637
3489
|
EXISTS (
|
|
2638
3490
|
SELECT 1 FROM defn_enclosing_ranges der
|
|
2639
3491
|
JOIN documents d ON der.document_id = d.id
|
|
2640
|
-
WHERE der.symbol_id = gs.id AND d.relative_path
|
|
3492
|
+
WHERE der.symbol_id = gs.id AND d.relative_path = ?
|
|
2641
3493
|
)
|
|
2642
3494
|
AND EXISTS (
|
|
2643
3495
|
SELECT 1 FROM mentions m
|
|
2644
3496
|
JOIN chunks c ON m.chunk_id = c.id
|
|
2645
3497
|
JOIN documents d ON c.document_id = d.id
|
|
2646
|
-
WHERE m.symbol_id = gs.id AND m.role != 1 AND d.relative_path
|
|
3498
|
+
WHERE m.symbol_id = gs.id AND m.role != 1 AND d.relative_path = ?
|
|
2647
3499
|
)
|
|
2648
3500
|
)`,
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
3501
|
+
resolvedFile1,
|
|
3502
|
+
resolvedFile2,
|
|
3503
|
+
resolvedFile2,
|
|
3504
|
+
resolvedFile1
|
|
2653
3505
|
);
|
|
2654
3506
|
return {
|
|
2655
|
-
file1,
|
|
2656
|
-
file2,
|
|
3507
|
+
file1: resolvedFile1,
|
|
3508
|
+
file2: resolvedFile2,
|
|
2657
3509
|
sharedSymbols: row?.shared ?? 0
|
|
2658
3510
|
};
|
|
2659
3511
|
}
|
|
@@ -2949,7 +3801,7 @@ function byKind(db, kindQuery, opts = {}) {
|
|
|
2949
3801
|
`SELECT COUNT(*) AS c FROM global_symbols WHERE kind IS NOT NULL`
|
|
2950
3802
|
);
|
|
2951
3803
|
if (!hasKinds || hasKinds.c === 0) {
|
|
2952
|
-
return
|
|
3804
|
+
return inferByKind(db, kindNum, scope, limit);
|
|
2953
3805
|
}
|
|
2954
3806
|
const rows = db.all(
|
|
2955
3807
|
`SELECT gs.symbol, gs.kind, d.relative_path, der.start_line, der.end_line
|
|
@@ -2989,12 +3841,89 @@ function kindCounts(db, opts = {}) {
|
|
|
2989
3841
|
GROUP BY gs.kind
|
|
2990
3842
|
ORDER BY cnt DESC`
|
|
2991
3843
|
);
|
|
3844
|
+
if (rows.length === 0) {
|
|
3845
|
+
return inferKindCounts(db, opts.scope);
|
|
3846
|
+
}
|
|
2992
3847
|
return rows.map((r) => ({
|
|
2993
3848
|
kind: r.kind,
|
|
2994
3849
|
kindName: KIND_NAMES[r.kind] ?? "Unknown",
|
|
2995
3850
|
count: r.cnt
|
|
2996
3851
|
}));
|
|
2997
3852
|
}
|
|
3853
|
+
function inferByKind(db, kindNum, scope, limit = 100) {
|
|
3854
|
+
const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : "";
|
|
3855
|
+
const rows = db.all(
|
|
3856
|
+
`SELECT gs.symbol, d.relative_path, der.start_line, der.end_line, gs.documentation, gs.enclosing_symbol
|
|
3857
|
+
FROM global_symbols gs
|
|
3858
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
3859
|
+
JOIN documents d ON der.document_id = d.id
|
|
3860
|
+
WHERE 1 = 1
|
|
3861
|
+
${db.pathExclusionsFor("d")}
|
|
3862
|
+
${scopeFilter}
|
|
3863
|
+
ORDER BY d.relative_path, der.start_line`
|
|
3864
|
+
);
|
|
3865
|
+
return rows.filter((row) => !db.isIgnored(row.relative_path)).map((row) => ({
|
|
3866
|
+
row,
|
|
3867
|
+
inferredKind: inferKindNumber(row.symbol, row.documentation, row.enclosing_symbol)
|
|
3868
|
+
})).filter((entry) => entry.inferredKind === kindNum).slice(0, limit).map(({ row, inferredKind }) => ({
|
|
3869
|
+
symbol: row.symbol,
|
|
3870
|
+
shortName: shortenSymbol(row.symbol),
|
|
3871
|
+
kind: inferredKind,
|
|
3872
|
+
kindName: KIND_NAMES[inferredKind] ?? "Unknown",
|
|
3873
|
+
relativePath: row.relative_path,
|
|
3874
|
+
startLine: row.start_line,
|
|
3875
|
+
endLine: row.end_line
|
|
3876
|
+
}));
|
|
3877
|
+
}
|
|
3878
|
+
function inferKindCounts(db, scope) {
|
|
3879
|
+
const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : "";
|
|
3880
|
+
const rows = db.all(
|
|
3881
|
+
`SELECT gs.symbol, gs.documentation, gs.enclosing_symbol, d.relative_path
|
|
3882
|
+
FROM global_symbols gs
|
|
3883
|
+
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
3884
|
+
JOIN documents d ON der.document_id = d.id
|
|
3885
|
+
WHERE 1 = 1
|
|
3886
|
+
${db.pathExclusionsFor("d")}
|
|
3887
|
+
${scopeFilter}`
|
|
3888
|
+
);
|
|
3889
|
+
const counts = /* @__PURE__ */ new Map();
|
|
3890
|
+
for (const row of rows) {
|
|
3891
|
+
if (db.isIgnored(row.relative_path)) continue;
|
|
3892
|
+
const inferred = inferKindNumber(row.symbol, row.documentation, row.enclosing_symbol);
|
|
3893
|
+
if (inferred === null || inferred === 0) continue;
|
|
3894
|
+
counts.set(inferred, (counts.get(inferred) ?? 0) + 1);
|
|
3895
|
+
}
|
|
3896
|
+
return [...counts.entries()].sort((a, b) => b[1] - a[1] || a[0] - b[0]).map(([kind, count]) => ({
|
|
3897
|
+
kind,
|
|
3898
|
+
kindName: KIND_NAMES[kind] ?? "Unknown",
|
|
3899
|
+
count
|
|
3900
|
+
}));
|
|
3901
|
+
}
|
|
3902
|
+
function inferKindNumber(symbol, documentation, enclosingSymbol) {
|
|
3903
|
+
const parsed = parseSymbol(symbol);
|
|
3904
|
+
if ("kind" in parsed) {
|
|
3905
|
+
return null;
|
|
3906
|
+
}
|
|
3907
|
+
const descriptors = parsed.descriptors;
|
|
3908
|
+
const last = descriptors[descriptors.length - 1] ?? null;
|
|
3909
|
+
const parent = descriptors[descriptors.length - 2] ?? null;
|
|
3910
|
+
const suffix = leafSuffix(symbol);
|
|
3911
|
+
if (suffix === "type") return 9;
|
|
3912
|
+
if (suffix === "method") {
|
|
3913
|
+
return parent?.suffix === "type" ? 33 : 23;
|
|
3914
|
+
}
|
|
3915
|
+
if (suffix === "namespace") return 39;
|
|
3916
|
+
if (suffix !== "term") return null;
|
|
3917
|
+
const signature = (documentation ?? "").toLowerCase();
|
|
3918
|
+
if (signature.includes("async def ") || signature.includes("def ")) {
|
|
3919
|
+
return 23;
|
|
3920
|
+
}
|
|
3921
|
+
const enclosingSuffix = enclosingSymbol ? leafSuffix(enclosingSymbol) : parent?.suffix ?? null;
|
|
3922
|
+
if (enclosingSuffix === "type") {
|
|
3923
|
+
return 21;
|
|
3924
|
+
}
|
|
3925
|
+
return 83;
|
|
3926
|
+
}
|
|
2998
3927
|
|
|
2999
3928
|
// src/queries/doc-coverage.ts
|
|
3000
3929
|
function docCoverage(db, opts = {}) {
|
|
@@ -3158,36 +4087,15 @@ function hierarchy(db, symbolPattern) {
|
|
|
3158
4087
|
function callGraph(db, symbolPattern) {
|
|
3159
4088
|
const target = findFirstSymbolMatch(db, symbolPattern);
|
|
3160
4089
|
if (!target) return null;
|
|
3161
|
-
const callerRows = db
|
|
3162
|
-
|
|
3163
|
-
FROM mentions m
|
|
3164
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
3165
|
-
JOIN documents ref_d ON c.document_id = ref_d.id
|
|
3166
|
-
-- Find the enclosing symbol for where the reference appears
|
|
3167
|
-
JOIN defn_enclosing_ranges caller_der
|
|
3168
|
-
ON caller_der.document_id = ref_d.id
|
|
3169
|
-
AND c.start_line >= caller_der.start_line
|
|
3170
|
-
AND c.end_line <= caller_der.end_line
|
|
3171
|
-
JOIN global_symbols caller_gs ON caller_der.symbol_id = caller_gs.id
|
|
3172
|
-
JOIN documents caller_d ON caller_der.document_id = caller_d.id
|
|
3173
|
-
WHERE m.symbol_id = ?
|
|
3174
|
-
AND m.role != 1
|
|
3175
|
-
AND caller_gs.id != ?
|
|
3176
|
-
${db.symbolNoiseFor("caller_gs")}
|
|
3177
|
-
${db.pathExclusionsFor("caller_d")}
|
|
3178
|
-
ORDER BY caller_d.relative_path
|
|
3179
|
-
LIMIT 50`,
|
|
3180
|
-
target.symbolId,
|
|
3181
|
-
target.symbolId
|
|
3182
|
-
);
|
|
3183
|
-
const calleeRows = getCalleeRowsForSymbol(db, target, { limit: 50 });
|
|
4090
|
+
const callerRows = getCallerRowsForSymbol(db, target, { limit: 50 });
|
|
4091
|
+
const calleeRows = uniqueRows(getCalleeRowsForSymbol(db, target, { limit: 50 }));
|
|
3184
4092
|
return {
|
|
3185
4093
|
symbol: target.symbol,
|
|
3186
4094
|
shortName: shortenSymbol(target.symbol),
|
|
3187
|
-
callers: callerRows.
|
|
3188
|
-
symbol: r.
|
|
3189
|
-
shortName: shortenSymbol(r.
|
|
3190
|
-
file: r.
|
|
4095
|
+
callers: callerRows.map((r) => ({
|
|
4096
|
+
symbol: r.symbol,
|
|
4097
|
+
shortName: shortenSymbol(r.symbol),
|
|
4098
|
+
file: r.file
|
|
3191
4099
|
})),
|
|
3192
4100
|
callees: calleeRows.map((r) => ({
|
|
3193
4101
|
symbol: r.symbol,
|
|
@@ -3196,6 +4104,17 @@ function callGraph(db, symbolPattern) {
|
|
|
3196
4104
|
}))
|
|
3197
4105
|
};
|
|
3198
4106
|
}
|
|
4107
|
+
function uniqueRows(rows) {
|
|
4108
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4109
|
+
const unique = [];
|
|
4110
|
+
for (const row of rows) {
|
|
4111
|
+
const key = `${row.symbol}|${row.file}`;
|
|
4112
|
+
if (seen.has(key)) continue;
|
|
4113
|
+
seen.add(key);
|
|
4114
|
+
unique.push(row);
|
|
4115
|
+
}
|
|
4116
|
+
return unique;
|
|
4117
|
+
}
|
|
3199
4118
|
|
|
3200
4119
|
// src/queries/similar.ts
|
|
3201
4120
|
function similar(db, symbolPattern, opts = {}) {
|
|
@@ -3502,8 +4421,8 @@ function similarChains(db, opts = {}) {
|
|
|
3502
4421
|
}
|
|
3503
4422
|
const structuralNames = ["index.ts", "index.js", "cli.ts", "main.ts", "health.ts", "health.js"];
|
|
3504
4423
|
for (const node of nodeFreq.keys()) {
|
|
3505
|
-
const
|
|
3506
|
-
if (structuralNames.includes(
|
|
4424
|
+
const basename2 = node.split("/").pop() ?? "";
|
|
4425
|
+
if (structuralNames.includes(basename2)) infraNodes.add(node);
|
|
3507
4426
|
}
|
|
3508
4427
|
const filteredChains = [];
|
|
3509
4428
|
for (const chain of rawChains) {
|
|
@@ -3690,8 +4609,8 @@ function extractCandidates(db, opts = {}) {
|
|
|
3690
4609
|
const results = [];
|
|
3691
4610
|
for (const sym of symbols2) {
|
|
3692
4611
|
if (db.isIgnored(sym.relative_path)) continue;
|
|
3693
|
-
const
|
|
3694
|
-
if (
|
|
4612
|
+
const basename2 = sym.relative_path.split("/").pop() ?? "";
|
|
4613
|
+
if (basename2.includes("types")) continue;
|
|
3695
4614
|
const calleeChunks = getCalleeRowsForSymbol(db, {
|
|
3696
4615
|
documentId: sym.document_id,
|
|
3697
4616
|
startLine: sym.start_line,
|
|
@@ -3778,63 +4697,119 @@ function affected(db, symbolPattern, opts = {}) {
|
|
|
3778
4697
|
const { maxDepth = 5, scope } = opts;
|
|
3779
4698
|
const target = findFirstSymbolMatch(db, symbolPattern);
|
|
3780
4699
|
if (!target) return [];
|
|
3781
|
-
const scopeFilter = scope ? `AND enc_d.relative_path LIKE '%${scope}%'` : "";
|
|
3782
4700
|
const results = [];
|
|
3783
4701
|
const visited = /* @__PURE__ */ new Set([target.symbolId]);
|
|
3784
|
-
|
|
4702
|
+
const seenResults = /* @__PURE__ */ new Set();
|
|
4703
|
+
let frontier = [target];
|
|
3785
4704
|
for (let depth = 1; depth <= maxDepth; depth++) {
|
|
3786
|
-
if (frontier.
|
|
3787
|
-
const
|
|
3788
|
-
const
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
${scopeFilter}`,
|
|
3809
|
-
...[...frontier],
|
|
3810
|
-
...[...frontier]
|
|
3811
|
-
);
|
|
3812
|
-
for (const row of rows) {
|
|
3813
|
-
if (visited.has(row.symbol_id)) continue;
|
|
3814
|
-
if (db.isIgnored(row.relative_path)) continue;
|
|
3815
|
-
visited.add(row.symbol_id);
|
|
3816
|
-
nextFrontier.add(row.symbol_id);
|
|
3817
|
-
results.push({
|
|
3818
|
-
symbol: row.symbol,
|
|
3819
|
-
shortName: shortenSymbol(row.symbol),
|
|
3820
|
-
file: row.relative_path,
|
|
3821
|
-
depth
|
|
3822
|
-
});
|
|
4705
|
+
if (frontier.length === 0) break;
|
|
4706
|
+
const nextFrontier = [];
|
|
4707
|
+
for (const current of frontier) {
|
|
4708
|
+
for (const row of getDirectAffectedRows(db, current, scope)) {
|
|
4709
|
+
const resultKey = `${row.file}|${row.shortName}`;
|
|
4710
|
+
if (row.symbolId !== null) {
|
|
4711
|
+
if (visited.has(row.symbolId)) continue;
|
|
4712
|
+
visited.add(row.symbolId);
|
|
4713
|
+
} else if (seenResults.has(resultKey)) {
|
|
4714
|
+
continue;
|
|
4715
|
+
}
|
|
4716
|
+
seenResults.add(resultKey);
|
|
4717
|
+
results.push({
|
|
4718
|
+
symbol: row.symbol,
|
|
4719
|
+
shortName: row.shortName,
|
|
4720
|
+
file: row.file,
|
|
4721
|
+
depth
|
|
4722
|
+
});
|
|
4723
|
+
if (row.symbolId !== null && row.symbolMatch) {
|
|
4724
|
+
nextFrontier.push(row.symbolMatch);
|
|
4725
|
+
}
|
|
4726
|
+
}
|
|
3823
4727
|
}
|
|
3824
4728
|
frontier = nextFrontier;
|
|
3825
4729
|
}
|
|
3826
4730
|
results.sort((a, b) => a.depth - b.depth || a.file.localeCompare(b.file));
|
|
3827
4731
|
return results;
|
|
3828
4732
|
}
|
|
4733
|
+
function getDirectAffectedRows(db, target, scope) {
|
|
4734
|
+
const sourceSites = getSourceReferenceSites(db, target).filter((site) => !db.isIgnored(site.file)).filter((site) => !scope || site.file.includes(scope));
|
|
4735
|
+
if (sourceSites.length > 0) {
|
|
4736
|
+
const rows2 = [];
|
|
4737
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4738
|
+
for (const site of sourceSites) {
|
|
4739
|
+
if (!site.enclosingSymbol || site.enclosingSymbol === target.symbol) {
|
|
4740
|
+
const key2 = `${site.file}|(top-level)`;
|
|
4741
|
+
if (seen.has(key2)) continue;
|
|
4742
|
+
seen.add(key2);
|
|
4743
|
+
rows2.push({
|
|
4744
|
+
symbolId: null,
|
|
4745
|
+
symbol: site.file,
|
|
4746
|
+
shortName: "(top-level)",
|
|
4747
|
+
file: site.file,
|
|
4748
|
+
symbolMatch: null
|
|
4749
|
+
});
|
|
4750
|
+
continue;
|
|
4751
|
+
}
|
|
4752
|
+
const enclosing = findExactSymbolMatch(db, site.enclosingSymbol);
|
|
4753
|
+
if (!enclosing || enclosing.symbolId === target.symbolId || db.isIgnored(enclosing.relativePath)) {
|
|
4754
|
+
continue;
|
|
4755
|
+
}
|
|
4756
|
+
const key = `${enclosing.symbolId}|${enclosing.relativePath}`;
|
|
4757
|
+
if (seen.has(key)) continue;
|
|
4758
|
+
seen.add(key);
|
|
4759
|
+
rows2.push({
|
|
4760
|
+
symbolId: enclosing.symbolId,
|
|
4761
|
+
symbol: enclosing.symbol,
|
|
4762
|
+
shortName: shortenSymbol(enclosing.symbol),
|
|
4763
|
+
file: enclosing.relativePath,
|
|
4764
|
+
symbolMatch: enclosing
|
|
4765
|
+
});
|
|
4766
|
+
}
|
|
4767
|
+
return rows2;
|
|
4768
|
+
}
|
|
4769
|
+
const rows = db.all(
|
|
4770
|
+
`SELECT DISTINCT
|
|
4771
|
+
enc_gs.id AS symbol_id,
|
|
4772
|
+
enc_gs.symbol AS symbol,
|
|
4773
|
+
enc_d.relative_path AS relative_path
|
|
4774
|
+
FROM mentions m
|
|
4775
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
4776
|
+
JOIN documents ref_d ON c.document_id = ref_d.id
|
|
4777
|
+
JOIN defn_enclosing_ranges enc_der
|
|
4778
|
+
ON enc_der.document_id = ref_d.id
|
|
4779
|
+
AND c.start_line >= enc_der.start_line
|
|
4780
|
+
AND c.end_line <= enc_der.end_line
|
|
4781
|
+
JOIN global_symbols enc_gs ON enc_der.symbol_id = enc_gs.id
|
|
4782
|
+
JOIN documents enc_d ON enc_der.document_id = enc_d.id
|
|
4783
|
+
WHERE m.symbol_id = ?
|
|
4784
|
+
AND m.role != 1
|
|
4785
|
+
AND enc_gs.id != ?
|
|
4786
|
+
${db.symbolNoiseFor("enc_gs")}
|
|
4787
|
+
${db.pathExclusionsFor("enc_d")}
|
|
4788
|
+
${scope ? `AND enc_d.relative_path LIKE '%${scope}%'` : ""}
|
|
4789
|
+
ORDER BY enc_d.relative_path
|
|
4790
|
+
LIMIT 1`,
|
|
4791
|
+
target.symbolId,
|
|
4792
|
+
target.symbolId
|
|
4793
|
+
);
|
|
4794
|
+
return rows.filter((row) => !db.isIgnored(row.relative_path)).map((row) => ({
|
|
4795
|
+
symbolId: row.symbol_id,
|
|
4796
|
+
symbol: row.symbol,
|
|
4797
|
+
shortName: shortenSymbol(row.symbol),
|
|
4798
|
+
file: row.relative_path,
|
|
4799
|
+
symbolMatch: findExactSymbolMatch(db, row.symbol)
|
|
4800
|
+
}));
|
|
4801
|
+
}
|
|
3829
4802
|
|
|
3830
4803
|
// src/queries/change-surface.ts
|
|
3831
4804
|
function changeSurface(db, filePattern) {
|
|
4805
|
+
const resolvedFile = resolveIndexedFile(db, filePattern);
|
|
4806
|
+
if (!resolvedFile) return null;
|
|
3832
4807
|
const doc = db.get(
|
|
3833
4808
|
`SELECT id, relative_path FROM documents
|
|
3834
|
-
WHERE relative_path
|
|
4809
|
+
WHERE relative_path = ?
|
|
3835
4810
|
${db.pathExclusionsFor("documents")}
|
|
3836
4811
|
LIMIT 1`,
|
|
3837
|
-
|
|
4812
|
+
resolvedFile
|
|
3838
4813
|
);
|
|
3839
4814
|
if (!doc || db.isIgnored(doc.relative_path)) return null;
|
|
3840
4815
|
const syms = db.all(
|
|
@@ -4172,17 +5147,17 @@ function isLikelyTypeOnlyDep(dep) {
|
|
|
4172
5147
|
function shouldSkipDriftFile(filePath) {
|
|
4173
5148
|
return isStructuralRole(path.basename(filePath)) || isTestLikePath(filePath);
|
|
4174
5149
|
}
|
|
4175
|
-
function isStructuralRole(
|
|
4176
|
-
if (
|
|
4177
|
-
if (
|
|
4178
|
-
if (
|
|
4179
|
-
if (
|
|
5150
|
+
function isStructuralRole(basename2) {
|
|
5151
|
+
if (basename2 === "index.ts" || basename2 === "index.js") return true;
|
|
5152
|
+
if (basename2 === "cli.ts" || basename2 === "main.ts" || basename2 === "main.rs") return true;
|
|
5153
|
+
if (basename2.includes("worker.") || basename2.includes("postinstall.")) return true;
|
|
5154
|
+
if (basename2 === "health.ts" || basename2 === "health.js") return true;
|
|
4180
5155
|
return false;
|
|
4181
5156
|
}
|
|
4182
5157
|
function isTestLikePath(filePath) {
|
|
4183
5158
|
const normalized = filePath.replace(/\\/g, "/");
|
|
4184
|
-
const
|
|
4185
|
-
return normalized.includes("/__tests__/") || normalized.includes("/tests/") || normalized.includes("/test/") || /\.(test|spec)\.[A-Za-z0-9]+$/.test(
|
|
5159
|
+
const basename2 = path.basename(normalized);
|
|
5160
|
+
return normalized.includes("/__tests__/") || normalized.includes("/tests/") || normalized.includes("/test/") || /\.(test|spec)\.[A-Za-z0-9]+$/.test(basename2) || /_(test|spec)\.[A-Za-z0-9]+$/.test(basename2) || /^test[_-]/.test(basename2) || /^test\./.test(basename2);
|
|
4186
5161
|
}
|
|
4187
5162
|
|
|
4188
5163
|
// src/queries/wrapper-candidates.ts
|
|
@@ -4368,8 +5343,8 @@ function staleAbstractions(db, opts) {
|
|
|
4368
5343
|
limit
|
|
4369
5344
|
);
|
|
4370
5345
|
return rows.filter((r) => !db.isIgnored(r.file)).filter((r) => {
|
|
4371
|
-
const
|
|
4372
|
-
const isTypeFile =
|
|
5346
|
+
const basename2 = r.file.split("/").pop() ?? "";
|
|
5347
|
+
const isTypeFile = basename2.includes("types") || r.file.includes("/types/");
|
|
4373
5348
|
if (isTypeFile && r.consumers > 0) return false;
|
|
4374
5349
|
return true;
|
|
4375
5350
|
}).map((r) => ({
|
|
@@ -4773,9 +5748,11 @@ function code(db, symbolPattern, opts = {}) {
|
|
|
4773
5748
|
};
|
|
4774
5749
|
}
|
|
4775
5750
|
function readFileRange(db, filePath, startLine, endLine, context) {
|
|
5751
|
+
const resolvedPath = resolveIndexedFile(db, filePath);
|
|
5752
|
+
if (!resolvedPath) return null;
|
|
4776
5753
|
const doc = db.get(
|
|
4777
|
-
`SELECT relative_path, language FROM documents WHERE relative_path
|
|
4778
|
-
|
|
5754
|
+
`SELECT relative_path, language FROM documents WHERE relative_path = ?`,
|
|
5755
|
+
resolvedPath
|
|
4779
5756
|
);
|
|
4780
5757
|
if (!doc) return null;
|
|
4781
5758
|
const fullPath = join7(db.config.projectRoot, doc.relative_path);
|
|
@@ -4902,93 +5879,75 @@ function stripCommentsAndStrings2(source) {
|
|
|
4902
5879
|
function dataflow(db, symbolPattern) {
|
|
4903
5880
|
const match = findFirstSymbolMatch(db, symbolPattern);
|
|
4904
5881
|
if (!match) return null;
|
|
4905
|
-
const defSites =
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
5882
|
+
const defSites = [{
|
|
5883
|
+
file: match.relativePath,
|
|
5884
|
+
line: match.startLine
|
|
5885
|
+
}];
|
|
5886
|
+
const sourceUsageSites = getSourceReferenceSites(db, match);
|
|
5887
|
+
const usageSites = sourceUsageSites.length > 0 ? sourceUsageSites.map((site) => ({
|
|
5888
|
+
file: site.file,
|
|
5889
|
+
line: site.line,
|
|
5890
|
+
enclosing_symbol: site.enclosingSymbol
|
|
5891
|
+
})) : db.all(
|
|
4915
5892
|
`SELECT d.relative_path AS file, c.start_line AS line,
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
match.symbolId
|
|
4932
|
-
);
|
|
4933
|
-
const producers = db.all(
|
|
4934
|
-
`SELECT DISTINCT other_gs.symbol, other_d.relative_path AS file
|
|
4935
|
-
FROM mentions other_m
|
|
4936
|
-
JOIN chunks other_c ON other_m.chunk_id = other_c.id
|
|
4937
|
-
JOIN global_symbols other_gs ON other_m.symbol_id = other_gs.id
|
|
4938
|
-
JOIN defn_enclosing_ranges other_der ON other_gs.id = other_der.symbol_id
|
|
4939
|
-
JOIN documents other_d ON other_der.document_id = other_d.id
|
|
4940
|
-
WHERE other_c.document_id = ?
|
|
4941
|
-
AND other_c.start_line >= ? AND other_c.end_line <= ?
|
|
4942
|
-
AND other_m.role != 1
|
|
4943
|
-
AND other_gs.id != ?
|
|
4944
|
-
${db.symbolNoiseFor("other_gs")}
|
|
4945
|
-
${db.pathExclusionsFor("other_d")}
|
|
4946
|
-
ORDER BY other_d.relative_path
|
|
4947
|
-
LIMIT 30`,
|
|
4948
|
-
match.documentId,
|
|
4949
|
-
match.startLine,
|
|
4950
|
-
match.endLine,
|
|
5893
|
+
(SELECT enc_gs.symbol
|
|
5894
|
+
FROM defn_enclosing_ranges enc_der
|
|
5895
|
+
JOIN global_symbols enc_gs ON enc_der.symbol_id = enc_gs.id
|
|
5896
|
+
WHERE enc_der.document_id = d.id
|
|
5897
|
+
AND enc_der.start_line <= c.start_line
|
|
5898
|
+
AND enc_der.end_line >= c.end_line
|
|
5899
|
+
ORDER BY (enc_der.end_line - enc_der.start_line) ASC
|
|
5900
|
+
LIMIT 1
|
|
5901
|
+
) AS enclosing_symbol
|
|
5902
|
+
FROM mentions m
|
|
5903
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
5904
|
+
JOIN documents d ON c.document_id = d.id
|
|
5905
|
+
WHERE m.symbol_id = ? AND m.role != 1
|
|
5906
|
+
${db.pathExclusionsFor("d")}
|
|
5907
|
+
ORDER BY d.relative_path, c.start_line`,
|
|
4951
5908
|
match.symbolId
|
|
4952
5909
|
);
|
|
4953
|
-
const
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
JOIN global_symbols consumer_gs ON consumer_m.symbol_id = consumer_gs.id
|
|
4969
|
-
WHERE ref_m.symbol_id = ? AND ref_m.role != 1
|
|
4970
|
-
AND consumer_d.id != ref_d.id
|
|
4971
|
-
${db.symbolNoiseFor("consumer_gs")}
|
|
4972
|
-
${db.pathExclusionsFor("consumer_d")}
|
|
4973
|
-
ORDER BY consumer_d.relative_path
|
|
4974
|
-
LIMIT 30`,
|
|
4975
|
-
match.symbolId
|
|
5910
|
+
const normalizedUsageSites = usageSites.filter((site) => !db.isIgnored(site.file)).map((site) => ({
|
|
5911
|
+
file: site.file,
|
|
5912
|
+
line: site.line,
|
|
5913
|
+
enclosingSymbol: site.enclosing_symbol ?? "(top-level)",
|
|
5914
|
+
enclosingShort: site.enclosing_symbol ? shortenSymbol(site.enclosing_symbol) : "(top-level)"
|
|
5915
|
+
}));
|
|
5916
|
+
const producers = uniqueSymbolRows(getCalleeRowsForSymbol(db, match, { limit: 30 }).map((row) => ({
|
|
5917
|
+
symbol: row.symbol,
|
|
5918
|
+
file: row.file
|
|
5919
|
+
})));
|
|
5920
|
+
const consumers = uniqueSymbolRows(
|
|
5921
|
+
normalizedUsageSites.map((site) => ({
|
|
5922
|
+
symbol: site.enclosingSymbol === "(top-level)" ? site.file : site.enclosingSymbol,
|
|
5923
|
+
file: site.file
|
|
5924
|
+
}))
|
|
4976
5925
|
);
|
|
4977
5926
|
return {
|
|
4978
5927
|
symbol: match.symbol,
|
|
4979
5928
|
shortName: shortenSymbol(match.symbol),
|
|
4980
5929
|
relativePath: match.relativePath,
|
|
4981
5930
|
definitionSites: defSites.filter((s) => !db.isIgnored(s.file)),
|
|
4982
|
-
usageSites:
|
|
4983
|
-
file: s.file,
|
|
4984
|
-
line: s.line,
|
|
4985
|
-
enclosingSymbol: s.enclosing_symbol ?? "(top-level)",
|
|
4986
|
-
enclosingShort: s.enclosing_symbol ? shortenSymbol(s.enclosing_symbol) : "(top-level)"
|
|
4987
|
-
})),
|
|
5931
|
+
usageSites: normalizedUsageSites,
|
|
4988
5932
|
producers: producers.filter((p) => !db.isIgnored(p.file)).map((p) => ({ symbol: p.symbol, shortName: shortenSymbol(p.symbol), file: p.file })),
|
|
4989
|
-
consumers: consumers.filter((c) => !db.isIgnored(c.file)).map((c) => ({
|
|
5933
|
+
consumers: consumers.filter((c) => !db.isIgnored(c.file)).map((c) => ({
|
|
5934
|
+
symbol: c.symbol,
|
|
5935
|
+
shortName: c.symbol === c.file ? "(top-level)" : shortenSymbol(c.symbol),
|
|
5936
|
+
file: c.file
|
|
5937
|
+
}))
|
|
4990
5938
|
};
|
|
4991
5939
|
}
|
|
5940
|
+
function uniqueSymbolRows(rows) {
|
|
5941
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5942
|
+
const unique = [];
|
|
5943
|
+
for (const row of rows) {
|
|
5944
|
+
const key = `${row.symbol}|${row.file}`;
|
|
5945
|
+
if (seen.has(key)) continue;
|
|
5946
|
+
seen.add(key);
|
|
5947
|
+
unique.push(row);
|
|
5948
|
+
}
|
|
5949
|
+
return unique;
|
|
5950
|
+
}
|
|
4992
5951
|
|
|
4993
5952
|
// src/queries/slice.ts
|
|
4994
5953
|
function slice(db, symbolPattern, opts = {}) {
|
|
@@ -5003,21 +5962,6 @@ function slice(db, symbolPattern, opts = {}) {
|
|
|
5003
5962
|
}
|
|
5004
5963
|
function backwardSlice(db, match) {
|
|
5005
5964
|
const callees = getCalleeRowsForSymbol(db, match);
|
|
5006
|
-
const localPredecessors = db.all(
|
|
5007
|
-
`SELECT DISTINCT gs.symbol, d.relative_path AS file
|
|
5008
|
-
FROM defn_enclosing_ranges der
|
|
5009
|
-
JOIN global_symbols gs ON der.symbol_id = gs.id
|
|
5010
|
-
JOIN documents d ON der.document_id = d.id
|
|
5011
|
-
WHERE der.document_id = ?
|
|
5012
|
-
AND der.end_line < ?
|
|
5013
|
-
AND gs.id != ?
|
|
5014
|
-
${db.symbolNoiseFor("gs")}
|
|
5015
|
-
ORDER BY der.start_line DESC
|
|
5016
|
-
LIMIT 15`,
|
|
5017
|
-
match.documentId,
|
|
5018
|
-
match.startLine,
|
|
5019
|
-
match.symbolId
|
|
5020
|
-
);
|
|
5021
5965
|
const seen = /* @__PURE__ */ new Set();
|
|
5022
5966
|
const connected = [];
|
|
5023
5967
|
for (const c of callees) {
|
|
@@ -5030,16 +5974,6 @@ function backwardSlice(db, match) {
|
|
|
5030
5974
|
relationship: "referenced within definition (callee)"
|
|
5031
5975
|
});
|
|
5032
5976
|
}
|
|
5033
|
-
for (const p of localPredecessors) {
|
|
5034
|
-
if (seen.has(p.symbol) || db.isIgnored(p.file)) continue;
|
|
5035
|
-
seen.add(p.symbol);
|
|
5036
|
-
connected.push({
|
|
5037
|
-
symbol: p.symbol,
|
|
5038
|
-
shortName: shortenSymbol(p.symbol),
|
|
5039
|
-
file: p.file,
|
|
5040
|
-
relationship: "defined before target in same file (local predecessor)"
|
|
5041
|
-
});
|
|
5042
|
-
}
|
|
5043
5977
|
return {
|
|
5044
5978
|
symbol: match.symbol,
|
|
5045
5979
|
shortName: shortenSymbol(match.symbol),
|
|
@@ -5332,6 +6266,8 @@ function installSkills(opts = {}) {
|
|
|
5332
6266
|
}
|
|
5333
6267
|
|
|
5334
6268
|
// src/cli.ts
|
|
6269
|
+
var require2 = createRequire(import.meta.url);
|
|
6270
|
+
var { version: cliVersion } = require2("../package.json");
|
|
5335
6271
|
function resolveProjectRoot() {
|
|
5336
6272
|
return process.env["SCIP_QUERY_PROJECT_ROOT"] ?? process.cwd();
|
|
5337
6273
|
}
|
|
@@ -5370,6 +6306,15 @@ function runQuery(query, render) {
|
|
|
5370
6306
|
render(query(db));
|
|
5371
6307
|
});
|
|
5372
6308
|
}
|
|
6309
|
+
function displayLine(line) {
|
|
6310
|
+
return line + 1;
|
|
6311
|
+
}
|
|
6312
|
+
function displayRange(startLine, endLine) {
|
|
6313
|
+
return `${displayLine(startLine)}-${displayLine(endLine)}`;
|
|
6314
|
+
}
|
|
6315
|
+
function displayPathRange(relativePath, startLine, endLine) {
|
|
6316
|
+
return `${relativePath}:${displayRange(startLine, endLine)}`;
|
|
6317
|
+
}
|
|
5373
6318
|
var queries = {
|
|
5374
6319
|
stats,
|
|
5375
6320
|
files,
|
|
@@ -5425,7 +6370,7 @@ var queries = {
|
|
|
5425
6370
|
redundantReexports,
|
|
5426
6371
|
similarSignatures
|
|
5427
6372
|
};
|
|
5428
|
-
program.name("scip-query").description("Language-agnostic code intelligence CLI powered by SCIP indexes").version(
|
|
6373
|
+
program.name("scip-query").description("Language-agnostic code intelligence CLI powered by SCIP indexes").version(cliVersion);
|
|
5429
6374
|
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) => {
|
|
5430
6375
|
const projectRoot = resolveProjectRoot();
|
|
5431
6376
|
try {
|
|
@@ -5469,7 +6414,7 @@ program.command("symbols <file>").description("List symbols defined in a file (w
|
|
|
5469
6414
|
(results) => {
|
|
5470
6415
|
for (const r of results) {
|
|
5471
6416
|
const sig = r.signature ? ` \u2014 ${r.signature}` : "";
|
|
5472
|
-
console.log(` ${r.startLine
|
|
6417
|
+
console.log(` ${displayRange(r.startLine, r.endLine)} ${r.shortName}${sig}`);
|
|
5473
6418
|
}
|
|
5474
6419
|
}
|
|
5475
6420
|
);
|
|
@@ -5479,7 +6424,7 @@ program.command("methods <className>").description("List methods of a class (wit
|
|
|
5479
6424
|
(db) => queries.methods(db, className),
|
|
5480
6425
|
(results) => {
|
|
5481
6426
|
for (const r of results) {
|
|
5482
|
-
console.log(` ${r.startLine
|
|
6427
|
+
console.log(` ${displayRange(r.startLine, r.endLine)} ${r.name}`);
|
|
5483
6428
|
}
|
|
5484
6429
|
}
|
|
5485
6430
|
);
|
|
@@ -5495,7 +6440,7 @@ program.command("refs <symbol>").description("Find all files referencing a symbo
|
|
|
5495
6440
|
console.log(r.relativePath);
|
|
5496
6441
|
prevFile = r.relativePath;
|
|
5497
6442
|
}
|
|
5498
|
-
console.log(` line ${r.line}`);
|
|
6443
|
+
console.log(` line ${displayLine(r.line)}`);
|
|
5499
6444
|
}
|
|
5500
6445
|
}
|
|
5501
6446
|
);
|
|
@@ -5507,11 +6452,17 @@ program.command("trace <symbol>").description("Trace a symbol: definition + all
|
|
|
5507
6452
|
console.log("\u2550\u2550\u2550 DEFINITION \u2550\u2550\u2550");
|
|
5508
6453
|
for (const d of result.definitions) {
|
|
5509
6454
|
const sig = d.signature ? ` \u2014 ${d.signature}` : "";
|
|
5510
|
-
console.log(` ${d.relativePath
|
|
6455
|
+
console.log(` ${displayPathRange(d.relativePath, d.startLine, d.endLine)}${sig}`);
|
|
5511
6456
|
}
|
|
5512
6457
|
console.log("\n\u2550\u2550\u2550 REFERENCED BY \u2550\u2550\u2550");
|
|
6458
|
+
let prevFile = "";
|
|
5513
6459
|
for (const ref of result.referencedBy) {
|
|
5514
|
-
|
|
6460
|
+
if (ref.relativePath !== prevFile) {
|
|
6461
|
+
if (prevFile) console.log("");
|
|
6462
|
+
console.log(` ${ref.relativePath}`);
|
|
6463
|
+
prevFile = ref.relativePath;
|
|
6464
|
+
}
|
|
6465
|
+
console.log(` line ${displayLine(ref.line)} in ${ref.enclosingShort}`);
|
|
5515
6466
|
}
|
|
5516
6467
|
}
|
|
5517
6468
|
);
|
|
@@ -5540,7 +6491,7 @@ program.command("system <module>").description("Full module map: files, symbols,
|
|
|
5540
6491
|
for (const f of result.files) console.log(f);
|
|
5541
6492
|
console.log("\n\u2550\u2550\u2550 EXPORTED SYMBOLS \u2550\u2550\u2550");
|
|
5542
6493
|
for (const s of result.symbols) {
|
|
5543
|
-
console.log(` ${s.startLine
|
|
6494
|
+
console.log(` ${displayRange(s.startLine, s.endLine)} ${s.shortName}`);
|
|
5544
6495
|
}
|
|
5545
6496
|
console.log("\n\u2550\u2550\u2550 DEPENDS ON (internal) \u2550\u2550\u2550");
|
|
5546
6497
|
for (const d of result.dependsOn) console.log(` ${d}`);
|
|
@@ -5581,7 +6532,7 @@ program.command("dead [scope]").description("Find dead code and file-internal sy
|
|
|
5581
6532
|
prevFile = s.relativePath;
|
|
5582
6533
|
}
|
|
5583
6534
|
const tag = s.kind === "dead-code" ? "[dead code]" : "[file-internal only]";
|
|
5584
|
-
console.log(` ${s.startLine
|
|
6535
|
+
console.log(` ${displayRange(s.startLine, s.endLine)} (${s.loc} LOC) ${s.shortName} ${tag}`);
|
|
5585
6536
|
}
|
|
5586
6537
|
console.log("\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
5587
6538
|
console.log(
|
|
@@ -5647,7 +6598,7 @@ program.command("outline <file>").description("Tree view of symbols in a file (u
|
|
|
5647
6598
|
function printTree(nodes, indent) {
|
|
5648
6599
|
for (const n of nodes) {
|
|
5649
6600
|
const prefix = " ".repeat(indent);
|
|
5650
|
-
console.log(`${prefix}${n.startLine
|
|
6601
|
+
console.log(`${prefix}${displayRange(n.startLine, n.endLine)} ${n.shortName}`);
|
|
5651
6602
|
printTree(n.children, indent + 1);
|
|
5652
6603
|
}
|
|
5653
6604
|
}
|
|
@@ -5660,7 +6611,7 @@ program.command("members <symbol>").description("All children of a symbol (metho
|
|
|
5660
6611
|
(db) => queries.members(db, symbol),
|
|
5661
6612
|
(results) => {
|
|
5662
6613
|
for (const r of results) {
|
|
5663
|
-
console.log(` ${r.startLine
|
|
6614
|
+
console.log(` ${displayRange(r.startLine, r.endLine)} [${r.kind}] ${r.shortName}`);
|
|
5664
6615
|
}
|
|
5665
6616
|
}
|
|
5666
6617
|
);
|
|
@@ -5770,7 +6721,7 @@ program.command("isolated").description("Find completely orphaned symbols (no re
|
|
|
5770
6721
|
console.log(r.relativePath);
|
|
5771
6722
|
prevFile = r.relativePath;
|
|
5772
6723
|
}
|
|
5773
|
-
console.log(` ${r.startLine
|
|
6724
|
+
console.log(` ${displayRange(r.startLine, r.endLine)} (${r.loc} LOC) ${r.shortName}`);
|
|
5774
6725
|
}
|
|
5775
6726
|
console.log(`
|
|
5776
6727
|
${results.length} isolated symbol(s)`);
|
|
@@ -5786,7 +6737,7 @@ program.command("by-kind <kind>").description("Find symbols by SCIP kind (class,
|
|
|
5786
6737
|
console.log(`No symbols found for kind "${kind}". Use "kind-counts" to see available kinds.`);
|
|
5787
6738
|
} else {
|
|
5788
6739
|
for (const r of results) {
|
|
5789
|
-
console.log(` ${r.relativePath
|
|
6740
|
+
console.log(` ${displayPathRange(r.relativePath, r.startLine, r.endLine)} [${r.kindName}] ${r.shortName}`);
|
|
5790
6741
|
}
|
|
5791
6742
|
console.log(`
|
|
5792
6743
|
${results.length} symbol(s)`);
|
|
@@ -5821,7 +6772,7 @@ program.command("doc-coverage").description("Check documentation coverage across
|
|
|
5821
6772
|
if (result.undocumentedSymbols.length > 0) {
|
|
5822
6773
|
console.log("\nUndocumented:");
|
|
5823
6774
|
for (const s of result.undocumentedSymbols) {
|
|
5824
|
-
console.log(` ${s.relativePath}:${s.startLine} ${s.shortName}`);
|
|
6775
|
+
console.log(` ${s.relativePath}:${displayLine(s.startLine)} ${s.shortName}`);
|
|
5825
6776
|
}
|
|
5826
6777
|
}
|
|
5827
6778
|
}
|
|
@@ -6003,7 +6954,7 @@ program.command("extract-candidates").description("Find functions with natural e
|
|
|
6003
6954
|
} else {
|
|
6004
6955
|
for (const r of results) {
|
|
6005
6956
|
console.log(`
|
|
6006
|
-
${r.relativePath
|
|
6957
|
+
${displayPathRange(r.relativePath, r.startLine, r.endLine)} ${r.shortName} (${r.loc} LOC, ${r.totalCallees} callees)`);
|
|
6007
6958
|
for (let i = 0; i < r.clusters.length; i++) {
|
|
6008
6959
|
const c = r.clusters[i];
|
|
6009
6960
|
console.log(` Cluster ${i + 1} (${Math.round(c.isolation * 100)}% isolated, ${c.callees.length} callees):`);
|
|
@@ -6051,7 +7002,7 @@ program.command("change-surface <file>").description("Pre-change briefing: expor
|
|
|
6051
7002
|
`);
|
|
6052
7003
|
for (const s of result.symbols) {
|
|
6053
7004
|
const risk = s.riskLevel === "high" ? " *** HIGH RISK ***" : s.riskLevel === "medium" ? " * medium risk *" : "";
|
|
6054
|
-
console.log(` ${s.startLine
|
|
7005
|
+
console.log(` ${displayRange(s.startLine, s.endLine)} ${s.shortName} [${s.externalConsumers} consumers]${risk}`);
|
|
6055
7006
|
}
|
|
6056
7007
|
db.close();
|
|
6057
7008
|
});
|
|
@@ -6111,7 +7062,7 @@ program.command("wrapper-candidates").description("Find symbols only called by o
|
|
|
6111
7062
|
console.log("No wrapper candidates found.");
|
|
6112
7063
|
} else {
|
|
6113
7064
|
for (const r of results) {
|
|
6114
|
-
console.log(` ${r.file
|
|
7065
|
+
console.log(` ${displayPathRange(r.file, r.startLine, r.endLine)} ${r.shortName} (${r.loc} LOC)`);
|
|
6115
7066
|
console.log(` Only called by: ${r.singleCallerShort} (fan-in: ${r.callerFanIn})`);
|
|
6116
7067
|
}
|
|
6117
7068
|
console.log(`
|
|
@@ -6126,7 +7077,7 @@ program.command("passthrough-candidates").description("Find functions that just
|
|
|
6126
7077
|
console.log("No passthrough candidates found.");
|
|
6127
7078
|
} else {
|
|
6128
7079
|
for (const r of results) {
|
|
6129
|
-
console.log(` ${r.file
|
|
7080
|
+
console.log(` ${displayPathRange(r.file, r.startLine, r.endLine)} ${r.shortName} (${r.loc} LOC)`);
|
|
6130
7081
|
console.log(` Forwards to: ${r.forwardsToShort} (${r.forwardsToFile})`);
|
|
6131
7082
|
}
|
|
6132
7083
|
console.log(`
|
|
@@ -6142,7 +7093,7 @@ program.command("stale-abstractions").description("Find types/interfaces with 0-
|
|
|
6142
7093
|
} else {
|
|
6143
7094
|
for (const r of results) {
|
|
6144
7095
|
const label = r.consumers === 0 ? "unused" : "1 consumer";
|
|
6145
|
-
console.log(` ${r.file
|
|
7096
|
+
console.log(` ${displayPathRange(r.file, r.startLine, r.endLine)} ${r.shortName} (${r.loc} LOC, ${label})`);
|
|
6146
7097
|
}
|
|
6147
7098
|
console.log(`
|
|
6148
7099
|
${results.length} stale abstraction(s).`);
|
|
@@ -6244,11 +7195,11 @@ program.command("code <symbol>").description("Read the source code for a symbol
|
|
|
6244
7195
|
db.close();
|
|
6245
7196
|
return;
|
|
6246
7197
|
}
|
|
6247
|
-
console.log(`${result.relativePath
|
|
7198
|
+
console.log(`${displayPathRange(result.relativePath, result.startLine, result.endLine)} ${result.shortName} [${result.language ?? "unknown"}]
|
|
6248
7199
|
`);
|
|
6249
7200
|
const lines = result.source.split("\n");
|
|
6250
7201
|
for (let i = 0; i < lines.length; i++) {
|
|
6251
|
-
console.log(` ${String(result.startLine + i).padStart(4)} ${lines[i]}`);
|
|
7202
|
+
console.log(` ${String(displayLine(result.startLine + i)).padStart(4)} ${lines[i]}`);
|
|
6252
7203
|
}
|
|
6253
7204
|
db.close();
|
|
6254
7205
|
});
|
|
@@ -6260,7 +7211,7 @@ program.command("complexity <symbol>").description("Per-symbol complexity: branc
|
|
|
6260
7211
|
db.close();
|
|
6261
7212
|
return;
|
|
6262
7213
|
}
|
|
6263
|
-
console.log(`${result.relativePath
|
|
7214
|
+
console.log(`${displayPathRange(result.relativePath, result.startLine, result.endLine)} ${result.shortName}
|
|
6264
7215
|
`);
|
|
6265
7216
|
console.log(` LOC: ${result.loc}`);
|
|
6266
7217
|
console.log(` Branches: ${result.branches}`);
|
|
@@ -6283,13 +7234,13 @@ program.command("dataflow <symbol>").description("Reference-level dataflow: defi
|
|
|
6283
7234
|
if (result.definitionSites.length > 0) {
|
|
6284
7235
|
console.log(" \u2550\u2550\u2550 DEFINED AT \u2550\u2550\u2550");
|
|
6285
7236
|
for (const s of result.definitionSites) {
|
|
6286
|
-
console.log(` ${s.file}:${s.line}`);
|
|
7237
|
+
console.log(` ${s.file}:${displayLine(s.line)}`);
|
|
6287
7238
|
}
|
|
6288
7239
|
}
|
|
6289
7240
|
if (result.usageSites.length > 0) {
|
|
6290
7241
|
console.log("\n \u2550\u2550\u2550 USED AT \u2550\u2550\u2550");
|
|
6291
7242
|
for (const s of result.usageSites) {
|
|
6292
|
-
console.log(` ${s.file}:${s.line} in ${s.enclosingShort}`);
|
|
7243
|
+
console.log(` ${s.file}:${displayLine(s.line)} in ${s.enclosingShort}`);
|
|
6293
7244
|
}
|
|
6294
7245
|
}
|
|
6295
7246
|
if (result.producers.length > 0) {
|
|
@@ -6376,7 +7327,7 @@ program.command("similar-signatures").description("Find functions with near-iden
|
|
|
6376
7327
|
console.log(`
|
|
6377
7328
|
Signature: ${g.signature} (${g.functions.length} functions)`);
|
|
6378
7329
|
for (const f of g.functions) {
|
|
6379
|
-
console.log(` ${f.file
|
|
7330
|
+
console.log(` ${displayPathRange(f.file, f.startLine, f.endLine)} ${f.shortName} (${f.loc} LOC)`);
|
|
6380
7331
|
}
|
|
6381
7332
|
}
|
|
6382
7333
|
console.log(`
|
|
@@ -6445,9 +7396,20 @@ program.command("status").description("Show index status for this project").acti
|
|
|
6445
7396
|
});
|
|
6446
7397
|
}
|
|
6447
7398
|
});
|
|
6448
|
-
if (
|
|
7399
|
+
if (isCliEntrypoint()) {
|
|
6449
7400
|
program.parse();
|
|
6450
7401
|
}
|
|
7402
|
+
function isCliEntrypoint() {
|
|
7403
|
+
if (!process.argv[1]) {
|
|
7404
|
+
return false;
|
|
7405
|
+
}
|
|
7406
|
+
const thisFile = fileURLToPath2(import.meta.url);
|
|
7407
|
+
try {
|
|
7408
|
+
return realpathSync(thisFile) === realpathSync(process.argv[1]);
|
|
7409
|
+
} catch {
|
|
7410
|
+
return thisFile === process.argv[1];
|
|
7411
|
+
}
|
|
7412
|
+
}
|
|
6451
7413
|
function collect(value, prev) {
|
|
6452
7414
|
return prev.concat([value]);
|
|
6453
7415
|
}
|