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