scip-query 0.5.0 → 0.6.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/README.md +14 -7
- package/dist/chunk-2DSS2NGF.js +10 -0
- package/dist/chunk-2RLP74AO.js +2 -0
- package/dist/chunk-4QJ7LVW5.js +2 -0
- package/dist/chunk-4TYGGOLO.js +5 -0
- package/dist/chunk-5IADAU5B.js +7 -0
- package/dist/chunk-7754WFFV.js +18 -0
- package/dist/chunk-7VOF4ZG6.js +2 -0
- package/dist/chunk-7Z4COVMC.js +2 -0
- package/dist/chunk-AJ5PWKD4.js +2 -0
- package/dist/chunk-BDBRZPX3.js +7 -0
- package/dist/chunk-BE6EQIWY.js +2 -0
- package/dist/chunk-BQ3INTYT.js +8 -0
- package/dist/chunk-D7KLLMPB.js +2 -0
- package/dist/chunk-D7YBWSON.js +29 -0
- package/dist/chunk-DE5ZBHMK.js +39 -0
- package/dist/chunk-DHYIJHXZ.js +33 -0
- package/dist/chunk-EEF3YEHW.js +2 -0
- package/dist/chunk-F2LLHRRZ.js +2 -0
- package/dist/chunk-FCC3XJTI.js +2 -0
- package/dist/chunk-GNXRLK5G.js +2 -0
- package/dist/chunk-GXVB36TG.js +62 -0
- package/dist/chunk-HMKJTAZD.js +2 -0
- package/dist/chunk-IBGBI3VU.js +2 -0
- package/dist/chunk-IYFZS4PV.js +84 -0
- package/dist/chunk-JH3A7HTU.js +2 -0
- package/dist/chunk-JS2RNIC7.js +2 -0
- package/dist/chunk-K5FQFCSN.js +41 -0
- package/dist/chunk-K6GBKEQE.js +6 -0
- package/dist/chunk-KO7YJRWP.js +12 -0
- package/dist/chunk-KYT47WU2.js +4 -0
- package/dist/chunk-LORWXBOO.js +2 -0
- package/dist/chunk-LX4H4LLG.js +89 -0
- package/dist/chunk-N3Z2SJCR.js +2 -0
- package/dist/chunk-NTDA4A2D.js +25 -0
- package/dist/chunk-NXMYYHDO.js +24 -0
- package/dist/chunk-PZ6ESKRH.js +7 -0
- package/dist/chunk-QXE6EDY2.js +6 -0
- package/dist/chunk-RJ7SPBJ5.js +5 -0
- package/dist/chunk-RWE6FHG3.js +3 -0
- package/dist/chunk-SDX6MDBL.js +2 -0
- package/dist/chunk-SG35Y7J2.js +2 -0
- package/dist/chunk-STOGKRJH.js +4 -0
- package/dist/chunk-TINPMWJK.js +2 -0
- package/dist/chunk-UJB62HV3.js +2 -0
- package/dist/chunk-VEUMRDHW.js +2 -0
- package/dist/chunk-WCDXJGYT.js +65 -0
- package/dist/chunk-WTSTDJZ7.js +6 -0
- package/dist/chunk-XAZTIDST.js +2 -0
- package/dist/chunk-XVDASCN7.js +35 -0
- package/dist/chunk-Y7H6D2EV.js +2 -0
- package/dist/chunk-Y7LOQSWY.js +2 -0
- package/dist/chunk-YIPCV7M7.js +70 -0
- package/dist/chunk-ZSRXMNMK.js +5 -0
- package/dist/chunk-ZXKURFVB.js +56 -0
- package/dist/cli.js +509 -8758
- package/dist/{db-6F9R9e_t.d.ts → db-BSTtBG_H.d.ts} +146 -1
- package/dist/index.d.ts +11 -2
- package/dist/index.js +13 -1616
- package/dist/postinstall.js +4 -100
- package/dist/queries/affected.d.ts +1 -1
- package/dist/queries/affected.js +1 -8
- package/dist/queries/bottlenecks.d.ts +1 -1
- package/dist/queries/bottlenecks.js +1 -8
- package/dist/queries/by-kind.d.ts +1 -4
- package/dist/queries/by-kind.js +1 -10
- package/dist/queries/call-graph.d.ts +1 -1
- package/dist/queries/call-graph.js +1 -8
- package/dist/queries/change-surface.d.ts +1 -1
- package/dist/queries/change-surface.js +1 -8
- package/dist/queries/code.d.ts +1 -1
- package/dist/queries/code.js +1 -8
- package/dist/queries/complexity-hotspots.d.ts +5 -10
- package/dist/queries/complexity-hotspots.js +1 -8
- package/dist/queries/complexity.d.ts +1 -1
- package/dist/queries/complexity.js +1 -8
- package/dist/queries/convergence.d.ts +1 -1
- package/dist/queries/convergence.js +1 -8
- package/dist/queries/coupling.d.ts +1 -1
- package/dist/queries/coupling.js +1 -10
- package/dist/queries/cycles.d.ts +1 -1
- package/dist/queries/cycles.js +1 -8
- package/dist/queries/dataflow.d.ts +1 -1
- package/dist/queries/dataflow.js +1 -8
- package/dist/queries/dead.d.ts +1 -1
- package/dist/queries/dead.js +1 -9
- package/dist/queries/deep-chains.d.ts +9 -1
- package/dist/queries/deep-chains.js +1 -8
- package/dist/queries/deps.d.ts +1 -1
- package/dist/queries/deps.js +1 -10
- package/dist/queries/diff-impact.d.ts +1 -1
- package/dist/queries/diff-impact.js +1 -7
- package/dist/queries/drift.d.ts +1 -1
- package/dist/queries/drift.js +1 -8
- package/dist/queries/extract-candidates.d.ts +1 -1
- package/dist/queries/extract-candidates.js +1 -8
- package/dist/queries/fan.d.ts +1 -1
- package/dist/queries/fan.js +1 -14
- package/dist/queries/files.d.ts +1 -1
- package/dist/queries/files.js +1 -6
- package/dist/queries/health.d.ts +1 -1
- package/dist/queries/health.js +1 -20
- package/dist/queries/hierarchy.d.ts +1 -1
- package/dist/queries/hierarchy.js +1 -8
- package/dist/queries/hotspots.d.ts +1 -1
- package/dist/queries/hotspots.js +1 -8
- package/dist/queries/imports.d.ts +1 -1
- package/dist/queries/imports.js +1 -12
- package/dist/queries/index.d.ts +1 -1
- package/dist/queries/index.js +1 -197
- package/dist/queries/isolated.d.ts +1 -1
- package/dist/queries/isolated.js +1 -9
- package/dist/queries/members.d.ts +1 -1
- package/dist/queries/members.js +1 -8
- package/dist/queries/methods.d.ts +1 -1
- package/dist/queries/methods.js +1 -8
- package/dist/queries/outline.d.ts +1 -1
- package/dist/queries/outline.js +1 -8
- package/dist/queries/passthrough-candidates.d.ts +1 -1
- package/dist/queries/passthrough-candidates.js +1 -8
- package/dist/queries/redundant-reexports.d.ts +1 -1
- package/dist/queries/redundant-reexports.js +1 -9
- package/dist/queries/refs.d.ts +1 -1
- package/dist/queries/refs.js +1 -8
- package/dist/queries/similar-chains.d.ts +1 -1
- package/dist/queries/similar-chains.js +1 -8
- package/dist/queries/similar-files.d.ts +1 -1
- package/dist/queries/similar-files.js +1 -8
- package/dist/queries/similar-signatures.d.ts +1 -1
- package/dist/queries/similar-signatures.js +1 -8
- package/dist/queries/similar.d.ts +1 -1
- package/dist/queries/similar.js +1 -10
- package/dist/queries/slice.d.ts +1 -1
- package/dist/queries/slice.js +1 -8
- package/dist/queries/stale-abstractions.d.ts +15 -5
- package/dist/queries/stale-abstractions.js +1 -8
- package/dist/queries/stats.d.ts +1 -1
- package/dist/queries/stats.js +1 -6
- package/dist/queries/surface.d.ts +1 -1
- package/dist/queries/surface.js +1 -8
- package/dist/queries/symbols.d.ts +1 -1
- package/dist/queries/symbols.js +1 -9
- package/dist/queries/system.d.ts +1 -1
- package/dist/queries/system.js +1 -9
- package/dist/queries/trace.d.ts +1 -1
- package/dist/queries/trace.js +1 -9
- package/dist/queries/wrapper-candidates.d.ts +1 -1
- package/dist/queries/wrapper-candidates.js +1 -8
- package/dist/reindex-worker.js +7 -672
- package/package.json +20 -2
- package/skills/concrete-plan/SKILL.md +3 -3
- package/skills/scip-debloat/SKILL.md +3 -6
- package/skills/scip-language-playbook/SKILL.md +2 -0
- package/dist/chunk-2MQ5DPY6.js +0 -61
- package/dist/chunk-2QTYIOJ5.js +0 -165
- package/dist/chunk-3VI4YXCL.js +0 -172
- package/dist/chunk-3VV2G6U7.js +0 -34
- package/dist/chunk-44PFXVXG.js +0 -76
- package/dist/chunk-6SLFQR36.js +0 -64
- package/dist/chunk-74RFWB5T.js +0 -24
- package/dist/chunk-7DBPRGMS.js +0 -221
- package/dist/chunk-DTB724R3.js +0 -110
- package/dist/chunk-FLOYI6I4.js +0 -185
- package/dist/chunk-FO2CBB7U.js +0 -23
- package/dist/chunk-GJT3MO2T.js +0 -17
- package/dist/chunk-HAP4LJKX.js +0 -66
- package/dist/chunk-JCOJQ4I6.js +0 -93
- package/dist/chunk-JGQMOS4V.js +0 -59
- package/dist/chunk-JMD4WJ2Q.js +0 -213
- package/dist/chunk-JSQPZOPO.js +0 -64
- package/dist/chunk-JSXGC2EH.js +0 -151
- package/dist/chunk-JZN3DRCT.js +0 -59
- package/dist/chunk-KMWYB3CX.js +0 -71
- package/dist/chunk-MRM755FU.js +0 -37
- package/dist/chunk-N2XO3Z5F.js +0 -69
- package/dist/chunk-OLW5UL36.js +0 -76
- package/dist/chunk-OMCRXXDX.js +0 -2600
- package/dist/chunk-OWJOHUZE.js +0 -44
- package/dist/chunk-P3VCDYMJ.js +0 -269
- package/dist/chunk-PEDH3D4G.js +0 -53
- package/dist/chunk-POAN4SCR.js +0 -46
- package/dist/chunk-PTMGEBU3.js +0 -101
- package/dist/chunk-PU44HK7P.js +0 -87
- package/dist/chunk-QJI7EECA.js +0 -327
- package/dist/chunk-R5HICGMB.js +0 -110
- package/dist/chunk-RJ2D6YWQ.js +0 -49
- package/dist/chunk-RZ5GYPBP.js +0 -79
- package/dist/chunk-SRLQNO6O.js +0 -101
- package/dist/chunk-UGS7HJI4.js +0 -84
- package/dist/chunk-VKUUXOE7.js +0 -105
- package/dist/chunk-VPUJSJCI.js +0 -84
- package/dist/chunk-VRWVV3EP.js +0 -72
- package/dist/chunk-WJWQEU4A.js +0 -162
- package/dist/chunk-WJZHDUSB.js +0 -40
- package/dist/chunk-WWOCQ5W4.js +0 -34
- package/dist/chunk-X3Q2OVRL.js +0 -77
- package/dist/chunk-Y3P7QKKN.js +0 -27
- package/dist/chunk-Y6FAHY4N.js +0 -81
- package/dist/chunk-YMSJCSRG.js +0 -213
- package/dist/chunk-ZDL3U4W2.js +0 -124
- package/dist/chunk-ZXNX5JRE.js +0 -216
- package/dist/queries/clean-signature.d.ts +0 -17
- package/dist/queries/clean-signature.js +0 -9
package/dist/chunk-OMCRXXDX.js
DELETED
|
@@ -1,2600 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isFunctionLikeSymbol,
|
|
3
|
-
isModuleLikeSymbol,
|
|
4
|
-
leafName,
|
|
5
|
-
leafSuffix,
|
|
6
|
-
parseSymbol,
|
|
7
|
-
shortenSymbol
|
|
8
|
-
} from "./chunk-YMSJCSRG.js";
|
|
9
|
-
|
|
10
|
-
// src/source-analysis.ts
|
|
11
|
-
import {
|
|
12
|
-
existsSync,
|
|
13
|
-
readFileSync
|
|
14
|
-
} from "fs";
|
|
15
|
-
import {
|
|
16
|
-
basename,
|
|
17
|
-
dirname,
|
|
18
|
-
extname,
|
|
19
|
-
join,
|
|
20
|
-
relative,
|
|
21
|
-
resolve
|
|
22
|
-
} from "path";
|
|
23
|
-
var SOURCE_IMPORT_CACHE = /* @__PURE__ */ new WeakMap();
|
|
24
|
-
var SOURCE_EXPORT_CACHE = /* @__PURE__ */ new WeakMap();
|
|
25
|
-
var SOURCE_TEXT_CACHE = /* @__PURE__ */ new WeakMap();
|
|
26
|
-
var SOURCE_CALL_CACHE = /* @__PURE__ */ new WeakMap();
|
|
27
|
-
var SOURCE_BINDING_CACHE = /* @__PURE__ */ new WeakMap();
|
|
28
|
-
var INDEXED_PATH_CACHE = /* @__PURE__ */ new WeakMap();
|
|
29
|
-
var SOURCE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
30
|
-
var PYTHON_SOURCE_EXTENSIONS = [".py", ".pyi"];
|
|
31
|
-
var JVM_SOURCE_EXTENSIONS = [".java", ".scala", ".kt", ".kts"];
|
|
32
|
-
var RUST_SOURCE_EXTENSIONS = [".rs"];
|
|
33
|
-
var RUBY_SOURCE_EXTENSIONS = [".rb"];
|
|
34
|
-
var C_LIKE_SOURCE_EXTENSIONS = [".c", ".h", ".cc", ".cpp", ".cxx", ".hpp", ".hh", ".hxx"];
|
|
35
|
-
var DOTNET_SOURCE_EXTENSIONS = [".cs", ".vb"];
|
|
36
|
-
var DART_SOURCE_EXTENSIONS = [".dart"];
|
|
37
|
-
var PHP_SOURCE_EXTENSIONS = [".php"];
|
|
38
|
-
function getSourceImports(db, relativePath) {
|
|
39
|
-
const cache = getCachedMap(SOURCE_IMPORT_CACHE, db);
|
|
40
|
-
const normalized = normalizePath(relativePath);
|
|
41
|
-
const cached = cache.get(normalized);
|
|
42
|
-
if (cached) {
|
|
43
|
-
return cached;
|
|
44
|
-
}
|
|
45
|
-
const fullPath = join(db.config.projectRoot, normalized);
|
|
46
|
-
if (!existsSync(fullPath)) {
|
|
47
|
-
cache.set(normalized, []);
|
|
48
|
-
return [];
|
|
49
|
-
}
|
|
50
|
-
const source = readFileSync(fullPath, "utf-8");
|
|
51
|
-
const parsed = isPythonSourcePath(normalized) ? parsePythonImports(db, normalized, source) : isJavaScriptSourcePath(normalized) ? parseJavaScriptImports(db, normalized, source) : isJvmSourcePath(normalized) ? parseJvmImports(db, normalized, source) : isRustSourcePath(normalized) ? parseRustImports(db, normalized, source) : isRubySourcePath(normalized) ? parseRubyImports(db, normalized, source) : isCLikeSourcePath(normalized) ? parseCLikeImports(db, normalized, source) : isDotNetSourcePath(normalized) ? parseDotNetImports(db, normalized, source) : isDartSourcePath(normalized) ? parseDartImports(db, normalized, source) : isPhpSourcePath(normalized) ? parsePhpImports(db, normalized, source) : [];
|
|
52
|
-
cache.set(normalized, parsed);
|
|
53
|
-
return parsed;
|
|
54
|
-
}
|
|
55
|
-
function getSourceExports(db, relativePath) {
|
|
56
|
-
const cache = getCachedMap(SOURCE_EXPORT_CACHE, db);
|
|
57
|
-
const normalized = normalizePath(relativePath);
|
|
58
|
-
const cached = cache.get(normalized);
|
|
59
|
-
if (cached) {
|
|
60
|
-
return cached;
|
|
61
|
-
}
|
|
62
|
-
const fullPath = join(db.config.projectRoot, normalized);
|
|
63
|
-
if (!existsSync(fullPath)) {
|
|
64
|
-
cache.set(normalized, []);
|
|
65
|
-
return [];
|
|
66
|
-
}
|
|
67
|
-
const source = readFileSync(fullPath, "utf-8");
|
|
68
|
-
const parsed = isDartSourcePath(normalized) ? parseDartExports(db, normalized, source) : isRustSourcePath(normalized) ? parseRustExports(db, normalized, source) : [];
|
|
69
|
-
cache.set(normalized, parsed);
|
|
70
|
-
return parsed;
|
|
71
|
-
}
|
|
72
|
-
function getSourceCalls(db, relativePath, opts = {}) {
|
|
73
|
-
const normalized = normalizePath(relativePath);
|
|
74
|
-
if (!isPythonSourcePath(normalized) && !isJavaScriptSourcePath(normalized)) {
|
|
75
|
-
return [];
|
|
76
|
-
}
|
|
77
|
-
const cache = getCachedMap(SOURCE_CALL_CACHE, db);
|
|
78
|
-
const key = `${normalized}:${opts.startLine ?? 0}:${opts.endLine ?? Number.MAX_SAFE_INTEGER}`;
|
|
79
|
-
const cached = cache.get(key);
|
|
80
|
-
if (cached) {
|
|
81
|
-
return cached;
|
|
82
|
-
}
|
|
83
|
-
const source = getSourceText(db, normalized);
|
|
84
|
-
if (!source) {
|
|
85
|
-
cache.set(key, []);
|
|
86
|
-
return [];
|
|
87
|
-
}
|
|
88
|
-
const lines = source.split("\n");
|
|
89
|
-
const startLine = Math.max(0, opts.startLine ?? 0);
|
|
90
|
-
const endLine = Math.min(lines.length - 1, opts.endLine ?? lines.length - 1);
|
|
91
|
-
const scopedLines = lines.slice(startLine, endLine + 1);
|
|
92
|
-
const calls = isPythonSourcePath(normalized) ? parsePythonCalls(scopedLines, startLine) : parseJavaScriptCalls(scopedLines, startLine);
|
|
93
|
-
cache.set(key, calls);
|
|
94
|
-
return calls;
|
|
95
|
-
}
|
|
96
|
-
function getSourceConstructorBindings(db, relativePath, opts = {}) {
|
|
97
|
-
const normalized = normalizePath(relativePath);
|
|
98
|
-
if (!isPythonSourcePath(normalized) && !isJavaScriptSourcePath(normalized)) {
|
|
99
|
-
return [];
|
|
100
|
-
}
|
|
101
|
-
const cache = getCachedMap(SOURCE_BINDING_CACHE, db);
|
|
102
|
-
const key = `${normalized}:${opts.startLine ?? 0}:${opts.endLine ?? Number.MAX_SAFE_INTEGER}`;
|
|
103
|
-
const cached = cache.get(key);
|
|
104
|
-
if (cached) {
|
|
105
|
-
return cached;
|
|
106
|
-
}
|
|
107
|
-
const source = getSourceText(db, normalized);
|
|
108
|
-
if (!source) {
|
|
109
|
-
cache.set(key, []);
|
|
110
|
-
return [];
|
|
111
|
-
}
|
|
112
|
-
const lines = source.split("\n");
|
|
113
|
-
const startLine = Math.max(0, opts.startLine ?? 0);
|
|
114
|
-
const endLine = Math.min(lines.length - 1, opts.endLine ?? lines.length - 1);
|
|
115
|
-
const scopedLines = lines.slice(startLine, endLine + 1);
|
|
116
|
-
const bindings = isPythonSourcePath(normalized) ? parsePythonConstructorBindings(scopedLines) : parseJavaScriptConstructorBindings(scopedLines);
|
|
117
|
-
cache.set(key, bindings);
|
|
118
|
-
return bindings;
|
|
119
|
-
}
|
|
120
|
-
function findIdentifierLines(db, relativePath, identifier, opts = {}) {
|
|
121
|
-
if (!identifier) {
|
|
122
|
-
return [];
|
|
123
|
-
}
|
|
124
|
-
const source = getSourceText(db, normalizePath(relativePath));
|
|
125
|
-
if (!source) {
|
|
126
|
-
return [];
|
|
127
|
-
}
|
|
128
|
-
const lines = stripCommentsAndStrings(source).split("\n");
|
|
129
|
-
const regex = new RegExp(`\\b${escapeRegex(identifier)}\\b`);
|
|
130
|
-
const results = [];
|
|
131
|
-
for (let line = 0; line < lines.length; line++) {
|
|
132
|
-
if (typeof opts.excludeStartLine === "number" && typeof opts.excludeEndLine === "number" && line >= opts.excludeStartLine && line <= opts.excludeEndLine) {
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
if (regex.test(lines[line] ?? "")) {
|
|
136
|
-
results.push(line);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return results;
|
|
140
|
-
}
|
|
141
|
-
function parseJavaScriptImports(db, importerPath, source) {
|
|
142
|
-
return parseJavaScriptImportStatements(source).flatMap((statement) => parseJavaScriptImportStatement(
|
|
143
|
-
db,
|
|
144
|
-
importerPath,
|
|
145
|
-
statement.clause,
|
|
146
|
-
statement.specifier,
|
|
147
|
-
statement.start,
|
|
148
|
-
statement.end,
|
|
149
|
-
source
|
|
150
|
-
));
|
|
151
|
-
}
|
|
152
|
-
function parseJavaScriptImportStatements(source) {
|
|
153
|
-
const statements = [];
|
|
154
|
-
const importFromRegex = /^[ \t]*import\s+([\s\S]*?)\s+from\s+['"]([^'"]+)['"]\s*;?/gm;
|
|
155
|
-
for (const match of source.matchAll(importFromRegex)) {
|
|
156
|
-
const full = match[0];
|
|
157
|
-
const clause = match[1];
|
|
158
|
-
const specifier = match[2];
|
|
159
|
-
if (!full || !specifier || typeof match.index !== "number") continue;
|
|
160
|
-
statements.push({
|
|
161
|
-
clause,
|
|
162
|
-
specifier,
|
|
163
|
-
start: match.index,
|
|
164
|
-
end: match.index + full.length
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
const sideEffectRegex = /^[ \t]*import\s+['"]([^'"]+)['"]\s*;?/gm;
|
|
168
|
-
for (const match of source.matchAll(sideEffectRegex)) {
|
|
169
|
-
const full = match[0];
|
|
170
|
-
const specifier = match[1];
|
|
171
|
-
if (!full || !specifier || typeof match.index !== "number") continue;
|
|
172
|
-
statements.push({
|
|
173
|
-
clause: null,
|
|
174
|
-
specifier,
|
|
175
|
-
start: match.index,
|
|
176
|
-
end: match.index + full.length
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
return statements.sort((a, b) => a.start - b.start);
|
|
180
|
-
}
|
|
181
|
-
function parseJavaScriptImportStatement(db, importerPath, clause, specifier, start, end, source) {
|
|
182
|
-
const resolvedSource = resolveImportPath(db, importerPath, specifier);
|
|
183
|
-
const body = buildUsageBody(source, start, end);
|
|
184
|
-
if (!clause) {
|
|
185
|
-
return [{
|
|
186
|
-
importedName: "*",
|
|
187
|
-
localName: null,
|
|
188
|
-
sourcePath: resolvedSource,
|
|
189
|
-
kind: "side-effect",
|
|
190
|
-
used: true,
|
|
191
|
-
usedMembers: []
|
|
192
|
-
}];
|
|
193
|
-
}
|
|
194
|
-
const bindings = parseImportClause(clause).map((binding) => ({
|
|
195
|
-
...binding,
|
|
196
|
-
sourcePath: resolvedSource
|
|
197
|
-
}));
|
|
198
|
-
return bindings.map((binding) => {
|
|
199
|
-
if (binding.kind === "namespace") {
|
|
200
|
-
const usedMembers = collectNamespaceMembers(body, binding.localName);
|
|
201
|
-
return {
|
|
202
|
-
...binding,
|
|
203
|
-
used: usedMembers.length > 0 || hasIdentifierUsage(body, binding.localName),
|
|
204
|
-
usedMembers
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
if (binding.kind === "side-effect") {
|
|
208
|
-
return { ...binding, used: true, usedMembers: [] };
|
|
209
|
-
}
|
|
210
|
-
return {
|
|
211
|
-
...binding,
|
|
212
|
-
used: binding.localName ? hasIdentifierUsage(body, binding.localName) : false,
|
|
213
|
-
usedMembers: []
|
|
214
|
-
};
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
function parseJvmImports(db, importerPath, source) {
|
|
218
|
-
const statements = [];
|
|
219
|
-
for (const match of source.matchAll(/^[ \t]*import\s+(?:static\s+)?(.+?)\s*;?$/gm)) {
|
|
220
|
-
const clause = match[1]?.trim();
|
|
221
|
-
const full = match[0];
|
|
222
|
-
if (!clause || !full || typeof match.index !== "number") continue;
|
|
223
|
-
const body = buildUsageBody(source, match.index, match.index + full.length);
|
|
224
|
-
statements.push(...parseJvmImportClause(db, importerPath, clause, body));
|
|
225
|
-
}
|
|
226
|
-
return statements;
|
|
227
|
-
}
|
|
228
|
-
function parseJvmImportClause(db, importerPath, clause, body) {
|
|
229
|
-
if (clause.includes("{") && clause.includes("}")) {
|
|
230
|
-
const prefix = clause.slice(0, clause.indexOf("{")).replace(/\.$/, "").trim();
|
|
231
|
-
const inner = clause.slice(clause.indexOf("{") + 1, clause.lastIndexOf("}")).trim();
|
|
232
|
-
return splitTopLevel(inner).flatMap((entry) => {
|
|
233
|
-
const cleaned = entry.trim();
|
|
234
|
-
if (!cleaned) return [];
|
|
235
|
-
const [importedPart, aliasPart] = cleaned.includes("=>") ? cleaned.split(/\s*=>\s*/) : cleaned.split(/\s+as\s+/);
|
|
236
|
-
const importedName = importedPart?.trim();
|
|
237
|
-
if (!importedName || importedName === "_") return [];
|
|
238
|
-
const localName = (aliasPart ?? importedName.split(".").pop() ?? importedName).trim();
|
|
239
|
-
const qualified = importedName === "_" ? prefix : `${prefix}.${importedName}`.replace(/\.\./g, ".");
|
|
240
|
-
return [buildSimpleImport(db, importerPath, body, qualified, importedName, localName)];
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
return [buildSimpleImport(
|
|
244
|
-
db,
|
|
245
|
-
importerPath,
|
|
246
|
-
body,
|
|
247
|
-
clause,
|
|
248
|
-
clause.split(".").pop() ?? clause,
|
|
249
|
-
clause.split(".").pop() ?? clause
|
|
250
|
-
)];
|
|
251
|
-
}
|
|
252
|
-
function parseRustImports(db, importerPath, source) {
|
|
253
|
-
const statements = [];
|
|
254
|
-
for (const match of source.matchAll(/^[ \t]*use\s+(.+?)\s*;$/gm)) {
|
|
255
|
-
const clause = match[1]?.trim();
|
|
256
|
-
const full = match[0];
|
|
257
|
-
if (!clause || !full || typeof match.index !== "number") continue;
|
|
258
|
-
const body = buildUsageBody(source, match.index, match.index + full.length);
|
|
259
|
-
statements.push(...parseRustUseClause(db, importerPath, clause, body));
|
|
260
|
-
}
|
|
261
|
-
return statements;
|
|
262
|
-
}
|
|
263
|
-
function parseRustUseClause(db, importerPath, clause, body) {
|
|
264
|
-
const trimmed = clause.trim();
|
|
265
|
-
if (trimmed.includes("{") && trimmed.includes("}")) {
|
|
266
|
-
const prefix = trimmed.slice(0, trimmed.indexOf("{")).replace(/::$/, "").trim();
|
|
267
|
-
const inner = trimmed.slice(trimmed.indexOf("{") + 1, trimmed.lastIndexOf("}")).trim();
|
|
268
|
-
return splitTopLevel(inner).flatMap((entry) => {
|
|
269
|
-
const cleaned = entry.trim();
|
|
270
|
-
if (!cleaned || cleaned === "self") return [];
|
|
271
|
-
const [importedPart2, aliasPart2] = cleaned.split(/\s+as\s+/);
|
|
272
|
-
const importedName2 = importedPart2?.trim();
|
|
273
|
-
if (!importedName2) return [];
|
|
274
|
-
const localName2 = (aliasPart2 ?? importedName2.split("::").pop() ?? importedName2).trim();
|
|
275
|
-
const moduleSpecifier = `${prefix}::${importedName2}`.replace(/::::/g, "::");
|
|
276
|
-
return [buildSimpleImport(
|
|
277
|
-
db,
|
|
278
|
-
importerPath,
|
|
279
|
-
body,
|
|
280
|
-
moduleSpecifier,
|
|
281
|
-
importedName2.split("::").pop() ?? importedName2,
|
|
282
|
-
localName2,
|
|
283
|
-
resolveRustImportPath(db, importerPath, prefix)
|
|
284
|
-
)];
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
const [importedPart, aliasPart] = trimmed.split(/\s+as\s+/);
|
|
288
|
-
const importedName = importedPart?.trim() ?? trimmed;
|
|
289
|
-
const localName = (aliasPart ?? importedName.split("::").pop() ?? importedName).trim();
|
|
290
|
-
const resolved = resolveRustImportPath(db, importerPath, importedName) ?? resolveRustImportPath(db, importerPath, importedName.split("::").slice(0, -1).join("::"));
|
|
291
|
-
return [buildSimpleImport(
|
|
292
|
-
db,
|
|
293
|
-
importerPath,
|
|
294
|
-
body,
|
|
295
|
-
importedName,
|
|
296
|
-
importedName.split("::").pop() ?? importedName,
|
|
297
|
-
localName,
|
|
298
|
-
resolved
|
|
299
|
-
)];
|
|
300
|
-
}
|
|
301
|
-
function parseRustExports(db, importerPath, source) {
|
|
302
|
-
const statements = [];
|
|
303
|
-
for (const match of source.matchAll(/^[ \t]*pub\s+use\s+(.+?)\s*;$/gm)) {
|
|
304
|
-
const clause = match[1]?.trim();
|
|
305
|
-
if (!clause) continue;
|
|
306
|
-
statements.push(...parseRustExportClause(db, importerPath, clause));
|
|
307
|
-
}
|
|
308
|
-
return statements;
|
|
309
|
-
}
|
|
310
|
-
function parseRustExportClause(db, importerPath, clause) {
|
|
311
|
-
const trimmed = clause.trim();
|
|
312
|
-
if (trimmed.includes("{") && trimmed.includes("}")) {
|
|
313
|
-
const prefix = trimmed.slice(0, trimmed.indexOf("{")).replace(/::$/, "").trim();
|
|
314
|
-
const inner = trimmed.slice(trimmed.indexOf("{") + 1, trimmed.lastIndexOf("}")).trim();
|
|
315
|
-
return splitTopLevel(inner).flatMap((entry) => {
|
|
316
|
-
const cleaned = entry.trim();
|
|
317
|
-
if (!cleaned || cleaned === "self") return [];
|
|
318
|
-
const [qualifiedPart] = cleaned.split(/\s+as\s+/);
|
|
319
|
-
const qualified = `${prefix}::${qualifiedPart?.trim() ?? cleaned}`.replace(/::::/g, "::");
|
|
320
|
-
return [buildRustExport(db, importerPath, qualified)];
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
return [buildRustExport(db, importerPath, trimmed)];
|
|
324
|
-
}
|
|
325
|
-
function buildRustExport(db, importerPath, specifier) {
|
|
326
|
-
return {
|
|
327
|
-
specifier,
|
|
328
|
-
sourcePath: resolveRustImportPath(db, importerPath, specifier) ?? resolveRustImportPath(db, importerPath, specifier.split("::").slice(0, -1).join("::"))
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
function parseRubyImports(db, importerPath, source) {
|
|
332
|
-
const statements = [];
|
|
333
|
-
for (const match of source.matchAll(/^[ \t]*(require_relative|require)\s+["']([^"']+)["']\s*$/gm)) {
|
|
334
|
-
const kind = match[1];
|
|
335
|
-
const specifier = match[2];
|
|
336
|
-
const full = match[0];
|
|
337
|
-
if (!kind || !specifier || !full || typeof match.index !== "number") continue;
|
|
338
|
-
const body = buildUsageBody(source, match.index, match.index + full.length);
|
|
339
|
-
const sourcePath = kind === "require_relative" ? resolveRubyImportPath(db, importerPath, specifier) : null;
|
|
340
|
-
if (sourcePath) {
|
|
341
|
-
const localName = rubyConstantName(specifier);
|
|
342
|
-
statements.push({
|
|
343
|
-
importedName: localName,
|
|
344
|
-
localName,
|
|
345
|
-
sourcePath,
|
|
346
|
-
kind: "named",
|
|
347
|
-
used: hasIdentifierUsage(body, localName),
|
|
348
|
-
usedMembers: []
|
|
349
|
-
});
|
|
350
|
-
continue;
|
|
351
|
-
}
|
|
352
|
-
statements.push({
|
|
353
|
-
importedName: specifier,
|
|
354
|
-
localName: null,
|
|
355
|
-
sourcePath,
|
|
356
|
-
kind: "side-effect",
|
|
357
|
-
used: true,
|
|
358
|
-
usedMembers: []
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
return statements;
|
|
362
|
-
}
|
|
363
|
-
function rubyConstantName(specifier) {
|
|
364
|
-
return basename(specifier).replace(/\.[^.]+$/, "").split("_").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
365
|
-
}
|
|
366
|
-
function parseCLikeImports(db, importerPath, source) {
|
|
367
|
-
const statements = [];
|
|
368
|
-
for (const match of source.matchAll(/^[ \t]*#include\s+[<"]([^">]+)[">]\s*$/gm)) {
|
|
369
|
-
const specifier = match[1]?.trim();
|
|
370
|
-
const full = match[0];
|
|
371
|
-
if (!specifier || !full || typeof match.index !== "number") continue;
|
|
372
|
-
const body = buildUsageBody(source, match.index, match.index + full.length);
|
|
373
|
-
const localName = basename(specifier).replace(/\.[^.]+$/, "");
|
|
374
|
-
statements.push({
|
|
375
|
-
importedName: specifier,
|
|
376
|
-
localName,
|
|
377
|
-
sourcePath: resolveCLikeImportPath(db, importerPath, specifier),
|
|
378
|
-
kind: "named",
|
|
379
|
-
used: hasIdentifierUsage(body, localName),
|
|
380
|
-
usedMembers: []
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
return statements;
|
|
384
|
-
}
|
|
385
|
-
function parseDotNetImports(db, importerPath, source) {
|
|
386
|
-
const statements = [];
|
|
387
|
-
const lineRegex = isVisualBasicSourcePath(importerPath) ? /^[ \t]*Imports\s+(.+?)\s*$/gm : /^[ \t]*using\s+(.+?)\s*;$/gm;
|
|
388
|
-
for (const match of source.matchAll(lineRegex)) {
|
|
389
|
-
const clause = match[1]?.trim();
|
|
390
|
-
const full = match[0];
|
|
391
|
-
if (!clause || !full || typeof match.index !== "number") continue;
|
|
392
|
-
const body = buildUsageBody(source, match.index, match.index + full.length);
|
|
393
|
-
const [aliasPart, targetPart] = isVisualBasicSourcePath(importerPath) ? clause.split(/\s*=\s*/) : clause.split(/\s*=\s*/);
|
|
394
|
-
const hasAlias = Boolean(targetPart);
|
|
395
|
-
const qualified = (hasAlias ? targetPart : aliasPart)?.trim() ?? clause;
|
|
396
|
-
const importedName = qualified.split(".").pop() ?? qualified;
|
|
397
|
-
const localName = hasAlias ? aliasPart?.trim() ?? importedName : importedName;
|
|
398
|
-
statements.push(buildSimpleImport(
|
|
399
|
-
db,
|
|
400
|
-
importerPath,
|
|
401
|
-
body,
|
|
402
|
-
qualified,
|
|
403
|
-
importedName,
|
|
404
|
-
localName,
|
|
405
|
-
resolveQualifiedImportPath(db, qualified, DOTNET_SOURCE_EXTENSIONS)
|
|
406
|
-
));
|
|
407
|
-
}
|
|
408
|
-
return statements;
|
|
409
|
-
}
|
|
410
|
-
function parseDartImports(db, importerPath, source) {
|
|
411
|
-
const statements = [];
|
|
412
|
-
for (const match of source.matchAll(/^[ \t]*import\s+['"]([^'"]+)['"](?:\s+as\s+([A-Za-z_]\w*))?[\s\S]*?;$/gm)) {
|
|
413
|
-
const specifier = match[1]?.trim();
|
|
414
|
-
const alias = match[2]?.trim() ?? null;
|
|
415
|
-
const full = match[0];
|
|
416
|
-
if (!specifier || !full || typeof match.index !== "number") continue;
|
|
417
|
-
const body = buildUsageBody(source, match.index, match.index + full.length);
|
|
418
|
-
statements.push({
|
|
419
|
-
importedName: specifier,
|
|
420
|
-
localName: alias,
|
|
421
|
-
sourcePath: resolveDartImportPath(db, importerPath, specifier),
|
|
422
|
-
kind: alias ? "namespace" : "side-effect",
|
|
423
|
-
used: alias ? hasIdentifierUsage(body, alias) : true,
|
|
424
|
-
usedMembers: alias ? collectNamespaceMembers(body, alias) : []
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
return statements;
|
|
428
|
-
}
|
|
429
|
-
function parseDartExports(db, importerPath, source) {
|
|
430
|
-
const statements = [];
|
|
431
|
-
for (const match of source.matchAll(/^[ \t]*export\s+['"]([^'"]+)['"][\s\S]*?;$/gm)) {
|
|
432
|
-
const specifier = match[1]?.trim();
|
|
433
|
-
if (!specifier) continue;
|
|
434
|
-
statements.push({
|
|
435
|
-
specifier,
|
|
436
|
-
sourcePath: resolveDartImportPath(db, importerPath, specifier)
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
return statements;
|
|
440
|
-
}
|
|
441
|
-
function parsePhpImports(db, importerPath, source) {
|
|
442
|
-
const statements = [];
|
|
443
|
-
for (const match of source.matchAll(/^[ \t]*use\s+(.+?)\s*;$/gm)) {
|
|
444
|
-
const clause = match[1]?.trim();
|
|
445
|
-
const full = match[0];
|
|
446
|
-
if (!clause || !full || typeof match.index !== "number") continue;
|
|
447
|
-
const body = buildUsageBody(source, match.index, match.index + full.length);
|
|
448
|
-
for (const entry of splitTopLevel(clause)) {
|
|
449
|
-
const cleaned = entry.trim();
|
|
450
|
-
if (!cleaned) continue;
|
|
451
|
-
const [qualifiedPart, aliasPart] = cleaned.split(/\s+as\s+/i);
|
|
452
|
-
const qualified = qualifiedPart?.trim() ?? cleaned;
|
|
453
|
-
const importedName = qualified.split("\\").pop() ?? qualified;
|
|
454
|
-
const localName = (aliasPart ?? importedName).trim();
|
|
455
|
-
statements.push(buildSimpleImport(
|
|
456
|
-
db,
|
|
457
|
-
importerPath,
|
|
458
|
-
body,
|
|
459
|
-
qualified,
|
|
460
|
-
importedName,
|
|
461
|
-
localName,
|
|
462
|
-
resolveQualifiedImportPath(db, qualified.replace(/\\/g, "."), PHP_SOURCE_EXTENSIONS)
|
|
463
|
-
));
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
return statements;
|
|
467
|
-
}
|
|
468
|
-
function buildSimpleImport(db, importerPath, body, qualifiedName, importedName, localName, sourcePath) {
|
|
469
|
-
return {
|
|
470
|
-
importedName,
|
|
471
|
-
localName,
|
|
472
|
-
sourcePath: sourcePath ?? resolveQualifiedImportPath(db, qualifiedName, extensionFamilyFor(importerPath)),
|
|
473
|
-
kind: "named",
|
|
474
|
-
used: hasIdentifierUsage(body, localName),
|
|
475
|
-
usedMembers: []
|
|
476
|
-
};
|
|
477
|
-
}
|
|
478
|
-
function parsePythonCalls(lines, baseLine) {
|
|
479
|
-
const calls = [];
|
|
480
|
-
const controlKeywords = /* @__PURE__ */ new Set([
|
|
481
|
-
"if",
|
|
482
|
-
"for",
|
|
483
|
-
"while",
|
|
484
|
-
"with",
|
|
485
|
-
"except",
|
|
486
|
-
"elif",
|
|
487
|
-
"return",
|
|
488
|
-
"yield",
|
|
489
|
-
"assert",
|
|
490
|
-
"raise",
|
|
491
|
-
"lambda",
|
|
492
|
-
"class",
|
|
493
|
-
"def"
|
|
494
|
-
]);
|
|
495
|
-
for (let index = 0; index < lines.length; index++) {
|
|
496
|
-
const rawLine = lines[index] ?? "";
|
|
497
|
-
const stripped = stripCommentsAndStrings(rawLine);
|
|
498
|
-
if (!stripped.trim()) continue;
|
|
499
|
-
const attributeMatches = [...stripped.matchAll(/\b([A-Za-z_][\w]*)\s*\.\s*([A-Za-z_][\w]*)\s*\(/g)];
|
|
500
|
-
const attributeRanges = attributeMatches.map((match) => ({
|
|
501
|
-
start: match.index ?? -1,
|
|
502
|
-
end: (match.index ?? -1) + match[0].length
|
|
503
|
-
}));
|
|
504
|
-
for (const match of attributeMatches) {
|
|
505
|
-
const receiverName = match[1];
|
|
506
|
-
const calleeName = match[2];
|
|
507
|
-
if (!receiverName || !calleeName) continue;
|
|
508
|
-
calls.push({
|
|
509
|
-
receiverName,
|
|
510
|
-
calleeName,
|
|
511
|
-
line: baseLine + index
|
|
512
|
-
});
|
|
513
|
-
}
|
|
514
|
-
for (const match of stripped.matchAll(/\b([A-Za-z_][\w]*)\s*\(/g)) {
|
|
515
|
-
const calleeName = match[1];
|
|
516
|
-
const start = match.index ?? -1;
|
|
517
|
-
if (!calleeName || start < 0) continue;
|
|
518
|
-
if (controlKeywords.has(calleeName)) continue;
|
|
519
|
-
if (attributeRanges.some((range) => start >= range.start && start < range.end)) continue;
|
|
520
|
-
const prefix = stripped.slice(0, start).trimEnd();
|
|
521
|
-
if (prefix.endsWith("def") || prefix.endsWith("class") || prefix.endsWith("async def")) {
|
|
522
|
-
continue;
|
|
523
|
-
}
|
|
524
|
-
calls.push({
|
|
525
|
-
receiverName: null,
|
|
526
|
-
calleeName,
|
|
527
|
-
line: baseLine + index
|
|
528
|
-
});
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
return calls;
|
|
532
|
-
}
|
|
533
|
-
function parseJavaScriptCalls(lines, baseLine) {
|
|
534
|
-
const calls = [];
|
|
535
|
-
const controlKeywords = /* @__PURE__ */ new Set([
|
|
536
|
-
"if",
|
|
537
|
-
"for",
|
|
538
|
-
"while",
|
|
539
|
-
"switch",
|
|
540
|
-
"catch",
|
|
541
|
-
"function",
|
|
542
|
-
"class",
|
|
543
|
-
"return",
|
|
544
|
-
"typeof",
|
|
545
|
-
"import"
|
|
546
|
-
]);
|
|
547
|
-
for (let index = 0; index < lines.length; index++) {
|
|
548
|
-
const stripped = stripCommentsAndStrings(lines[index] ?? "");
|
|
549
|
-
if (!stripped.trim()) continue;
|
|
550
|
-
const attributeMatches = [
|
|
551
|
-
...stripped.matchAll(/\b([A-Za-z_$][\w$]*)\s*(?:\?\.|\.)\s*([A-Za-z_$][\w$]*)\s*\(/g),
|
|
552
|
-
...stripped.matchAll(/\bthis\s*(?:\?\.|\.)\s*([A-Za-z_$][\w$]*)\s*\(/g)
|
|
553
|
-
];
|
|
554
|
-
const attributeRanges = attributeMatches.map((match) => ({
|
|
555
|
-
start: match.index ?? -1,
|
|
556
|
-
end: (match.index ?? -1) + match[0].length
|
|
557
|
-
}));
|
|
558
|
-
for (const match of attributeMatches) {
|
|
559
|
-
const receiverName = match[2] ? match[1] : "this";
|
|
560
|
-
const calleeName = match[2] ?? match[1];
|
|
561
|
-
if (!receiverName || !calleeName) continue;
|
|
562
|
-
calls.push({
|
|
563
|
-
receiverName,
|
|
564
|
-
calleeName,
|
|
565
|
-
line: baseLine + index
|
|
566
|
-
});
|
|
567
|
-
}
|
|
568
|
-
for (const match of stripped.matchAll(/\b([A-Za-z_$][\w$]*)\s*\(/g)) {
|
|
569
|
-
const calleeName = match[1];
|
|
570
|
-
const start = match.index ?? -1;
|
|
571
|
-
if (!calleeName || start < 0) continue;
|
|
572
|
-
if (controlKeywords.has(calleeName)) continue;
|
|
573
|
-
if (attributeRanges.some((range) => start >= range.start && start < range.end)) continue;
|
|
574
|
-
const prefix = stripped.slice(0, start).trimEnd();
|
|
575
|
-
if (prefix.endsWith("function") || prefix.endsWith("class") || prefix.endsWith("new")) {
|
|
576
|
-
continue;
|
|
577
|
-
}
|
|
578
|
-
calls.push({
|
|
579
|
-
receiverName: null,
|
|
580
|
-
calleeName,
|
|
581
|
-
line: baseLine + index
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
return calls;
|
|
586
|
-
}
|
|
587
|
-
function parsePythonConstructorBindings(lines) {
|
|
588
|
-
const bindings = /* @__PURE__ */ new Map();
|
|
589
|
-
for (const rawLine of lines) {
|
|
590
|
-
const stripped = stripCommentsAndStrings(rawLine);
|
|
591
|
-
const constructorMatch = stripped.match(/\b([A-Za-z_][\w]*)\s*=\s*([A-Z][\w]*)\s*\(/);
|
|
592
|
-
if (constructorMatch?.[1] && constructorMatch[2]) {
|
|
593
|
-
bindings.set(constructorMatch[1], constructorMatch[2]);
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
return [...bindings.entries()].map(([localName, typeName]) => ({ localName, typeName }));
|
|
597
|
-
}
|
|
598
|
-
function parseJavaScriptConstructorBindings(lines) {
|
|
599
|
-
const bindings = /* @__PURE__ */ new Map();
|
|
600
|
-
for (const rawLine of lines) {
|
|
601
|
-
const stripped = stripCommentsAndStrings(rawLine);
|
|
602
|
-
for (const match of stripped.matchAll(/\b([A-Za-z_$][\w$]*)\s*:\s*([A-Z][A-Za-z0-9_$]*)\b/g)) {
|
|
603
|
-
const localName = match[1];
|
|
604
|
-
const typeName = match[2];
|
|
605
|
-
if (localName && typeName) {
|
|
606
|
-
bindings.set(localName, typeName);
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
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*\(/);
|
|
610
|
-
if (constructorMatch?.[1] && constructorMatch[2]) {
|
|
611
|
-
bindings.set(constructorMatch[1], constructorMatch[2]);
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
return [...bindings.entries()].map(([localName, typeName]) => ({ localName, typeName }));
|
|
615
|
-
}
|
|
616
|
-
function parsePythonImports(db, importerPath, source) {
|
|
617
|
-
return collectPythonImportStatements(source).flatMap(
|
|
618
|
-
(statement) => parsePythonImportStatement(db, importerPath, statement, source)
|
|
619
|
-
);
|
|
620
|
-
}
|
|
621
|
-
function collectPythonImportStatements(source) {
|
|
622
|
-
const lines = source.split("\n");
|
|
623
|
-
const statements = [];
|
|
624
|
-
let offset = 0;
|
|
625
|
-
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
626
|
-
const line = lines[lineIndex];
|
|
627
|
-
const trimmed = line.trimStart();
|
|
628
|
-
const lineStart = offset;
|
|
629
|
-
offset += line.length + 1;
|
|
630
|
-
if (!trimmed.startsWith("import ") && !trimmed.startsWith("from ")) {
|
|
631
|
-
continue;
|
|
632
|
-
}
|
|
633
|
-
let statement = line;
|
|
634
|
-
let statementEnd = lineStart + line.length;
|
|
635
|
-
let balance = pythonParenBalance(line);
|
|
636
|
-
while (lineIndex + 1 < lines.length && (balance > 0 || statement.trimEnd().endsWith("\\"))) {
|
|
637
|
-
lineIndex++;
|
|
638
|
-
const nextLine = lines[lineIndex];
|
|
639
|
-
statement += `
|
|
640
|
-
${nextLine}`;
|
|
641
|
-
statementEnd += 1 + nextLine.length;
|
|
642
|
-
balance += pythonParenBalance(nextLine);
|
|
643
|
-
offset += nextLine.length + 1;
|
|
644
|
-
}
|
|
645
|
-
const parsed = parsePythonStatementHeader(statement);
|
|
646
|
-
if (parsed) {
|
|
647
|
-
statements.push({
|
|
648
|
-
...parsed,
|
|
649
|
-
start: lineStart,
|
|
650
|
-
end: statementEnd
|
|
651
|
-
});
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
return statements;
|
|
655
|
-
}
|
|
656
|
-
function parsePythonStatementHeader(statement) {
|
|
657
|
-
const normalized = statement.replace(/\\\s*\n/g, " ").trim();
|
|
658
|
-
if (normalized.startsWith("import ")) {
|
|
659
|
-
return {
|
|
660
|
-
kind: "import",
|
|
661
|
-
module: null,
|
|
662
|
-
clause: normalized.slice("import ".length).trim()
|
|
663
|
-
};
|
|
664
|
-
}
|
|
665
|
-
const fromMatch = normalized.match(/^from\s+([.\w]+)\s+import\s+([\s\S]+)$/);
|
|
666
|
-
if (!fromMatch) {
|
|
667
|
-
return null;
|
|
668
|
-
}
|
|
669
|
-
let clause = fromMatch[2].trim();
|
|
670
|
-
if (clause.startsWith("(") && clause.endsWith(")")) {
|
|
671
|
-
clause = clause.slice(1, -1).trim();
|
|
672
|
-
}
|
|
673
|
-
return {
|
|
674
|
-
kind: "from",
|
|
675
|
-
module: fromMatch[1],
|
|
676
|
-
clause
|
|
677
|
-
};
|
|
678
|
-
}
|
|
679
|
-
function parsePythonImportStatement(db, importerPath, statement, source) {
|
|
680
|
-
const body = buildUsageBody(source, statement.start, statement.end);
|
|
681
|
-
const normalizedClause = statement.clause.replace(/\n/g, " ").trim();
|
|
682
|
-
if (statement.kind === "import") {
|
|
683
|
-
return splitTopLevel(normalizedClause).flatMap((entry) => {
|
|
684
|
-
const cleaned = entry.trim().replace(/,$/, "");
|
|
685
|
-
if (!cleaned) return [];
|
|
686
|
-
const [moduleName, alias] = cleaned.split(/\s+as\s+/);
|
|
687
|
-
const importedName = moduleName.trim();
|
|
688
|
-
const localName = (alias ?? importedName.split(".")[0] ?? importedName).trim();
|
|
689
|
-
const sourcePath2 = resolvePythonImportPath(db, importerPath, importedName);
|
|
690
|
-
const usedMembers = collectNamespaceMembers(body, localName);
|
|
691
|
-
return [{
|
|
692
|
-
importedName,
|
|
693
|
-
localName,
|
|
694
|
-
sourcePath: sourcePath2,
|
|
695
|
-
kind: "namespace",
|
|
696
|
-
used: hasIdentifierUsage(body, localName) || usedMembers.length > 0,
|
|
697
|
-
usedMembers
|
|
698
|
-
}];
|
|
699
|
-
});
|
|
700
|
-
}
|
|
701
|
-
const sourcePath = statement.module ? resolvePythonImportPath(db, importerPath, statement.module) : null;
|
|
702
|
-
const results = [];
|
|
703
|
-
for (const entry of splitTopLevel(normalizedClause)) {
|
|
704
|
-
const cleaned = entry.trim().replace(/,$/, "");
|
|
705
|
-
if (!cleaned) continue;
|
|
706
|
-
if (cleaned === "*") {
|
|
707
|
-
results.push({
|
|
708
|
-
importedName: "*",
|
|
709
|
-
localName: null,
|
|
710
|
-
sourcePath,
|
|
711
|
-
kind: "side-effect",
|
|
712
|
-
used: true,
|
|
713
|
-
usedMembers: []
|
|
714
|
-
});
|
|
715
|
-
continue;
|
|
716
|
-
}
|
|
717
|
-
const [importedName, alias] = cleaned.split(/\s+as\s+/);
|
|
718
|
-
const localName = (alias ?? importedName).trim();
|
|
719
|
-
results.push({
|
|
720
|
-
importedName: importedName.trim(),
|
|
721
|
-
localName,
|
|
722
|
-
sourcePath,
|
|
723
|
-
kind: "named",
|
|
724
|
-
used: hasIdentifierUsage(body, localName),
|
|
725
|
-
usedMembers: []
|
|
726
|
-
});
|
|
727
|
-
}
|
|
728
|
-
return results;
|
|
729
|
-
}
|
|
730
|
-
function parseImportClause(clause) {
|
|
731
|
-
const trimmed = clause.trim().replace(/^type\s+/, "");
|
|
732
|
-
const [first, second] = splitImportClause(trimmed);
|
|
733
|
-
const entries = [];
|
|
734
|
-
if (first) {
|
|
735
|
-
entries.push(...parseImportBinding(first));
|
|
736
|
-
}
|
|
737
|
-
if (second) {
|
|
738
|
-
entries.push(...parseImportBinding(second));
|
|
739
|
-
}
|
|
740
|
-
return entries;
|
|
741
|
-
}
|
|
742
|
-
function parseImportBinding(binding) {
|
|
743
|
-
const trimmed = binding.trim();
|
|
744
|
-
if (!trimmed) return [];
|
|
745
|
-
if (trimmed.startsWith("{")) {
|
|
746
|
-
const inner = trimmed.slice(1, -1).trim();
|
|
747
|
-
if (!inner) return [];
|
|
748
|
-
return splitTopLevel(inner).map((entry) => {
|
|
749
|
-
const cleaned = entry.trim().replace(/^type\s+/, "");
|
|
750
|
-
const [importedName, alias] = cleaned.split(/\s+as\s+/);
|
|
751
|
-
return {
|
|
752
|
-
importedName: importedName.trim(),
|
|
753
|
-
localName: (alias ?? importedName).trim(),
|
|
754
|
-
kind: "named"
|
|
755
|
-
};
|
|
756
|
-
});
|
|
757
|
-
}
|
|
758
|
-
if (trimmed.startsWith("* as ")) {
|
|
759
|
-
return [{
|
|
760
|
-
importedName: "*",
|
|
761
|
-
localName: trimmed.slice(5).trim(),
|
|
762
|
-
kind: "namespace"
|
|
763
|
-
}];
|
|
764
|
-
}
|
|
765
|
-
return [{
|
|
766
|
-
importedName: "default",
|
|
767
|
-
localName: trimmed,
|
|
768
|
-
kind: "default"
|
|
769
|
-
}];
|
|
770
|
-
}
|
|
771
|
-
function splitImportClause(clause) {
|
|
772
|
-
let depth = 0;
|
|
773
|
-
for (let i = 0; i < clause.length; i++) {
|
|
774
|
-
const char = clause[i];
|
|
775
|
-
if (char === "{") depth++;
|
|
776
|
-
if (char === "}") depth--;
|
|
777
|
-
if (char === "," && depth === 0) {
|
|
778
|
-
return [clause.slice(0, i).trim(), clause.slice(i + 1).trim()];
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
return [clause.trim(), null];
|
|
782
|
-
}
|
|
783
|
-
function splitTopLevel(input) {
|
|
784
|
-
const parts = [];
|
|
785
|
-
let depth = 0;
|
|
786
|
-
let start = 0;
|
|
787
|
-
for (let i = 0; i < input.length; i++) {
|
|
788
|
-
const char = input[i];
|
|
789
|
-
if (char === "{" || char === "[" || char === "(") depth++;
|
|
790
|
-
if (char === "}" || char === "]" || char === ")") depth--;
|
|
791
|
-
if (char === "," && depth === 0) {
|
|
792
|
-
parts.push(input.slice(start, i));
|
|
793
|
-
start = i + 1;
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
parts.push(input.slice(start));
|
|
797
|
-
return parts;
|
|
798
|
-
}
|
|
799
|
-
function buildUsageBody(source, start, end) {
|
|
800
|
-
const masked = `${source.slice(0, start)}${" ".repeat(end - start)}${source.slice(end)}`;
|
|
801
|
-
return stripCommentsAndStrings(masked);
|
|
802
|
-
}
|
|
803
|
-
function stripCommentsAndStrings(source) {
|
|
804
|
-
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);
|
|
805
|
-
}
|
|
806
|
-
function maskPreservingLines(segment) {
|
|
807
|
-
return segment.replace(/[^\r\n]/g, " ");
|
|
808
|
-
}
|
|
809
|
-
function hasIdentifierUsage(body, identifier) {
|
|
810
|
-
return new RegExp(`\\b${escapeRegex(identifier)}\\b`, "m").test(body);
|
|
811
|
-
}
|
|
812
|
-
function collectNamespaceMembers(body, namespaceName) {
|
|
813
|
-
const members = /* @__PURE__ */ new Set();
|
|
814
|
-
const regex = new RegExp(`\\b${escapeRegex(namespaceName)}\\s*\\.\\s*([A-Za-z_$][\\w$]*)`, "g");
|
|
815
|
-
for (const match of body.matchAll(regex)) {
|
|
816
|
-
const member = match[1];
|
|
817
|
-
if (member) {
|
|
818
|
-
members.add(member);
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
return [...members];
|
|
822
|
-
}
|
|
823
|
-
function resolveImportPath(db, importerPath, specifier) {
|
|
824
|
-
if (isPythonSourcePath(importerPath)) {
|
|
825
|
-
return resolvePythonImportPath(db, importerPath, specifier);
|
|
826
|
-
}
|
|
827
|
-
if (isRustSourcePath(importerPath)) {
|
|
828
|
-
return resolveRustImportPath(db, importerPath, specifier);
|
|
829
|
-
}
|
|
830
|
-
if (isRubySourcePath(importerPath)) {
|
|
831
|
-
return resolveRubyImportPath(db, importerPath, specifier);
|
|
832
|
-
}
|
|
833
|
-
if (isCLikeSourcePath(importerPath)) {
|
|
834
|
-
return resolveCLikeImportPath(db, importerPath, specifier);
|
|
835
|
-
}
|
|
836
|
-
if (isJvmSourcePath(importerPath) || isDotNetSourcePath(importerPath) || isPhpSourcePath(importerPath)) {
|
|
837
|
-
return resolveQualifiedImportPath(db, specifier.replace(/\\/g, "."), extensionFamilyFor(importerPath));
|
|
838
|
-
}
|
|
839
|
-
if (isDartSourcePath(importerPath)) {
|
|
840
|
-
return resolveDartImportPath(db, importerPath, specifier);
|
|
841
|
-
}
|
|
842
|
-
return resolveJavaScriptImportPath(db, importerPath, specifier);
|
|
843
|
-
}
|
|
844
|
-
function resolveJavaScriptImportPath(db, importerPath, specifier) {
|
|
845
|
-
if (!specifier.startsWith(".") && !specifier.startsWith("/")) {
|
|
846
|
-
return null;
|
|
847
|
-
}
|
|
848
|
-
const importerDir = dirname(join(db.config.projectRoot, importerPath));
|
|
849
|
-
const absolute = resolve(importerDir, specifier);
|
|
850
|
-
const indexedPaths = getIndexedPaths(db);
|
|
851
|
-
for (const candidate of candidateImportPaths(absolute)) {
|
|
852
|
-
const relativeCandidate = normalizePath(relative(db.config.projectRoot, candidate));
|
|
853
|
-
if (indexedPaths.has(relativeCandidate) || existsSync(candidate)) {
|
|
854
|
-
return relativeCandidate;
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
return normalizePath(relative(db.config.projectRoot, absolute));
|
|
858
|
-
}
|
|
859
|
-
function resolvePythonImportPath(db, importerPath, specifier) {
|
|
860
|
-
const indexedPaths = getIndexedPaths(db);
|
|
861
|
-
let basePath;
|
|
862
|
-
if (specifier.startsWith(".")) {
|
|
863
|
-
const match = specifier.match(/^(\.+)(.*)$/);
|
|
864
|
-
if (!match) return null;
|
|
865
|
-
const dots = match[1].length;
|
|
866
|
-
const remainder = match[2].replace(/^\./, "");
|
|
867
|
-
let baseDir = dirname(join(db.config.projectRoot, importerPath));
|
|
868
|
-
for (let i = 1; i < dots; i++) {
|
|
869
|
-
baseDir = dirname(baseDir);
|
|
870
|
-
}
|
|
871
|
-
basePath = remainder ? resolve(baseDir, remainder.replace(/\./g, "/")) : baseDir;
|
|
872
|
-
} else {
|
|
873
|
-
basePath = resolve(db.config.projectRoot, specifier.replace(/\./g, "/"));
|
|
874
|
-
}
|
|
875
|
-
for (const candidate of pythonCandidateImportPaths(basePath)) {
|
|
876
|
-
const relativeCandidate = normalizePath(relative(db.config.projectRoot, candidate));
|
|
877
|
-
if (indexedPaths.has(relativeCandidate) || existsSync(candidate)) {
|
|
878
|
-
return relativeCandidate;
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
return null;
|
|
882
|
-
}
|
|
883
|
-
function resolveRustImportPath(db, importerPath, specifier) {
|
|
884
|
-
if (!specifier) return null;
|
|
885
|
-
const normalizedSpecifier = specifier.replace(/\s+as\s+.+$/, "").trim();
|
|
886
|
-
if (!normalizedSpecifier.startsWith("crate::") && !normalizedSpecifier.startsWith("self::") && !normalizedSpecifier.startsWith("super::")) {
|
|
887
|
-
return null;
|
|
888
|
-
}
|
|
889
|
-
const importerDir = dirname(join(db.config.projectRoot, importerPath));
|
|
890
|
-
let basePath;
|
|
891
|
-
if (normalizedSpecifier.startsWith("crate::")) {
|
|
892
|
-
basePath = resolve(db.config.projectRoot, "src", normalizedSpecifier.slice("crate::".length).replace(/::/g, "/"));
|
|
893
|
-
} else if (normalizedSpecifier.startsWith("self::")) {
|
|
894
|
-
basePath = resolve(importerDir, normalizedSpecifier.slice("self::".length).replace(/::/g, "/"));
|
|
895
|
-
} else {
|
|
896
|
-
basePath = resolve(dirname(importerDir), normalizedSpecifier.slice("super::".length).replace(/::/g, "/"));
|
|
897
|
-
}
|
|
898
|
-
for (const candidate of rustCandidateImportPaths(basePath)) {
|
|
899
|
-
const relativeCandidate = normalizePath(relative(db.config.projectRoot, candidate));
|
|
900
|
-
if (getIndexedPaths(db).has(relativeCandidate) || existsSync(candidate)) {
|
|
901
|
-
return relativeCandidate;
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
return null;
|
|
905
|
-
}
|
|
906
|
-
function resolveRubyImportPath(db, importerPath, specifier) {
|
|
907
|
-
const importerDir = dirname(join(db.config.projectRoot, importerPath));
|
|
908
|
-
const absolute = resolve(importerDir, specifier);
|
|
909
|
-
for (const candidate of rubyCandidateImportPaths(absolute)) {
|
|
910
|
-
const relativeCandidate = normalizePath(relative(db.config.projectRoot, candidate));
|
|
911
|
-
if (getIndexedPaths(db).has(relativeCandidate) || existsSync(candidate)) {
|
|
912
|
-
return relativeCandidate;
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
return null;
|
|
916
|
-
}
|
|
917
|
-
function resolveCLikeImportPath(db, importerPath, specifier) {
|
|
918
|
-
const indexedPaths = getIndexedPaths(db);
|
|
919
|
-
const importerDir = dirname(join(db.config.projectRoot, importerPath));
|
|
920
|
-
const candidates = [
|
|
921
|
-
resolve(importerDir, specifier),
|
|
922
|
-
resolve(db.config.projectRoot, specifier),
|
|
923
|
-
resolve(db.config.projectRoot, "include", specifier),
|
|
924
|
-
resolve(db.config.projectRoot, "src", specifier)
|
|
925
|
-
];
|
|
926
|
-
for (const candidate of candidates) {
|
|
927
|
-
const relativeCandidate = normalizePath(relative(db.config.projectRoot, candidate));
|
|
928
|
-
if (indexedPaths.has(relativeCandidate) || existsSync(candidate)) {
|
|
929
|
-
return relativeCandidate;
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
return null;
|
|
933
|
-
}
|
|
934
|
-
function resolveQualifiedImportPath(db, specifier, extensions) {
|
|
935
|
-
const indexedPaths = getIndexedPaths(db);
|
|
936
|
-
const normalized = specifier.replace(/\\/g, ".").replace(/::/g, ".").replace(/^global::/, "");
|
|
937
|
-
const pathified = normalized.replace(/\./g, "/");
|
|
938
|
-
const basenameOnly = normalized.split(".").pop() ?? normalized;
|
|
939
|
-
for (const ext of extensions) {
|
|
940
|
-
const exactSuffix = `${pathified}${ext}`;
|
|
941
|
-
const exact = [...indexedPaths].find((relativePath) => relativePath.endsWith(exactSuffix));
|
|
942
|
-
if (exact) return exact;
|
|
943
|
-
}
|
|
944
|
-
for (const ext of extensions) {
|
|
945
|
-
const basenameMatch = [...indexedPaths].find((relativePath) => basename(relativePath) === `${basenameOnly}${ext}`);
|
|
946
|
-
if (basenameMatch) return basenameMatch;
|
|
947
|
-
}
|
|
948
|
-
const folderMatches = [...indexedPaths].filter((relativePath) => extensions.includes(extname(relativePath).toLowerCase())).filter((relativePath) => relativePath.includes(`/${pathified}/`) || relativePath.includes(`/${basenameOnly}/`)).sort((left, right) => left.localeCompare(right));
|
|
949
|
-
if (folderMatches.length === 1) {
|
|
950
|
-
return folderMatches[0];
|
|
951
|
-
}
|
|
952
|
-
return null;
|
|
953
|
-
}
|
|
954
|
-
function resolveDartImportPath(db, importerPath, specifier) {
|
|
955
|
-
const indexedPaths = getIndexedPaths(db);
|
|
956
|
-
if (specifier.startsWith("package:")) {
|
|
957
|
-
const withoutScheme = specifier.slice("package:".length);
|
|
958
|
-
const slashIndex = withoutScheme.indexOf("/");
|
|
959
|
-
if (slashIndex < 0) return null;
|
|
960
|
-
const packageRelative = withoutScheme.slice(slashIndex + 1);
|
|
961
|
-
const candidate = normalizePath(packageRelative.startsWith("lib/") ? packageRelative : `lib/${packageRelative}`);
|
|
962
|
-
if (indexedPaths.has(candidate)) return candidate;
|
|
963
|
-
return null;
|
|
964
|
-
}
|
|
965
|
-
const importerDir = dirname(join(db.config.projectRoot, importerPath));
|
|
966
|
-
const absolute = resolve(importerDir, specifier);
|
|
967
|
-
for (const candidate of dartCandidateImportPaths(absolute)) {
|
|
968
|
-
const relativeCandidate = normalizePath(relative(db.config.projectRoot, candidate));
|
|
969
|
-
if (indexedPaths.has(relativeCandidate) || existsSync(candidate)) {
|
|
970
|
-
return relativeCandidate;
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
return null;
|
|
974
|
-
}
|
|
975
|
-
function pythonCandidateImportPaths(basePath) {
|
|
976
|
-
const ext = extname(basePath);
|
|
977
|
-
if (PYTHON_SOURCE_EXTENSIONS.includes(ext)) {
|
|
978
|
-
return [basePath];
|
|
979
|
-
}
|
|
980
|
-
return [
|
|
981
|
-
`${basePath}.py`,
|
|
982
|
-
`${basePath}.pyi`,
|
|
983
|
-
join(basePath, "__init__.py"),
|
|
984
|
-
join(basePath, "__init__.pyi")
|
|
985
|
-
];
|
|
986
|
-
}
|
|
987
|
-
function rustCandidateImportPaths(basePath) {
|
|
988
|
-
const ext = extname(basePath);
|
|
989
|
-
if (RUST_SOURCE_EXTENSIONS.includes(ext)) {
|
|
990
|
-
return [basePath];
|
|
991
|
-
}
|
|
992
|
-
return [
|
|
993
|
-
`${basePath}.rs`,
|
|
994
|
-
join(basePath, "mod.rs")
|
|
995
|
-
];
|
|
996
|
-
}
|
|
997
|
-
function rubyCandidateImportPaths(basePath) {
|
|
998
|
-
const ext = extname(basePath);
|
|
999
|
-
if (RUBY_SOURCE_EXTENSIONS.includes(ext)) {
|
|
1000
|
-
return [basePath];
|
|
1001
|
-
}
|
|
1002
|
-
return [
|
|
1003
|
-
`${basePath}.rb`,
|
|
1004
|
-
join(basePath, "index.rb")
|
|
1005
|
-
];
|
|
1006
|
-
}
|
|
1007
|
-
function dartCandidateImportPaths(basePath) {
|
|
1008
|
-
const ext = extname(basePath);
|
|
1009
|
-
if (DART_SOURCE_EXTENSIONS.includes(ext)) {
|
|
1010
|
-
return [basePath];
|
|
1011
|
-
}
|
|
1012
|
-
return [`${basePath}.dart`, basePath];
|
|
1013
|
-
}
|
|
1014
|
-
function candidateImportPaths(absolute) {
|
|
1015
|
-
const ext = extname(absolute);
|
|
1016
|
-
const candidates = /* @__PURE__ */ new Set();
|
|
1017
|
-
if (ext) {
|
|
1018
|
-
candidates.add(absolute);
|
|
1019
|
-
for (const sourceExt of SOURCE_EXTENSIONS) {
|
|
1020
|
-
candidates.add(absolute.slice(0, -ext.length) + sourceExt);
|
|
1021
|
-
}
|
|
1022
|
-
} else {
|
|
1023
|
-
for (const sourceExt of SOURCE_EXTENSIONS) {
|
|
1024
|
-
candidates.add(`${absolute}${sourceExt}`);
|
|
1025
|
-
candidates.add(join(absolute, `index${sourceExt}`));
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
return [...candidates];
|
|
1029
|
-
}
|
|
1030
|
-
function getIndexedPaths(db) {
|
|
1031
|
-
const cached = INDEXED_PATH_CACHE.get(db);
|
|
1032
|
-
if (cached) {
|
|
1033
|
-
return cached;
|
|
1034
|
-
}
|
|
1035
|
-
const paths = new Set(
|
|
1036
|
-
db.all(
|
|
1037
|
-
`SELECT relative_path
|
|
1038
|
-
FROM documents
|
|
1039
|
-
WHERE 1 = 1
|
|
1040
|
-
${db.pathExclusionsFor("documents")}`
|
|
1041
|
-
).map((row) => normalizePath(row.relative_path)).filter((relativePath) => !db.isIgnored(relativePath))
|
|
1042
|
-
);
|
|
1043
|
-
INDEXED_PATH_CACHE.set(db, paths);
|
|
1044
|
-
return paths;
|
|
1045
|
-
}
|
|
1046
|
-
function getCachedMap(cache, db) {
|
|
1047
|
-
let map = cache.get(db);
|
|
1048
|
-
if (!map) {
|
|
1049
|
-
map = /* @__PURE__ */ new Map();
|
|
1050
|
-
cache.set(db, map);
|
|
1051
|
-
}
|
|
1052
|
-
return map;
|
|
1053
|
-
}
|
|
1054
|
-
function normalizePath(path) {
|
|
1055
|
-
return path.replace(/\\/g, "/");
|
|
1056
|
-
}
|
|
1057
|
-
var LANGUAGE_EXTENSION_FAMILIES = [
|
|
1058
|
-
SOURCE_EXTENSIONS,
|
|
1059
|
-
PYTHON_SOURCE_EXTENSIONS,
|
|
1060
|
-
JVM_SOURCE_EXTENSIONS,
|
|
1061
|
-
RUST_SOURCE_EXTENSIONS,
|
|
1062
|
-
RUBY_SOURCE_EXTENSIONS,
|
|
1063
|
-
C_LIKE_SOURCE_EXTENSIONS,
|
|
1064
|
-
DOTNET_SOURCE_EXTENSIONS,
|
|
1065
|
-
DART_SOURCE_EXTENSIONS,
|
|
1066
|
-
PHP_SOURCE_EXTENSIONS
|
|
1067
|
-
];
|
|
1068
|
-
function hasExtensionIn(relativePath, extensions) {
|
|
1069
|
-
return extensions.includes(extname(relativePath).toLowerCase());
|
|
1070
|
-
}
|
|
1071
|
-
function isJavaScriptSourcePath(relativePath) {
|
|
1072
|
-
return hasExtensionIn(relativePath, SOURCE_EXTENSIONS);
|
|
1073
|
-
}
|
|
1074
|
-
function isPythonSourcePath(relativePath) {
|
|
1075
|
-
return hasExtensionIn(relativePath, PYTHON_SOURCE_EXTENSIONS);
|
|
1076
|
-
}
|
|
1077
|
-
function isJvmSourcePath(relativePath) {
|
|
1078
|
-
return hasExtensionIn(relativePath, JVM_SOURCE_EXTENSIONS);
|
|
1079
|
-
}
|
|
1080
|
-
function isRustSourcePath(relativePath) {
|
|
1081
|
-
return hasExtensionIn(relativePath, RUST_SOURCE_EXTENSIONS);
|
|
1082
|
-
}
|
|
1083
|
-
function isRubySourcePath(relativePath) {
|
|
1084
|
-
return hasExtensionIn(relativePath, RUBY_SOURCE_EXTENSIONS);
|
|
1085
|
-
}
|
|
1086
|
-
function isCLikeSourcePath(relativePath) {
|
|
1087
|
-
return hasExtensionIn(relativePath, C_LIKE_SOURCE_EXTENSIONS);
|
|
1088
|
-
}
|
|
1089
|
-
function isDotNetSourcePath(relativePath) {
|
|
1090
|
-
return hasExtensionIn(relativePath, DOTNET_SOURCE_EXTENSIONS);
|
|
1091
|
-
}
|
|
1092
|
-
function isVisualBasicSourcePath(relativePath) {
|
|
1093
|
-
return extname(relativePath).toLowerCase() === ".vb";
|
|
1094
|
-
}
|
|
1095
|
-
function isDartSourcePath(relativePath) {
|
|
1096
|
-
return hasExtensionIn(relativePath, DART_SOURCE_EXTENSIONS);
|
|
1097
|
-
}
|
|
1098
|
-
function isPhpSourcePath(relativePath) {
|
|
1099
|
-
return hasExtensionIn(relativePath, PHP_SOURCE_EXTENSIONS);
|
|
1100
|
-
}
|
|
1101
|
-
function extensionFamilyFor(relativePath) {
|
|
1102
|
-
const ext = extname(relativePath).toLowerCase();
|
|
1103
|
-
for (const family of LANGUAGE_EXTENSION_FAMILIES) {
|
|
1104
|
-
if (family.includes(ext)) return family;
|
|
1105
|
-
}
|
|
1106
|
-
return SOURCE_EXTENSIONS;
|
|
1107
|
-
}
|
|
1108
|
-
function getSourceText(db, relativePath) {
|
|
1109
|
-
const cache = getCachedMap(SOURCE_TEXT_CACHE, db);
|
|
1110
|
-
const normalized = normalizePath(relativePath);
|
|
1111
|
-
const cached = cache.get(normalized);
|
|
1112
|
-
if (typeof cached === "string") {
|
|
1113
|
-
return cached;
|
|
1114
|
-
}
|
|
1115
|
-
const fullPath = join(db.config.projectRoot, normalized);
|
|
1116
|
-
if (!existsSync(fullPath)) {
|
|
1117
|
-
cache.set(normalized, "");
|
|
1118
|
-
return "";
|
|
1119
|
-
}
|
|
1120
|
-
const source = readFileSync(fullPath, "utf-8");
|
|
1121
|
-
cache.set(normalized, source);
|
|
1122
|
-
return source;
|
|
1123
|
-
}
|
|
1124
|
-
function pythonParenBalance(value) {
|
|
1125
|
-
let balance = 0;
|
|
1126
|
-
for (const char of value) {
|
|
1127
|
-
if (char === "(") balance++;
|
|
1128
|
-
if (char === ")") balance--;
|
|
1129
|
-
}
|
|
1130
|
-
return balance;
|
|
1131
|
-
}
|
|
1132
|
-
function escapeRegex(value) {
|
|
1133
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1134
|
-
}
|
|
1135
|
-
|
|
1136
|
-
// src/query-support.ts
|
|
1137
|
-
import { basename as basename2 } from "path";
|
|
1138
|
-
var FILE_DEFINITION_CACHE = /* @__PURE__ */ new WeakMap();
|
|
1139
|
-
var TEST_FILE_PATTERNS = [
|
|
1140
|
-
"%/__tests__/%",
|
|
1141
|
-
"%.test.%",
|
|
1142
|
-
"%.spec.%",
|
|
1143
|
-
"%/test/%",
|
|
1144
|
-
"%/tests/%",
|
|
1145
|
-
"%_test.%",
|
|
1146
|
-
"%_spec.%",
|
|
1147
|
-
"%/test_%.%",
|
|
1148
|
-
"%/spec_%.%"
|
|
1149
|
-
];
|
|
1150
|
-
var TEST_SUPPORT_PATH_PATTERNS = [
|
|
1151
|
-
"%/test-utils/%"
|
|
1152
|
-
];
|
|
1153
|
-
function buildFileDepGraph(db, scope) {
|
|
1154
|
-
const scopeFilter = scope ? `AND d1.relative_path LIKE '%${scope}%'` : "";
|
|
1155
|
-
const edges = db.all(
|
|
1156
|
-
`SELECT DISTINCT
|
|
1157
|
-
d1.relative_path AS from_file,
|
|
1158
|
-
d2.relative_path AS to_file
|
|
1159
|
-
FROM mentions m
|
|
1160
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
1161
|
-
JOIN documents d1 ON c.document_id = d1.id
|
|
1162
|
-
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
1163
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
1164
|
-
JOIN documents d2 ON der.document_id = d2.id
|
|
1165
|
-
WHERE d1.id != d2.id
|
|
1166
|
-
AND m.role != 1
|
|
1167
|
-
${db.pathExclusionsFor("d1", "d2")}
|
|
1168
|
-
${scopeFilter}`
|
|
1169
|
-
);
|
|
1170
|
-
const graph = /* @__PURE__ */ new Map();
|
|
1171
|
-
const indexedFiles = new Set(
|
|
1172
|
-
db.all(
|
|
1173
|
-
`SELECT relative_path
|
|
1174
|
-
FROM documents
|
|
1175
|
-
WHERE 1 = 1
|
|
1176
|
-
${db.pathExclusionsFor("documents")}
|
|
1177
|
-
ORDER BY relative_path`
|
|
1178
|
-
).map((row) => row.relative_path).filter((relativePath) => !db.isIgnored(relativePath))
|
|
1179
|
-
);
|
|
1180
|
-
const addEdge = (fromFile, toFile) => {
|
|
1181
|
-
if (fromFile === toFile) return;
|
|
1182
|
-
if (db.isIgnored(fromFile) || db.isIgnored(toFile)) return;
|
|
1183
|
-
if (!indexedFiles.has(toFile)) return;
|
|
1184
|
-
if (!graph.has(fromFile)) graph.set(fromFile, /* @__PURE__ */ new Set());
|
|
1185
|
-
graph.get(fromFile).add(toFile);
|
|
1186
|
-
};
|
|
1187
|
-
for (const edge of edges) {
|
|
1188
|
-
addEdge(edge.from_file, edge.to_file);
|
|
1189
|
-
}
|
|
1190
|
-
for (const relativePath of indexedFiles) {
|
|
1191
|
-
if (scope && !relativePath.includes(scope)) continue;
|
|
1192
|
-
for (const entry of getSourceImports(db, relativePath)) {
|
|
1193
|
-
if (!entry.sourcePath) continue;
|
|
1194
|
-
addEdge(relativePath, entry.sourcePath);
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
return graph;
|
|
1198
|
-
}
|
|
1199
|
-
function findFirstSymbolMatch(db, symbolPattern) {
|
|
1200
|
-
const exact = findExactSymbolMatch(db, symbolPattern.trim());
|
|
1201
|
-
if (exact) {
|
|
1202
|
-
return exact;
|
|
1203
|
-
}
|
|
1204
|
-
const fileLineMatch = symbolPattern.match(/^(.+):(\d+)-(\d+)$/);
|
|
1205
|
-
if (fileLineMatch) {
|
|
1206
|
-
const [, filePath, startStr, endStr] = fileLineMatch;
|
|
1207
|
-
const userStart0 = Math.max(0, parseInt(startStr, 10) - 1);
|
|
1208
|
-
const userEnd0 = Math.max(userStart0, parseInt(endStr, 10) - 1);
|
|
1209
|
-
let row = db.get(
|
|
1210
|
-
`SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path
|
|
1211
|
-
FROM global_symbols gs
|
|
1212
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
1213
|
-
JOIN documents d ON der.document_id = d.id
|
|
1214
|
-
WHERE d.relative_path LIKE ?
|
|
1215
|
-
AND der.start_line <= ? AND der.end_line >= ?
|
|
1216
|
-
${db.pathExclusionsFor("d")}
|
|
1217
|
-
ORDER BY (der.end_line - der.start_line) ASC
|
|
1218
|
-
LIMIT 1`,
|
|
1219
|
-
`%${filePath}%`,
|
|
1220
|
-
userStart0,
|
|
1221
|
-
userEnd0
|
|
1222
|
-
);
|
|
1223
|
-
if (!row) {
|
|
1224
|
-
row = db.get(
|
|
1225
|
-
`SELECT gs.id, gs.symbol, c.document_id, MIN(c.start_line) AS start_line, MAX(c.end_line) AS end_line, d.relative_path
|
|
1226
|
-
FROM global_symbols gs
|
|
1227
|
-
JOIN mentions m ON m.symbol_id = gs.id
|
|
1228
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
1229
|
-
JOIN documents d ON c.document_id = d.id
|
|
1230
|
-
WHERE m.role = 1
|
|
1231
|
-
AND d.relative_path LIKE ?
|
|
1232
|
-
AND c.start_line <= ? AND c.end_line >= ?
|
|
1233
|
-
${db.pathExclusionsFor("d")}
|
|
1234
|
-
GROUP BY gs.id, gs.symbol, c.document_id, d.relative_path
|
|
1235
|
-
ORDER BY (MAX(c.end_line) - MIN(c.start_line)) ASC
|
|
1236
|
-
LIMIT 1`,
|
|
1237
|
-
`%${filePath}%`,
|
|
1238
|
-
userStart0,
|
|
1239
|
-
userEnd0
|
|
1240
|
-
);
|
|
1241
|
-
}
|
|
1242
|
-
if (row && !db.isIgnored(row.relative_path)) {
|
|
1243
|
-
return hydrateSymbolMatch(db, row);
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
const cleaned = normalizeLookupPattern(symbolPattern);
|
|
1247
|
-
const tokens = lookupTokens(symbolPattern);
|
|
1248
|
-
const candidates = getSymbolLookupCandidates(db, tokens);
|
|
1249
|
-
const direct = findDirectSymbolCandidate(candidates, symbolPattern, cleaned);
|
|
1250
|
-
if (direct && !db.isIgnored(direct.relative_path)) {
|
|
1251
|
-
return hydrateSymbolMatch(db, direct);
|
|
1252
|
-
}
|
|
1253
|
-
let best = null;
|
|
1254
|
-
for (const row of candidates) {
|
|
1255
|
-
if (db.isIgnored(row.relative_path)) continue;
|
|
1256
|
-
const score = scoreSymbolCandidate(row, symbolPattern, cleaned, tokens);
|
|
1257
|
-
if (score <= 0) continue;
|
|
1258
|
-
if (!best || score > best.score) {
|
|
1259
|
-
best = { row, score };
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
if (best) {
|
|
1263
|
-
return hydrateSymbolMatch(db, best.row);
|
|
1264
|
-
}
|
|
1265
|
-
return null;
|
|
1266
|
-
}
|
|
1267
|
-
function findExactSymbolMatch(db, symbol) {
|
|
1268
|
-
const row = db.get(
|
|
1269
|
-
`SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path
|
|
1270
|
-
FROM global_symbols gs
|
|
1271
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
1272
|
-
JOIN documents d ON der.document_id = d.id
|
|
1273
|
-
WHERE gs.symbol = ?
|
|
1274
|
-
${db.pathExclusionsFor("d")}
|
|
1275
|
-
ORDER BY d.relative_path, der.start_line
|
|
1276
|
-
LIMIT 1`,
|
|
1277
|
-
symbol
|
|
1278
|
-
);
|
|
1279
|
-
if (!row || db.isIgnored(row.relative_path)) {
|
|
1280
|
-
return null;
|
|
1281
|
-
}
|
|
1282
|
-
return hydrateSymbolMatch(db, row);
|
|
1283
|
-
}
|
|
1284
|
-
function resolveIndexedFile(db, filePattern) {
|
|
1285
|
-
return resolveDocumentCandidates(db, filePattern, { allowMultiple: false })[0]?.relativePath ?? null;
|
|
1286
|
-
}
|
|
1287
|
-
function resolveIndexedPaths(db, filePattern) {
|
|
1288
|
-
return resolveDocumentCandidates(db, filePattern, { allowMultiple: true }).map((candidate) => candidate.relativePath);
|
|
1289
|
-
}
|
|
1290
|
-
function normalizeLookupPattern(symbolPattern) {
|
|
1291
|
-
return symbolPattern.trim().replace(/\(\)$/, "").replace(/\(.*$/, "");
|
|
1292
|
-
}
|
|
1293
|
-
function lookupTokens(symbolPattern) {
|
|
1294
|
-
const cleaned = normalizeLookupPattern(symbolPattern);
|
|
1295
|
-
const tokens = cleaned.split(/[^A-Za-z0-9_]+/).map((token) => token.trim()).filter((token) => token.length > 0);
|
|
1296
|
-
return tokens.length > 0 ? [...new Set(tokens)] : [cleaned];
|
|
1297
|
-
}
|
|
1298
|
-
function getSymbolLookupCandidates(db, tokens) {
|
|
1299
|
-
const tokenClauses = tokens.map(
|
|
1300
|
-
() => `(gs.symbol LIKE ? OR d.relative_path LIKE ? OR COALESCE(gs.display_name, '') LIKE ?)`
|
|
1301
|
-
);
|
|
1302
|
-
const params = tokens.flatMap((token) => {
|
|
1303
|
-
const like = `%${token}%`;
|
|
1304
|
-
return [like, like, like];
|
|
1305
|
-
});
|
|
1306
|
-
const primary = db.all(
|
|
1307
|
-
`SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path, gs.display_name
|
|
1308
|
-
FROM global_symbols gs
|
|
1309
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
1310
|
-
JOIN documents d ON der.document_id = d.id
|
|
1311
|
-
WHERE ${tokenClauses.join("\n AND ")}
|
|
1312
|
-
${db.pathExclusionsFor("d")}
|
|
1313
|
-
LIMIT 200`,
|
|
1314
|
-
...params
|
|
1315
|
-
);
|
|
1316
|
-
if (primary.length > 0) {
|
|
1317
|
-
return primary;
|
|
1318
|
-
}
|
|
1319
|
-
return db.all(
|
|
1320
|
-
`SELECT
|
|
1321
|
-
gs.id,
|
|
1322
|
-
gs.symbol,
|
|
1323
|
-
c.document_id,
|
|
1324
|
-
MIN(c.start_line) AS start_line,
|
|
1325
|
-
MAX(c.end_line) AS end_line,
|
|
1326
|
-
d.relative_path,
|
|
1327
|
-
gs.display_name
|
|
1328
|
-
FROM global_symbols gs
|
|
1329
|
-
JOIN mentions m ON m.symbol_id = gs.id
|
|
1330
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
1331
|
-
JOIN documents d ON c.document_id = d.id
|
|
1332
|
-
WHERE m.role = 1
|
|
1333
|
-
AND ${tokenClauses.join("\n AND ")}
|
|
1334
|
-
${db.pathExclusionsFor("d")}
|
|
1335
|
-
GROUP BY gs.id, gs.symbol, c.document_id, d.relative_path, gs.display_name
|
|
1336
|
-
LIMIT 200`,
|
|
1337
|
-
...params
|
|
1338
|
-
);
|
|
1339
|
-
}
|
|
1340
|
-
function scoreSymbolCandidate(row, originalPattern, cleanedPattern, tokens) {
|
|
1341
|
-
const originalCase = originalPattern.trim();
|
|
1342
|
-
const cleanedCase = cleanedPattern;
|
|
1343
|
-
const noParensCase = cleanedCase.replace(/\(\)$/, "");
|
|
1344
|
-
const original = originalPattern.toLowerCase();
|
|
1345
|
-
const cleaned = cleanedPattern.toLowerCase();
|
|
1346
|
-
const noParens = cleaned.replace(/\(\)$/, "");
|
|
1347
|
-
const rawCase = row.symbol;
|
|
1348
|
-
const shortCase = shortenSymbol(row.symbol);
|
|
1349
|
-
const leafCase = leafName(row.symbol);
|
|
1350
|
-
const displayCase = row.display_name ?? "";
|
|
1351
|
-
const raw = row.symbol.toLowerCase();
|
|
1352
|
-
const short = shortCase.toLowerCase();
|
|
1353
|
-
const leaf = leafCase.toLowerCase();
|
|
1354
|
-
const display = displayCase.toLowerCase();
|
|
1355
|
-
const path = row.relative_path.toLowerCase();
|
|
1356
|
-
const looksPathLike = /[/:.]/.test(cleanedPattern);
|
|
1357
|
-
let score = 0;
|
|
1358
|
-
if (rawCase === originalCase || rawCase === cleanedCase) score += 1150;
|
|
1359
|
-
if (shortCase === originalCase || shortCase === cleanedCase) score += 1100;
|
|
1360
|
-
if (displayCase === noParensCase) score += 980;
|
|
1361
|
-
if (leafCase === noParensCase) score += 960;
|
|
1362
|
-
if (`${leafCase}()` === originalCase || `${leafCase}()` === cleanedCase) score += 955;
|
|
1363
|
-
if (raw === original || raw === cleaned) score += 1e3;
|
|
1364
|
-
if (short === original || short === cleaned) score += 950;
|
|
1365
|
-
if (path === original || path === cleaned) score += 925;
|
|
1366
|
-
if (path.endsWith(`/${cleaned}`) || path.endsWith(`/${original}`)) score += 875;
|
|
1367
|
-
if (display === noParens) score += 850;
|
|
1368
|
-
if (leaf === noParens) score += 825;
|
|
1369
|
-
if (`${leaf}()` === original || `${leaf}()` === cleaned) score += 820;
|
|
1370
|
-
if (short.endsWith(`:${cleaned}`) || short.endsWith(`:${noParens}`) || short.endsWith(`:${noParens}()`)) score += 800;
|
|
1371
|
-
if (raw.includes(cleaned)) score += 120;
|
|
1372
|
-
if (short.includes(cleaned)) score += 140;
|
|
1373
|
-
if (path.includes(cleaned)) score += 140;
|
|
1374
|
-
if (display.includes(cleaned)) score += 110;
|
|
1375
|
-
if (tokens.every((token) => {
|
|
1376
|
-
const lower = token.toLowerCase();
|
|
1377
|
-
return raw.includes(lower) || short.includes(lower) || path.includes(lower) || display.includes(lower);
|
|
1378
|
-
})) {
|
|
1379
|
-
score += 100 + tokens.length * 15;
|
|
1380
|
-
}
|
|
1381
|
-
if (isFunctionLikeSymbol(row.symbol) && leaf === noParens) {
|
|
1382
|
-
score += 60;
|
|
1383
|
-
}
|
|
1384
|
-
if (!looksPathLike && isModuleLikeSymbol(row.symbol)) {
|
|
1385
|
-
score -= 160;
|
|
1386
|
-
}
|
|
1387
|
-
score -= Math.min(50, Math.max(0, row.end_line - row.start_line));
|
|
1388
|
-
return score;
|
|
1389
|
-
}
|
|
1390
|
-
function getCalleeRowsForSymbol(db, symbol, opts = {}) {
|
|
1391
|
-
const mentions = db.all(
|
|
1392
|
-
`SELECT DISTINCT m.symbol_id, c.id AS chunk_id
|
|
1393
|
-
FROM mentions m
|
|
1394
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
1395
|
-
WHERE c.document_id = ?
|
|
1396
|
-
AND c.start_line >= ?
|
|
1397
|
-
AND c.end_line <= ?
|
|
1398
|
-
AND m.role != 1
|
|
1399
|
-
AND m.symbol_id != ?
|
|
1400
|
-
${opts.limit ? "LIMIT ?" : ""}`,
|
|
1401
|
-
...calleeQueryParams(symbol, opts.limit)
|
|
1402
|
-
);
|
|
1403
|
-
const primary = [];
|
|
1404
|
-
const directSeen = /* @__PURE__ */ new Set();
|
|
1405
|
-
for (const mention of mentions) {
|
|
1406
|
-
const callee = getFullSymbolMatch(db, {
|
|
1407
|
-
symbolId: mention.symbol_id,
|
|
1408
|
-
documentId: symbol.documentId,
|
|
1409
|
-
startLine: symbol.startLine,
|
|
1410
|
-
endLine: symbol.endLine
|
|
1411
|
-
});
|
|
1412
|
-
if (!callee || db.isIgnored(callee.relativePath)) continue;
|
|
1413
|
-
const key = `${callee.symbol}|${callee.relativePath}|${mention.chunk_id}`;
|
|
1414
|
-
if (directSeen.has(key)) continue;
|
|
1415
|
-
directSeen.add(key);
|
|
1416
|
-
primary.push({
|
|
1417
|
-
symbol: callee.symbol,
|
|
1418
|
-
file: callee.relativePath,
|
|
1419
|
-
chunkId: mention.chunk_id
|
|
1420
|
-
});
|
|
1421
|
-
}
|
|
1422
|
-
const sourceFallback = getSourceBackedCalleeRows(db, symbol, opts.limit);
|
|
1423
|
-
if (sourceFallback.length > 0) {
|
|
1424
|
-
return applyLimit(sourceFallback, opts.limit);
|
|
1425
|
-
}
|
|
1426
|
-
return primary;
|
|
1427
|
-
}
|
|
1428
|
-
function getCallerRowsForSymbol(db, symbol, opts = {}) {
|
|
1429
|
-
const match = getFullSymbolMatch(db, symbol);
|
|
1430
|
-
if (!match) {
|
|
1431
|
-
return [];
|
|
1432
|
-
}
|
|
1433
|
-
const primary = db.all(
|
|
1434
|
-
`SELECT DISTINCT ref_d.relative_path AS caller_file, c.start_line AS line
|
|
1435
|
-
FROM mentions m
|
|
1436
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
1437
|
-
JOIN documents ref_d ON c.document_id = ref_d.id
|
|
1438
|
-
WHERE m.symbol_id = ?
|
|
1439
|
-
AND m.role != 1
|
|
1440
|
-
${db.pathExclusionsFor("ref_d")}
|
|
1441
|
-
ORDER BY ref_d.relative_path
|
|
1442
|
-
${opts.limit ? "LIMIT ?" : ""}`,
|
|
1443
|
-
...referenceQueryParams(match, opts.limit)
|
|
1444
|
-
).filter((row) => !db.isIgnored(row.caller_file)).flatMap((row) => {
|
|
1445
|
-
const enclosing = findEnclosingDefinition(getDefinitionsForFile(db, row.caller_file), row.line);
|
|
1446
|
-
if (!enclosing || enclosing.symbolId === match.symbolId) {
|
|
1447
|
-
return [];
|
|
1448
|
-
}
|
|
1449
|
-
return [{
|
|
1450
|
-
symbol: enclosing.symbol,
|
|
1451
|
-
file: row.caller_file
|
|
1452
|
-
}];
|
|
1453
|
-
});
|
|
1454
|
-
const sourceFallback = dedupeCallerRows([
|
|
1455
|
-
...getPythonSourceCallerRows(db, match, opts.limit),
|
|
1456
|
-
...getGenericSourceCallerRows(db, match, opts.limit)
|
|
1457
|
-
]);
|
|
1458
|
-
if (sourceFallback.length === 0) {
|
|
1459
|
-
return dedupeCallerRows(primary);
|
|
1460
|
-
}
|
|
1461
|
-
const merged = [...sourceFallback];
|
|
1462
|
-
const fallbackFiles = new Set(sourceFallback.map((row) => row.file));
|
|
1463
|
-
const seen = new Set(merged.map((row) => `${row.symbol}|${row.file}`));
|
|
1464
|
-
for (const row of primary) {
|
|
1465
|
-
if (fallbackFiles.has(row.file)) continue;
|
|
1466
|
-
const key = `${row.symbol}|${row.file}`;
|
|
1467
|
-
if (seen.has(key)) continue;
|
|
1468
|
-
if (isFunctionLikeSymbol(row.symbol) || merged.length === 0) {
|
|
1469
|
-
seen.add(key);
|
|
1470
|
-
merged.push(row);
|
|
1471
|
-
}
|
|
1472
|
-
}
|
|
1473
|
-
return applyLimit(merged, opts.limit);
|
|
1474
|
-
}
|
|
1475
|
-
function getGenericSourceCallerRows(db, symbol, limit) {
|
|
1476
|
-
return applyLimit(
|
|
1477
|
-
getSourceReferenceSites(db, symbol).filter((site) => site.enclosingSymbol && site.enclosingSymbol !== symbol.symbol).map((site) => ({
|
|
1478
|
-
symbol: site.enclosingSymbol,
|
|
1479
|
-
file: site.file
|
|
1480
|
-
})),
|
|
1481
|
-
limit
|
|
1482
|
-
);
|
|
1483
|
-
}
|
|
1484
|
-
function dedupeCallerRows(rows) {
|
|
1485
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1486
|
-
const unique = [];
|
|
1487
|
-
for (const row of rows) {
|
|
1488
|
-
const key = `${row.symbol}|${row.file}`;
|
|
1489
|
-
if (seen.has(key)) continue;
|
|
1490
|
-
seen.add(key);
|
|
1491
|
-
unique.push(row);
|
|
1492
|
-
}
|
|
1493
|
-
return unique;
|
|
1494
|
-
}
|
|
1495
|
-
function getSourceReferenceSites(db, symbol) {
|
|
1496
|
-
const match = getFullSymbolMatch(db, symbol);
|
|
1497
|
-
if (!match) {
|
|
1498
|
-
return [];
|
|
1499
|
-
}
|
|
1500
|
-
const identifier = leafName(match.symbol);
|
|
1501
|
-
if (!identifier || !hasUniqueLeafDefinition(db, identifier, match.symbolId)) {
|
|
1502
|
-
return [];
|
|
1503
|
-
}
|
|
1504
|
-
const documents = db.all(
|
|
1505
|
-
`SELECT relative_path
|
|
1506
|
-
FROM documents
|
|
1507
|
-
WHERE 1 = 1
|
|
1508
|
-
${db.pathExclusionsFor("documents")}
|
|
1509
|
-
ORDER BY relative_path`
|
|
1510
|
-
);
|
|
1511
|
-
const sites = [];
|
|
1512
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1513
|
-
for (const document of documents) {
|
|
1514
|
-
if (db.isIgnored(document.relative_path)) continue;
|
|
1515
|
-
const lines = findIdentifierLines(db, document.relative_path, identifier, document.relative_path === match.relativePath ? { excludeStartLine: match.startLine, excludeEndLine: match.endLine } : {});
|
|
1516
|
-
const definitions = getDefinitionsForFile(db, document.relative_path);
|
|
1517
|
-
for (const line of lines) {
|
|
1518
|
-
const enclosing = findEnclosingDefinition(definitions, line);
|
|
1519
|
-
const key = `${document.relative_path}|${line}|${enclosing?.symbol ?? ""}`;
|
|
1520
|
-
if (seen.has(key)) continue;
|
|
1521
|
-
seen.add(key);
|
|
1522
|
-
sites.push({
|
|
1523
|
-
file: document.relative_path,
|
|
1524
|
-
line,
|
|
1525
|
-
enclosingSymbol: enclosing?.symbol ?? null
|
|
1526
|
-
});
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
return sites;
|
|
1530
|
-
}
|
|
1531
|
-
function getResolvedReferenceSites(db, symbol) {
|
|
1532
|
-
const match = getFullSymbolMatch(db, symbol);
|
|
1533
|
-
if (!match) {
|
|
1534
|
-
return [];
|
|
1535
|
-
}
|
|
1536
|
-
const rows = db.all(
|
|
1537
|
-
`SELECT DISTINCT d.relative_path, c.start_line, c.end_line
|
|
1538
|
-
FROM mentions m
|
|
1539
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
1540
|
-
JOIN documents d ON c.document_id = d.id
|
|
1541
|
-
WHERE m.symbol_id = ?
|
|
1542
|
-
AND m.role != 1
|
|
1543
|
-
${db.pathExclusionsFor("d")}
|
|
1544
|
-
ORDER BY d.relative_path, c.start_line`,
|
|
1545
|
-
match.symbolId
|
|
1546
|
-
);
|
|
1547
|
-
const chunksByFile = /* @__PURE__ */ new Map();
|
|
1548
|
-
for (const row of rows) {
|
|
1549
|
-
if (db.isIgnored(row.relative_path)) continue;
|
|
1550
|
-
let bucket = chunksByFile.get(row.relative_path);
|
|
1551
|
-
if (!bucket) {
|
|
1552
|
-
bucket = [];
|
|
1553
|
-
chunksByFile.set(row.relative_path, bucket);
|
|
1554
|
-
}
|
|
1555
|
-
bucket.push({ start_line: row.start_line, end_line: row.end_line });
|
|
1556
|
-
}
|
|
1557
|
-
const identifier = leafName(match.symbol);
|
|
1558
|
-
const sites = [];
|
|
1559
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1560
|
-
for (const [file, chunks] of chunksByFile) {
|
|
1561
|
-
const definitions = getDefinitionsForFile(db, file);
|
|
1562
|
-
const excludeOpts = file === match.relativePath ? { excludeStartLine: match.startLine, excludeEndLine: match.endLine } : {};
|
|
1563
|
-
const allHits = identifier ? findIdentifierLines(db, file, identifier, excludeOpts) : [];
|
|
1564
|
-
for (const chunk of chunks) {
|
|
1565
|
-
const hitsInChunk = allHits.filter(
|
|
1566
|
-
(line) => line >= chunk.start_line && line <= chunk.end_line
|
|
1567
|
-
);
|
|
1568
|
-
const lines = hitsInChunk.length > 0 ? hitsInChunk : [chunk.start_line];
|
|
1569
|
-
for (const line of lines) {
|
|
1570
|
-
const enclosing = findEnclosingDefinition(definitions, line);
|
|
1571
|
-
const key = `${file}|${line}|${enclosing?.symbol ?? ""}`;
|
|
1572
|
-
if (seen.has(key)) continue;
|
|
1573
|
-
seen.add(key);
|
|
1574
|
-
sites.push({
|
|
1575
|
-
file,
|
|
1576
|
-
line,
|
|
1577
|
-
enclosingSymbol: enclosing?.symbol ?? null
|
|
1578
|
-
});
|
|
1579
|
-
}
|
|
1580
|
-
}
|
|
1581
|
-
}
|
|
1582
|
-
return sites;
|
|
1583
|
-
}
|
|
1584
|
-
function calleeQueryParams(symbol, limit) {
|
|
1585
|
-
const params = [
|
|
1586
|
-
symbol.documentId,
|
|
1587
|
-
symbol.startLine,
|
|
1588
|
-
symbol.endLine,
|
|
1589
|
-
symbol.symbolId
|
|
1590
|
-
];
|
|
1591
|
-
if (typeof limit === "number") {
|
|
1592
|
-
params.push(limit);
|
|
1593
|
-
}
|
|
1594
|
-
return params;
|
|
1595
|
-
}
|
|
1596
|
-
function referenceQueryParams(symbol, limit) {
|
|
1597
|
-
const params = [symbol.symbolId];
|
|
1598
|
-
if (typeof limit === "number") {
|
|
1599
|
-
params.push(limit);
|
|
1600
|
-
}
|
|
1601
|
-
return params;
|
|
1602
|
-
}
|
|
1603
|
-
function findEnclosingDefinition(definitions, line) {
|
|
1604
|
-
let best = null;
|
|
1605
|
-
for (const definition of definitions) {
|
|
1606
|
-
if (definition.startLine > line || definition.endLine < line) continue;
|
|
1607
|
-
if (!best || definition.endLine - definition.startLine < best.endLine - best.startLine) {
|
|
1608
|
-
best = definition;
|
|
1609
|
-
}
|
|
1610
|
-
}
|
|
1611
|
-
return best;
|
|
1612
|
-
}
|
|
1613
|
-
var LANGUAGE_CALLEE_CONFIGS = [
|
|
1614
|
-
// Python (index 0) — complex resolver
|
|
1615
|
-
{ kind: "complex", languageIndex: 0, resolver: resolvePythonCallTarget },
|
|
1616
|
-
// JavaScript/TypeScript (index 1) — complex resolver
|
|
1617
|
-
{ kind: "complex", languageIndex: 1, resolver: resolveJavaScriptCallTarget },
|
|
1618
|
-
// Java (index 2) — simple with field bindings
|
|
1619
|
-
{ kind: "simple", languageIndex: 2, parseBindings: (_db, source) => parseJavaFieldBindings(source) },
|
|
1620
|
-
// Kotlin (index 3) — simple with field bindings
|
|
1621
|
-
{ kind: "simple", languageIndex: 3, parseBindings: (_db, source) => parseKotlinFieldBindings(source) },
|
|
1622
|
-
// Scala (index 4) — simple, no bindings
|
|
1623
|
-
{ kind: "simple", languageIndex: 4, parseBindings: null },
|
|
1624
|
-
// C# (index 5) — simple, no bindings
|
|
1625
|
-
{ kind: "simple", languageIndex: 5, parseBindings: null },
|
|
1626
|
-
// Visual Basic (index 6) — simple, no bindings
|
|
1627
|
-
{ kind: "simple", languageIndex: 6, parseBindings: null },
|
|
1628
|
-
// C++ (index 7) — simple with receiver bindings
|
|
1629
|
-
{ kind: "simple", languageIndex: 7, parseBindings: (_db, source) => parseCppReceiverBindings(source) },
|
|
1630
|
-
// Rust (index 8) — simple, no bindings
|
|
1631
|
-
{ kind: "simple", languageIndex: 8, parseBindings: null },
|
|
1632
|
-
// Ruby (index 9) — simple with dual-attempt logic
|
|
1633
|
-
{
|
|
1634
|
-
kind: "simple",
|
|
1635
|
-
languageIndex: 9,
|
|
1636
|
-
parseBindings: (db, source) => parseRubyReceiverBindings(db, source),
|
|
1637
|
-
dualAttempt: {
|
|
1638
|
-
baseOpts: { allowInstanceVariables: true },
|
|
1639
|
-
extendedOpts: { allowInstanceVariables: true, allowBareMemberCalls: true }
|
|
1640
|
-
}
|
|
1641
|
-
},
|
|
1642
|
-
// Dart (index 10) — simple, no bindings
|
|
1643
|
-
{ kind: "simple", languageIndex: 10, parseBindings: null },
|
|
1644
|
-
// PHP (index 11) — simple, no bindings
|
|
1645
|
-
{ kind: "simple", languageIndex: 11, parseBindings: null }
|
|
1646
|
-
];
|
|
1647
|
-
function getComplexSourceCalleeRows(db, match, config, limit) {
|
|
1648
|
-
const definitions = getDefinitionsForFile(db, match.relativePath);
|
|
1649
|
-
const current = definitions.find((definition) => definition.symbolId === match.symbolId);
|
|
1650
|
-
if (!current) {
|
|
1651
|
-
return [];
|
|
1652
|
-
}
|
|
1653
|
-
const imports = getSourceImports(db, match.relativePath);
|
|
1654
|
-
const bindings = new Map(
|
|
1655
|
-
getSourceConstructorBindings(db, match.relativePath, {
|
|
1656
|
-
startLine: match.startLine,
|
|
1657
|
-
endLine: match.endLine
|
|
1658
|
-
}).map((binding) => [binding.localName, binding.typeName])
|
|
1659
|
-
);
|
|
1660
|
-
const rows = [];
|
|
1661
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1662
|
-
for (const call of getSourceCalls(db, match.relativePath, {
|
|
1663
|
-
startLine: match.startLine,
|
|
1664
|
-
endLine: match.endLine
|
|
1665
|
-
})) {
|
|
1666
|
-
const resolved = config.resolver(
|
|
1667
|
-
db,
|
|
1668
|
-
current,
|
|
1669
|
-
definitions,
|
|
1670
|
-
imports,
|
|
1671
|
-
bindings,
|
|
1672
|
-
call.receiverName,
|
|
1673
|
-
call.calleeName
|
|
1674
|
-
);
|
|
1675
|
-
if (!resolved || resolved.symbolId === match.symbolId || db.isIgnored(resolved.relativePath)) continue;
|
|
1676
|
-
const chunkId = 1e9 + call.line;
|
|
1677
|
-
const key = `${resolved.symbol}|${resolved.relativePath}|${chunkId}`;
|
|
1678
|
-
if (seen.has(key)) continue;
|
|
1679
|
-
seen.add(key);
|
|
1680
|
-
rows.push({
|
|
1681
|
-
symbol: resolved.symbol,
|
|
1682
|
-
file: resolved.relativePath,
|
|
1683
|
-
chunkId
|
|
1684
|
-
});
|
|
1685
|
-
}
|
|
1686
|
-
return applyLimit(rows, limit);
|
|
1687
|
-
}
|
|
1688
|
-
function getSimpleLanguageCalleeRows(db, match, config, limit) {
|
|
1689
|
-
let calls;
|
|
1690
|
-
if (config.dualAttempt) {
|
|
1691
|
-
const baseCalls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine, config.dualAttempt.baseOpts);
|
|
1692
|
-
const extendedCalls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine, config.dualAttempt.extendedOpts);
|
|
1693
|
-
calls = extendedCalls.length > 0 ? extendedCalls : baseCalls;
|
|
1694
|
-
} else {
|
|
1695
|
-
calls = getSimpleSourceCalls(db, match.relativePath, match.startLine, match.endLine, config.sourceCallOpts);
|
|
1696
|
-
}
|
|
1697
|
-
const bindings = config.parseBindings ? config.parseBindings(db, getSourceText(db, match.relativePath)) : /* @__PURE__ */ new Map();
|
|
1698
|
-
return resolveSimpleSourceCallees(db, match, calls, bindings, limit);
|
|
1699
|
-
}
|
|
1700
|
-
function getSourceBackedCalleeRows(db, symbol, limit) {
|
|
1701
|
-
const match = getFullSymbolMatch(db, symbol);
|
|
1702
|
-
if (!match) {
|
|
1703
|
-
return [];
|
|
1704
|
-
}
|
|
1705
|
-
for (const config of LANGUAGE_CALLEE_CONFIGS) {
|
|
1706
|
-
if (!isDocumentLanguage(db, match.relativePath, DOCUMENT_LANGUAGE_TABLE[config.languageIndex])) {
|
|
1707
|
-
continue;
|
|
1708
|
-
}
|
|
1709
|
-
if (config.kind === "complex") {
|
|
1710
|
-
return getComplexSourceCalleeRows(db, match, config, limit);
|
|
1711
|
-
}
|
|
1712
|
-
return getSimpleLanguageCalleeRows(db, match, config, limit);
|
|
1713
|
-
}
|
|
1714
|
-
return [];
|
|
1715
|
-
}
|
|
1716
|
-
function getPythonSourceCallerRows(db, target, limit) {
|
|
1717
|
-
if (!isPythonDocument(db, target.relativePath)) {
|
|
1718
|
-
return [];
|
|
1719
|
-
}
|
|
1720
|
-
const rows = [];
|
|
1721
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1722
|
-
const pythonConfig = LANGUAGE_CALLEE_CONFIGS[0];
|
|
1723
|
-
for (const candidate of getAllFunctionLikeDefinitions(db)) {
|
|
1724
|
-
if (candidate.symbolId === target.symbolId) continue;
|
|
1725
|
-
if (!isDocumentLanguage(db, candidate.relativePath, DOCUMENT_LANGUAGE_TABLE[pythonConfig.languageIndex])) continue;
|
|
1726
|
-
const callees = getComplexSourceCalleeRows(db, candidate, pythonConfig);
|
|
1727
|
-
if (!callees.some((callee) => callee.symbol === target.symbol)) continue;
|
|
1728
|
-
const key = `${candidate.symbol}|${candidate.relativePath}`;
|
|
1729
|
-
if (seen.has(key) || db.isIgnored(candidate.relativePath)) continue;
|
|
1730
|
-
seen.add(key);
|
|
1731
|
-
rows.push({
|
|
1732
|
-
symbol: candidate.symbol,
|
|
1733
|
-
file: candidate.relativePath
|
|
1734
|
-
});
|
|
1735
|
-
if (typeof limit === "number" && rows.length >= limit) {
|
|
1736
|
-
break;
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
return rows;
|
|
1740
|
-
}
|
|
1741
|
-
function getDefinitionRowsForSymbolId(db, symbolId) {
|
|
1742
|
-
const primary = db.all(
|
|
1743
|
-
`SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path, gs.display_name
|
|
1744
|
-
FROM global_symbols gs
|
|
1745
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
1746
|
-
JOIN documents d ON der.document_id = d.id
|
|
1747
|
-
WHERE gs.id = ?
|
|
1748
|
-
ORDER BY der.start_line, der.end_line`,
|
|
1749
|
-
symbolId
|
|
1750
|
-
);
|
|
1751
|
-
if (primary.length > 0) {
|
|
1752
|
-
return primary;
|
|
1753
|
-
}
|
|
1754
|
-
return db.all(
|
|
1755
|
-
`SELECT
|
|
1756
|
-
gs.id,
|
|
1757
|
-
gs.symbol,
|
|
1758
|
-
c.document_id,
|
|
1759
|
-
MIN(c.start_line) AS start_line,
|
|
1760
|
-
MAX(c.end_line) AS end_line,
|
|
1761
|
-
d.relative_path,
|
|
1762
|
-
gs.display_name
|
|
1763
|
-
FROM global_symbols gs
|
|
1764
|
-
JOIN mentions m ON m.symbol_id = gs.id
|
|
1765
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
1766
|
-
JOIN documents d ON c.document_id = d.id
|
|
1767
|
-
WHERE gs.id = ?
|
|
1768
|
-
AND m.role = 1
|
|
1769
|
-
${db.pathExclusionsFor("d")}
|
|
1770
|
-
GROUP BY gs.id, gs.symbol, c.document_id, d.relative_path, gs.display_name
|
|
1771
|
-
ORDER BY start_line, end_line`,
|
|
1772
|
-
symbolId
|
|
1773
|
-
);
|
|
1774
|
-
}
|
|
1775
|
-
function getFullSymbolMatch(db, symbol) {
|
|
1776
|
-
if ("symbol" in symbol && "relativePath" in symbol) {
|
|
1777
|
-
return symbol;
|
|
1778
|
-
}
|
|
1779
|
-
const row = getDefinitionRowsForSymbolId(db, symbol.symbolId)[0];
|
|
1780
|
-
if (!row) {
|
|
1781
|
-
return null;
|
|
1782
|
-
}
|
|
1783
|
-
return hydrateSymbolMatch(db, row);
|
|
1784
|
-
}
|
|
1785
|
-
function getDefinitionsForFile(db, relativePath) {
|
|
1786
|
-
let cache = FILE_DEFINITION_CACHE.get(db);
|
|
1787
|
-
if (!cache) {
|
|
1788
|
-
cache = /* @__PURE__ */ new Map();
|
|
1789
|
-
FILE_DEFINITION_CACHE.set(db, cache);
|
|
1790
|
-
}
|
|
1791
|
-
const cached = cache.get(relativePath);
|
|
1792
|
-
if (cached) {
|
|
1793
|
-
return cached;
|
|
1794
|
-
}
|
|
1795
|
-
const primary = db.all(
|
|
1796
|
-
`SELECT
|
|
1797
|
-
gs.id,
|
|
1798
|
-
gs.symbol,
|
|
1799
|
-
der.document_id,
|
|
1800
|
-
der.start_line,
|
|
1801
|
-
der.end_line,
|
|
1802
|
-
d.relative_path,
|
|
1803
|
-
gs.display_name,
|
|
1804
|
-
gs.kind,
|
|
1805
|
-
gs.documentation,
|
|
1806
|
-
gs.enclosing_symbol
|
|
1807
|
-
FROM global_symbols gs
|
|
1808
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
1809
|
-
JOIN documents d ON der.document_id = d.id
|
|
1810
|
-
WHERE d.relative_path = ?
|
|
1811
|
-
${db.symbolNoiseFor("gs")}
|
|
1812
|
-
ORDER BY der.start_line, der.end_line`,
|
|
1813
|
-
relativePath
|
|
1814
|
-
);
|
|
1815
|
-
const fallback = primary.length > 0 ? [] : db.all(
|
|
1816
|
-
`SELECT
|
|
1817
|
-
gs.id,
|
|
1818
|
-
gs.symbol,
|
|
1819
|
-
c.document_id,
|
|
1820
|
-
MIN(c.start_line) AS start_line,
|
|
1821
|
-
MAX(c.end_line) AS end_line,
|
|
1822
|
-
d.relative_path,
|
|
1823
|
-
gs.display_name,
|
|
1824
|
-
gs.kind,
|
|
1825
|
-
gs.documentation,
|
|
1826
|
-
gs.enclosing_symbol
|
|
1827
|
-
FROM global_symbols gs
|
|
1828
|
-
JOIN mentions m ON m.symbol_id = gs.id
|
|
1829
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
1830
|
-
JOIN documents d ON c.document_id = d.id
|
|
1831
|
-
WHERE d.relative_path = ?
|
|
1832
|
-
AND m.role = 1
|
|
1833
|
-
${db.symbolNoiseFor("gs")}
|
|
1834
|
-
GROUP BY gs.id, gs.symbol, c.document_id, d.relative_path
|
|
1835
|
-
ORDER BY start_line, end_line`,
|
|
1836
|
-
relativePath
|
|
1837
|
-
);
|
|
1838
|
-
const definitions = correctDefinitionRangesFromSource(
|
|
1839
|
-
db,
|
|
1840
|
-
relativePath,
|
|
1841
|
-
(primary.length > 0 ? primary : fallback).map((row) => ({
|
|
1842
|
-
symbolId: row.id,
|
|
1843
|
-
symbol: row.symbol,
|
|
1844
|
-
documentId: row.document_id,
|
|
1845
|
-
startLine: row.start_line,
|
|
1846
|
-
endLine: row.end_line,
|
|
1847
|
-
relativePath: row.relative_path,
|
|
1848
|
-
leaf: leafName(row.symbol),
|
|
1849
|
-
parentTypeName: parentTypeName(row.symbol),
|
|
1850
|
-
isFunctionLike: isFunctionLikeSymbol(row.symbol),
|
|
1851
|
-
isTypeLike: leafSuffix(row.symbol) === "type",
|
|
1852
|
-
kind: row.kind ?? null,
|
|
1853
|
-
documentation: row.documentation ?? null,
|
|
1854
|
-
enclosingSymbol: row.enclosing_symbol ?? null
|
|
1855
|
-
}))
|
|
1856
|
-
);
|
|
1857
|
-
cache.set(relativePath, definitions);
|
|
1858
|
-
return definitions;
|
|
1859
|
-
}
|
|
1860
|
-
function hydrateSymbolMatch(db, row) {
|
|
1861
|
-
const corrected = getDefinitionsForFile(db, row.relative_path).find((definition) => definition.symbolId === row.id);
|
|
1862
|
-
if (corrected) {
|
|
1863
|
-
return {
|
|
1864
|
-
symbolId: corrected.symbolId,
|
|
1865
|
-
symbol: corrected.symbol,
|
|
1866
|
-
documentId: corrected.documentId,
|
|
1867
|
-
startLine: corrected.startLine,
|
|
1868
|
-
endLine: corrected.endLine,
|
|
1869
|
-
relativePath: corrected.relativePath
|
|
1870
|
-
};
|
|
1871
|
-
}
|
|
1872
|
-
return {
|
|
1873
|
-
symbolId: row.id,
|
|
1874
|
-
symbol: row.symbol,
|
|
1875
|
-
documentId: row.document_id,
|
|
1876
|
-
startLine: row.start_line,
|
|
1877
|
-
endLine: row.end_line,
|
|
1878
|
-
relativePath: row.relative_path
|
|
1879
|
-
};
|
|
1880
|
-
}
|
|
1881
|
-
function findDirectSymbolCandidate(candidates, symbolPattern, cleanedPattern) {
|
|
1882
|
-
const trimmed = symbolPattern.trim();
|
|
1883
|
-
const directMatches = candidates.filter((row) => {
|
|
1884
|
-
const short = shortenSymbol(row.symbol);
|
|
1885
|
-
const display = (row.display_name ?? "").trim();
|
|
1886
|
-
return row.symbol === trimmed || short === trimmed || short === cleanedPattern || display === trimmed || display === cleanedPattern || `${display}()` === trimmed || row.relative_path === trimmed;
|
|
1887
|
-
});
|
|
1888
|
-
if (directMatches.length === 0) {
|
|
1889
|
-
return null;
|
|
1890
|
-
}
|
|
1891
|
-
directMatches.sort(
|
|
1892
|
-
(left, right) => left.end_line - left.start_line - (right.end_line - right.start_line) || left.relative_path.localeCompare(right.relative_path) || left.symbol.localeCompare(right.symbol)
|
|
1893
|
-
);
|
|
1894
|
-
return directMatches[0] ?? null;
|
|
1895
|
-
}
|
|
1896
|
-
function correctDefinitionRangesFromSource(db, relativePath, definitions) {
|
|
1897
|
-
const source = getSourceText(db, relativePath);
|
|
1898
|
-
if (!source) {
|
|
1899
|
-
return definitions;
|
|
1900
|
-
}
|
|
1901
|
-
const lines = source.split(/\r?\n/);
|
|
1902
|
-
const correctedStarts = /* @__PURE__ */ new Map();
|
|
1903
|
-
for (const definition of definitions) {
|
|
1904
|
-
correctedStarts.set(
|
|
1905
|
-
definition.symbolId,
|
|
1906
|
-
resolveCallableDefinitionStartLine(lines, definition)
|
|
1907
|
-
);
|
|
1908
|
-
}
|
|
1909
|
-
const correctedRanges = /* @__PURE__ */ new Map();
|
|
1910
|
-
const callableDefinitions = definitions.filter((definition) => isCallableDefinition(definition.symbol)).map((definition) => ({
|
|
1911
|
-
definition,
|
|
1912
|
-
startLine: correctedStarts.get(definition.symbolId) ?? definition.startLine
|
|
1913
|
-
})).sort(
|
|
1914
|
-
(left, right) => left.startLine - right.startLine || left.definition.startLine - right.definition.startLine || left.definition.symbol.localeCompare(right.definition.symbol)
|
|
1915
|
-
);
|
|
1916
|
-
for (let index = 0; index < callableDefinitions.length; index += 1) {
|
|
1917
|
-
const current = callableDefinitions[index];
|
|
1918
|
-
const next = callableDefinitions[index + 1];
|
|
1919
|
-
const maxEndLine = next ? Math.max(current.startLine, next.startLine - 1) : lines.length - 1;
|
|
1920
|
-
correctedRanges.set(current.definition.symbolId, {
|
|
1921
|
-
startLine: current.startLine,
|
|
1922
|
-
endLine: resolveCallableDefinitionEndLine(
|
|
1923
|
-
lines,
|
|
1924
|
-
current.definition,
|
|
1925
|
-
current.startLine,
|
|
1926
|
-
maxEndLine
|
|
1927
|
-
)
|
|
1928
|
-
});
|
|
1929
|
-
}
|
|
1930
|
-
return definitions.map((definition) => {
|
|
1931
|
-
const corrected = correctedRanges.get(definition.symbolId);
|
|
1932
|
-
if (!corrected) {
|
|
1933
|
-
return definition;
|
|
1934
|
-
}
|
|
1935
|
-
return {
|
|
1936
|
-
...definition,
|
|
1937
|
-
startLine: corrected.startLine,
|
|
1938
|
-
endLine: corrected.endLine
|
|
1939
|
-
};
|
|
1940
|
-
});
|
|
1941
|
-
}
|
|
1942
|
-
function resolveCallableDefinitionStartLine(lines, definition) {
|
|
1943
|
-
if (!isCallableDefinition(definition.symbol)) {
|
|
1944
|
-
return definition.startLine;
|
|
1945
|
-
}
|
|
1946
|
-
const escapedLeaf = escapeRegex2(definition.leaf);
|
|
1947
|
-
const strongPatterns = [
|
|
1948
|
-
new RegExp(`\\b(?:function|def|fn)\\s+${escapedLeaf}\\b`),
|
|
1949
|
-
new RegExp(`\\b${escapedLeaf}\\b\\s*[:=]\\s*(?:async\\s*)?(?:function\\b|\\()`),
|
|
1950
|
-
// Method/function declaration with optional TypeScript generic
|
|
1951
|
-
// parameters: matches `foo(`, `foo<T>(`, `foo<T = Record<string, unknown>>(`.
|
|
1952
|
-
// `[^(]*` with greedy backtracking handles nested generics as long as
|
|
1953
|
-
// the generic block itself doesn't contain a `(`. Anchored to start of
|
|
1954
|
-
// line content (optional leading whitespace + optional access modifiers)
|
|
1955
|
-
// so it prefers the declaration line over call sites.
|
|
1956
|
-
new RegExp(
|
|
1957
|
-
`^\\s*(?:export\\s+|public\\s+|private\\s+|protected\\s+|static\\s+|readonly\\s+|async\\s+|abstract\\s+|get\\s+|set\\s+)*${escapedLeaf}\\s*(?:<[^(]*>)?\\s*\\(`
|
|
1958
|
-
)
|
|
1959
|
-
];
|
|
1960
|
-
const fallbackPatterns = [
|
|
1961
|
-
new RegExp(`\\b${escapedLeaf}\\b\\s*\\(`)
|
|
1962
|
-
];
|
|
1963
|
-
return findNearestMatchingLine(
|
|
1964
|
-
lines,
|
|
1965
|
-
[...strongPatterns, ...fallbackPatterns],
|
|
1966
|
-
definition.startLine,
|
|
1967
|
-
definition.endLine
|
|
1968
|
-
);
|
|
1969
|
-
}
|
|
1970
|
-
function findNearestMatchingLine(lines, patterns, preferredStartLine, preferredEndLine) {
|
|
1971
|
-
const windowStart = Math.max(0, preferredStartLine - 40);
|
|
1972
|
-
const windowEnd = Math.min(lines.length - 1, Math.max(preferredEndLine + 40, preferredStartLine + 5));
|
|
1973
|
-
const windowMatch = matchNearestLine(lines, patterns, preferredStartLine, windowStart, windowEnd);
|
|
1974
|
-
if (windowMatch !== null) {
|
|
1975
|
-
return windowMatch;
|
|
1976
|
-
}
|
|
1977
|
-
const fullMatch = matchNearestLine(lines, patterns, preferredStartLine, 0, lines.length - 1);
|
|
1978
|
-
return fullMatch ?? Math.max(0, Math.min(preferredStartLine, lines.length - 1));
|
|
1979
|
-
}
|
|
1980
|
-
function matchNearestLine(lines, patterns, preferredLine, startLine, endLine) {
|
|
1981
|
-
let best = null;
|
|
1982
|
-
for (let lineIndex = startLine; lineIndex <= endLine; lineIndex += 1) {
|
|
1983
|
-
const line = lines[lineIndex] ?? "";
|
|
1984
|
-
if (!patterns.some((pattern) => pattern.test(line))) continue;
|
|
1985
|
-
const distance = Math.abs(lineIndex - preferredLine);
|
|
1986
|
-
if (!best || distance < best.distance) {
|
|
1987
|
-
best = { line: lineIndex, distance };
|
|
1988
|
-
}
|
|
1989
|
-
}
|
|
1990
|
-
return best?.line ?? null;
|
|
1991
|
-
}
|
|
1992
|
-
function resolveCallableDefinitionEndLine(lines, definition, startLine, maxEndLine) {
|
|
1993
|
-
const boundedEndLine = Math.max(startLine, Math.min(lines.length - 1, maxEndLine));
|
|
1994
|
-
const fallbackEndLine = Math.max(startLine, Math.min(boundedEndLine, definition.endLine));
|
|
1995
|
-
let braceDepth = 0;
|
|
1996
|
-
let parenDepth = 0;
|
|
1997
|
-
let sawOpeningBrace = false;
|
|
1998
|
-
for (let lineIndex = startLine; lineIndex <= boundedEndLine; lineIndex += 1) {
|
|
1999
|
-
const masked = maskStructuralLine(lines[lineIndex] ?? "");
|
|
2000
|
-
for (const char of masked) {
|
|
2001
|
-
if (char === "{") {
|
|
2002
|
-
braceDepth += 1;
|
|
2003
|
-
sawOpeningBrace = true;
|
|
2004
|
-
} else if (char === "}") {
|
|
2005
|
-
braceDepth = Math.max(0, braceDepth - 1);
|
|
2006
|
-
} else if (char === "(") {
|
|
2007
|
-
parenDepth += 1;
|
|
2008
|
-
} else if (char === ")") {
|
|
2009
|
-
parenDepth = Math.max(0, parenDepth - 1);
|
|
2010
|
-
}
|
|
2011
|
-
}
|
|
2012
|
-
if (sawOpeningBrace && braceDepth === 0) {
|
|
2013
|
-
return lineIndex;
|
|
2014
|
-
}
|
|
2015
|
-
if (!sawOpeningBrace && parenDepth === 0 && lineIndex >= fallbackEndLine) {
|
|
2016
|
-
return lineIndex;
|
|
2017
|
-
}
|
|
2018
|
-
}
|
|
2019
|
-
return fallbackEndLine;
|
|
2020
|
-
}
|
|
2021
|
-
function maskStructuralLine(line) {
|
|
2022
|
-
let masked = "";
|
|
2023
|
-
let quote = null;
|
|
2024
|
-
let escaping = false;
|
|
2025
|
-
for (let index = 0; index < line.length; index += 1) {
|
|
2026
|
-
const char = line[index];
|
|
2027
|
-
const next = line[index + 1];
|
|
2028
|
-
if (!quote && char === "/" && next === "/") {
|
|
2029
|
-
masked += " ".repeat(line.length - index);
|
|
2030
|
-
break;
|
|
2031
|
-
}
|
|
2032
|
-
if (quote) {
|
|
2033
|
-
if (escaping) {
|
|
2034
|
-
escaping = false;
|
|
2035
|
-
masked += " ";
|
|
2036
|
-
continue;
|
|
2037
|
-
}
|
|
2038
|
-
if (char === "\\") {
|
|
2039
|
-
escaping = true;
|
|
2040
|
-
masked += " ";
|
|
2041
|
-
continue;
|
|
2042
|
-
}
|
|
2043
|
-
if (char === quote) {
|
|
2044
|
-
quote = null;
|
|
2045
|
-
}
|
|
2046
|
-
masked += " ";
|
|
2047
|
-
continue;
|
|
2048
|
-
}
|
|
2049
|
-
if (char === '"' || char === "'" || char === "`") {
|
|
2050
|
-
quote = char;
|
|
2051
|
-
masked += " ";
|
|
2052
|
-
continue;
|
|
2053
|
-
}
|
|
2054
|
-
masked += char;
|
|
2055
|
-
}
|
|
2056
|
-
return masked;
|
|
2057
|
-
}
|
|
2058
|
-
function isCallableDefinition(symbol) {
|
|
2059
|
-
return symbol.includes("().");
|
|
2060
|
-
}
|
|
2061
|
-
function escapeRegex2(value) {
|
|
2062
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2063
|
-
}
|
|
2064
|
-
function getAllDefinitions(db, opts = {}) {
|
|
2065
|
-
const { scope } = opts;
|
|
2066
|
-
const rows = db.all(
|
|
2067
|
-
`SELECT relative_path
|
|
2068
|
-
FROM documents
|
|
2069
|
-
WHERE 1 = 1
|
|
2070
|
-
${db.pathExclusionsFor("documents")}
|
|
2071
|
-
${scope ? "AND relative_path LIKE ?" : ""}
|
|
2072
|
-
ORDER BY relative_path`,
|
|
2073
|
-
...scope ? [`%${scope}%`] : []
|
|
2074
|
-
);
|
|
2075
|
-
return rows.filter((row) => !db.isIgnored(row.relative_path)).flatMap((row) => getDefinitionsForFile(db, row.relative_path));
|
|
2076
|
-
}
|
|
2077
|
-
function getScopedDefinitions(db, scope) {
|
|
2078
|
-
const scopeFilter = scope ? `AND relative_path LIKE '%${scope}%'` : "";
|
|
2079
|
-
return db.all(
|
|
2080
|
-
`SELECT relative_path
|
|
2081
|
-
FROM documents
|
|
2082
|
-
WHERE 1 = 1
|
|
2083
|
-
${db.pathExclusionsFor("documents")}
|
|
2084
|
-
${scopeFilter}
|
|
2085
|
-
ORDER BY relative_path`
|
|
2086
|
-
).flatMap((row) => getDefinitionsForFile(db, row.relative_path)).filter((row) => !db.isIgnored(row.relativePath));
|
|
2087
|
-
}
|
|
2088
|
-
function getAllFunctionLikeDefinitions(db) {
|
|
2089
|
-
return getAllDefinitions(db).filter((definition) => definition.isFunctionLike);
|
|
2090
|
-
}
|
|
2091
|
-
function resolvePythonCallTarget(db, current, currentFileDefinitions, imports, constructorBindings, receiverName, calleeName) {
|
|
2092
|
-
if (receiverName === "self" || receiverName === "cls") {
|
|
2093
|
-
return findDefinitionByName(currentFileDefinitions, calleeName, current.parentTypeName, ["function"]);
|
|
2094
|
-
}
|
|
2095
|
-
if (receiverName) {
|
|
2096
|
-
const inferredType = constructorBindings.get(receiverName);
|
|
2097
|
-
if (inferredType) {
|
|
2098
|
-
const boundMethod = findDefinitionByName(currentFileDefinitions, calleeName, inferredType, ["function"]);
|
|
2099
|
-
if (boundMethod) {
|
|
2100
|
-
return boundMethod;
|
|
2101
|
-
}
|
|
2102
|
-
for (const entry of imports) {
|
|
2103
|
-
if (entry.localName !== inferredType || !entry.sourcePath) continue;
|
|
2104
|
-
const importedDefinitions = getDefinitionsForFile(db, entry.sourcePath);
|
|
2105
|
-
const importedMethod = findDefinitionByName(importedDefinitions, calleeName, entry.importedName, ["function"]);
|
|
2106
|
-
if (importedMethod) {
|
|
2107
|
-
return importedMethod;
|
|
2108
|
-
}
|
|
2109
|
-
}
|
|
2110
|
-
}
|
|
2111
|
-
const localClassMethod = findDefinitionByName(currentFileDefinitions, calleeName, receiverName, ["function"]);
|
|
2112
|
-
if (localClassMethod) {
|
|
2113
|
-
return localClassMethod;
|
|
2114
|
-
}
|
|
2115
|
-
const namespaceImport = imports.find((entry) => entry.localName === receiverName && entry.sourcePath);
|
|
2116
|
-
if (namespaceImport?.sourcePath) {
|
|
2117
|
-
const importedDefinitions = getDefinitionsForFile(db, namespaceImport.sourcePath);
|
|
2118
|
-
const importedTypeMethod = namespaceImport.kind === "named" ? findDefinitionByName(importedDefinitions, calleeName, namespaceImport.importedName, ["function"]) : null;
|
|
2119
|
-
if (importedTypeMethod) {
|
|
2120
|
-
return importedTypeMethod;
|
|
2121
|
-
}
|
|
2122
|
-
return findDefinitionByName(importedDefinitions, calleeName, null, ["function", "type"]);
|
|
2123
|
-
}
|
|
2124
|
-
return null;
|
|
2125
|
-
}
|
|
2126
|
-
const importedBinding = imports.find((entry) => entry.localName === calleeName && entry.sourcePath);
|
|
2127
|
-
if (importedBinding?.sourcePath) {
|
|
2128
|
-
const importedDefinitions = getDefinitionsForFile(db, importedBinding.sourcePath);
|
|
2129
|
-
const importedName = importedBinding.importedName === "*" ? calleeName : importedBinding.importedName;
|
|
2130
|
-
const importedDefinition = findDefinitionByName(importedDefinitions, importedName, null, ["function", "type"]);
|
|
2131
|
-
if (importedDefinition) {
|
|
2132
|
-
return importedDefinition;
|
|
2133
|
-
}
|
|
2134
|
-
}
|
|
2135
|
-
return findDefinitionByName(currentFileDefinitions, calleeName, null, ["function", "type"]);
|
|
2136
|
-
}
|
|
2137
|
-
function resolveJavaScriptCallTarget(db, current, currentFileDefinitions, imports, constructorBindings, receiverName, calleeName) {
|
|
2138
|
-
if (receiverName === "this") {
|
|
2139
|
-
return findDefinitionByName(currentFileDefinitions, calleeName, current.parentTypeName, ["function"]);
|
|
2140
|
-
}
|
|
2141
|
-
if (receiverName) {
|
|
2142
|
-
const inferredType = constructorBindings.get(receiverName);
|
|
2143
|
-
if (inferredType) {
|
|
2144
|
-
const boundMethod = findDefinitionByName(currentFileDefinitions, calleeName, inferredType, ["function"]);
|
|
2145
|
-
if (boundMethod) {
|
|
2146
|
-
return boundMethod;
|
|
2147
|
-
}
|
|
2148
|
-
for (const entry of imports) {
|
|
2149
|
-
if (entry.localName !== inferredType || !entry.sourcePath) continue;
|
|
2150
|
-
const importedDefinitions = getDefinitionsForFile(db, entry.sourcePath);
|
|
2151
|
-
const importedMethod = findDefinitionByName(importedDefinitions, calleeName, entry.importedName, ["function"]);
|
|
2152
|
-
if (importedMethod) {
|
|
2153
|
-
return importedMethod;
|
|
2154
|
-
}
|
|
2155
|
-
}
|
|
2156
|
-
}
|
|
2157
|
-
const localClassMethod = findDefinitionByName(currentFileDefinitions, calleeName, receiverName, ["function"]);
|
|
2158
|
-
if (localClassMethod) {
|
|
2159
|
-
return localClassMethod;
|
|
2160
|
-
}
|
|
2161
|
-
const namespaceImport = imports.find((entry) => entry.localName === receiverName && entry.sourcePath);
|
|
2162
|
-
if (namespaceImport?.sourcePath) {
|
|
2163
|
-
const importedDefinitions = getDefinitionsForFile(db, namespaceImport.sourcePath);
|
|
2164
|
-
if (namespaceImport.kind === "named" || namespaceImport.kind === "default") {
|
|
2165
|
-
const importedTypeMethod = findDefinitionByName(
|
|
2166
|
-
importedDefinitions,
|
|
2167
|
-
calleeName,
|
|
2168
|
-
namespaceImport.importedName,
|
|
2169
|
-
["function"]
|
|
2170
|
-
);
|
|
2171
|
-
if (importedTypeMethod) {
|
|
2172
|
-
return importedTypeMethod;
|
|
2173
|
-
}
|
|
2174
|
-
}
|
|
2175
|
-
return findDefinitionByName(importedDefinitions, calleeName, null, ["function", "type"]);
|
|
2176
|
-
}
|
|
2177
|
-
return null;
|
|
2178
|
-
}
|
|
2179
|
-
const importedBinding = imports.find((entry) => entry.localName === calleeName && entry.sourcePath && entry.kind !== "namespace");
|
|
2180
|
-
if (importedBinding?.sourcePath) {
|
|
2181
|
-
const importedDefinitions = getDefinitionsForFile(db, importedBinding.sourcePath);
|
|
2182
|
-
const importedName = importedBinding.importedName === "default" ? importedBinding.localName ?? calleeName : importedBinding.importedName;
|
|
2183
|
-
const importedDefinition = findDefinitionByName(importedDefinitions, importedName, null, ["function", "type"]);
|
|
2184
|
-
if (importedDefinition) {
|
|
2185
|
-
return importedDefinition;
|
|
2186
|
-
}
|
|
2187
|
-
}
|
|
2188
|
-
return findDefinitionByName(currentFileDefinitions, calleeName, null, ["function", "type"]);
|
|
2189
|
-
}
|
|
2190
|
-
function resolveSimpleSourceCallees(db, current, calls, bindings, limit) {
|
|
2191
|
-
const currentFileDefinitions = getDefinitionsForFile(db, current.relativePath);
|
|
2192
|
-
const currentDefinition = currentFileDefinitions.find((definition) => definition.symbolId === current.symbolId);
|
|
2193
|
-
if (!currentDefinition) {
|
|
2194
|
-
return [];
|
|
2195
|
-
}
|
|
2196
|
-
const rows = [];
|
|
2197
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2198
|
-
for (const call of calls) {
|
|
2199
|
-
const resolved = resolveSimpleSourceCallTarget(
|
|
2200
|
-
db,
|
|
2201
|
-
currentDefinition,
|
|
2202
|
-
currentFileDefinitions,
|
|
2203
|
-
bindings,
|
|
2204
|
-
call.receiverName,
|
|
2205
|
-
call.calleeName
|
|
2206
|
-
);
|
|
2207
|
-
if (!resolved || resolved.symbolId === current.symbolId || db.isIgnored(resolved.relativePath)) continue;
|
|
2208
|
-
const chunkId = 2e9 + call.line;
|
|
2209
|
-
const key = `${resolved.symbol}|${resolved.relativePath}|${chunkId}`;
|
|
2210
|
-
if (seen.has(key)) continue;
|
|
2211
|
-
seen.add(key);
|
|
2212
|
-
rows.push({
|
|
2213
|
-
symbol: resolved.symbol,
|
|
2214
|
-
file: resolved.relativePath,
|
|
2215
|
-
chunkId
|
|
2216
|
-
});
|
|
2217
|
-
}
|
|
2218
|
-
return applyLimit(rows, limit);
|
|
2219
|
-
}
|
|
2220
|
-
function resolveSimpleSourceCallTarget(db, current, currentFileDefinitions, bindings, receiverName, calleeName) {
|
|
2221
|
-
if (!receiverName) {
|
|
2222
|
-
const localMethod = findDefinitionByName(currentFileDefinitions, calleeName, current.parentTypeName, ["function"]);
|
|
2223
|
-
if (localMethod) {
|
|
2224
|
-
return localMethod;
|
|
2225
|
-
}
|
|
2226
|
-
if (current.parentTypeName) {
|
|
2227
|
-
return findProjectDefinitionByTypeAndLeaf(db, current.parentTypeName, calleeName);
|
|
2228
|
-
}
|
|
2229
|
-
return findDefinitionByName(currentFileDefinitions, calleeName, null, ["function", "type"]);
|
|
2230
|
-
}
|
|
2231
|
-
const normalizedReceiver = normalizeReceiverName(receiverName);
|
|
2232
|
-
const inferredType = bindings.get(normalizedReceiver) ?? inferTypeNameFromReceiver(db, normalizedReceiver);
|
|
2233
|
-
if (!inferredType) {
|
|
2234
|
-
return null;
|
|
2235
|
-
}
|
|
2236
|
-
return findProjectDefinitionByTypeAndLeaf(db, inferredType, calleeName);
|
|
2237
|
-
}
|
|
2238
|
-
function findProjectDefinitionByTypeAndLeaf(db, typeName, calleeName) {
|
|
2239
|
-
const definitions = getAllDefinitions(db).filter((definition) => definition.isFunctionLike || definition.symbol.endsWith("()."));
|
|
2240
|
-
const exact = definitions.find((definition) => definition.leaf === calleeName && (definition.parentTypeName === typeName || definition.symbol.includes(typeName)));
|
|
2241
|
-
if (exact) {
|
|
2242
|
-
return exact;
|
|
2243
|
-
}
|
|
2244
|
-
const normalizedType = normalizeLookupName(typeName);
|
|
2245
|
-
const normalizedMatch = definitions.find((definition) => definition.leaf === calleeName && normalizeLookupName(definition.parentTypeName ?? "").includes(normalizedType));
|
|
2246
|
-
if (normalizedMatch) {
|
|
2247
|
-
return normalizedMatch;
|
|
2248
|
-
}
|
|
2249
|
-
return findLooseProjectDefinitionByTypeAndLeaf(db, typeName, calleeName);
|
|
2250
|
-
}
|
|
2251
|
-
function inferTypeNameFromReceiver(db, receiverName) {
|
|
2252
|
-
const normalizedReceiver = normalizeLookupName(receiverName);
|
|
2253
|
-
if (!normalizedReceiver) {
|
|
2254
|
-
return null;
|
|
2255
|
-
}
|
|
2256
|
-
const candidates = getAllDefinitions(db).filter((definition) => definition.isTypeLike || definition.symbol.endsWith("#")).map((definition) => definition.leaf).filter((leaf) => leaf.length > 0);
|
|
2257
|
-
let best = null;
|
|
2258
|
-
for (const candidate of candidates) {
|
|
2259
|
-
const normalizedCandidate = normalizeLookupName(candidate);
|
|
2260
|
-
let score = 0;
|
|
2261
|
-
if (normalizedCandidate === normalizedReceiver) score += 100;
|
|
2262
|
-
if (normalizedCandidate.endsWith(normalizedReceiver)) score += 80;
|
|
2263
|
-
if (normalizedCandidate.includes(normalizedReceiver)) score += 40;
|
|
2264
|
-
if (normalizedReceiver.includes(normalizedCandidate)) score += 20;
|
|
2265
|
-
if (score > 0 && (!best || score > best.score || score === best.score && candidate.length < best.name.length)) {
|
|
2266
|
-
best = { name: candidate, score };
|
|
2267
|
-
}
|
|
2268
|
-
}
|
|
2269
|
-
return best?.name ?? null;
|
|
2270
|
-
}
|
|
2271
|
-
function getSimpleSourceCalls(db, relativePath, startLine, endLine, opts = {}) {
|
|
2272
|
-
const source = getSourceText(db, relativePath);
|
|
2273
|
-
if (!source) {
|
|
2274
|
-
return [];
|
|
2275
|
-
}
|
|
2276
|
-
const lines = source.split("\n");
|
|
2277
|
-
const scoped = lines.slice(startLine, endLine + 1);
|
|
2278
|
-
const calls = [];
|
|
2279
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2280
|
-
const pattern = opts.allowInstanceVariables ? /(@?[A-Za-z_][\w]*)\s*(?:\.|::)\s*([A-Za-z_][\w!?=]*)\s*\(/g : /\b([A-Za-z_][\w]*)\s*(?:\.|::)\s*([A-Za-z_][\w]*)\s*\(/g;
|
|
2281
|
-
for (let index = 0; index < scoped.length; index++) {
|
|
2282
|
-
const rawLine = scoped[index] ?? "";
|
|
2283
|
-
for (const match of rawLine.matchAll(pattern)) {
|
|
2284
|
-
const receiverName = match[1];
|
|
2285
|
-
const calleeName = match[2];
|
|
2286
|
-
if (!receiverName || !calleeName) continue;
|
|
2287
|
-
const key = `${startLine + index}|${receiverName}|${calleeName}`;
|
|
2288
|
-
if (seen.has(key)) continue;
|
|
2289
|
-
seen.add(key);
|
|
2290
|
-
calls.push({
|
|
2291
|
-
receiverName,
|
|
2292
|
-
calleeName,
|
|
2293
|
-
line: startLine + index
|
|
2294
|
-
});
|
|
2295
|
-
}
|
|
2296
|
-
if (opts.allowBareMemberCalls) {
|
|
2297
|
-
for (const match of rawLine.matchAll(/(@?[A-Za-z_][\w]*)\s*\.\s*([A-Za-z_][\w!?=]*)\b(?!\s*[:=])/g)) {
|
|
2298
|
-
const receiverName = match[1];
|
|
2299
|
-
const calleeName = match[2];
|
|
2300
|
-
if (!receiverName || !calleeName) continue;
|
|
2301
|
-
const key = `${startLine + index}|${receiverName}|${calleeName}`;
|
|
2302
|
-
if (seen.has(key)) continue;
|
|
2303
|
-
seen.add(key);
|
|
2304
|
-
calls.push({
|
|
2305
|
-
receiverName,
|
|
2306
|
-
calleeName,
|
|
2307
|
-
line: startLine + index
|
|
2308
|
-
});
|
|
2309
|
-
}
|
|
2310
|
-
}
|
|
2311
|
-
for (const match of rawLine.matchAll(/\b([A-Za-z_][\w]*)\s*\(/g)) {
|
|
2312
|
-
const calleeName = match[1];
|
|
2313
|
-
const start = match.index ?? -1;
|
|
2314
|
-
if (!calleeName || start < 0) continue;
|
|
2315
|
-
const prefix = rawLine.slice(0, start).trimEnd();
|
|
2316
|
-
if (prefix.endsWith("def") || prefix.endsWith("fun") || prefix.endsWith("fn") || /\b(?:class|interface|trait|module|object)\s*$/.test(prefix)) {
|
|
2317
|
-
continue;
|
|
2318
|
-
}
|
|
2319
|
-
if (rawLine.slice(Math.max(0, start - 3), start).includes(".")) {
|
|
2320
|
-
continue;
|
|
2321
|
-
}
|
|
2322
|
-
calls.push({
|
|
2323
|
-
receiverName: null,
|
|
2324
|
-
calleeName,
|
|
2325
|
-
line: startLine + index
|
|
2326
|
-
});
|
|
2327
|
-
}
|
|
2328
|
-
}
|
|
2329
|
-
return calls;
|
|
2330
|
-
}
|
|
2331
|
-
function parseJavaFieldBindings(source) {
|
|
2332
|
-
const bindings = /* @__PURE__ */ new Map();
|
|
2333
|
-
for (const match of source.matchAll(/\b(?:private|protected|public)\s+(?:final\s+)?([A-Z][A-Za-z0-9_$<>?, ]*)\s+([A-Za-z_][\w$]*)\s*;/g)) {
|
|
2334
|
-
const typeName = stripGenericType(match[1]);
|
|
2335
|
-
const localName = match[2];
|
|
2336
|
-
if (typeName && localName) {
|
|
2337
|
-
bindings.set(localName, typeName);
|
|
2338
|
-
}
|
|
2339
|
-
}
|
|
2340
|
-
return bindings;
|
|
2341
|
-
}
|
|
2342
|
-
function parseKotlinFieldBindings(source) {
|
|
2343
|
-
const bindings = /* @__PURE__ */ new Map();
|
|
2344
|
-
for (const match of source.matchAll(/\b(?:private|protected|public)?\s*(?:val|var)\s+([A-Za-z_][\w]*)\s*:\s*([A-Z][A-Za-z0-9_.<>?]*)/g)) {
|
|
2345
|
-
const localName = match[1];
|
|
2346
|
-
const typeName = stripGenericType(match[2]);
|
|
2347
|
-
if (typeName && localName) {
|
|
2348
|
-
bindings.set(localName, typeName);
|
|
2349
|
-
}
|
|
2350
|
-
}
|
|
2351
|
-
return bindings;
|
|
2352
|
-
}
|
|
2353
|
-
function parseCppReceiverBindings(source) {
|
|
2354
|
-
const bindings = /* @__PURE__ */ new Map();
|
|
2355
|
-
const constructorMatch = source.match(/RunCoordinator::RunCoordinator\s*\(([\s\S]*?)\)\s*(?::\s*([\s\S]*?))?\s*\{/);
|
|
2356
|
-
if (!constructorMatch) {
|
|
2357
|
-
return bindings;
|
|
2358
|
-
}
|
|
2359
|
-
const params = constructorMatch[1] ?? "";
|
|
2360
|
-
const initializers = constructorMatch[2] ?? "";
|
|
2361
|
-
const parameterTypes = /* @__PURE__ */ new Map();
|
|
2362
|
-
for (const param of params.split(",")) {
|
|
2363
|
-
const trimmed = param.trim();
|
|
2364
|
-
const match = trimmed.match(/(.+?)\s+([A-Za-z_][\w]*)$/);
|
|
2365
|
-
if (!match) continue;
|
|
2366
|
-
const [, rawType, rawName] = match;
|
|
2367
|
-
if (!rawType || !rawName) continue;
|
|
2368
|
-
const typeName = stripGenericType(rawType.replace(/[&*]+/g, " ").replace(/\bconst\b/g, " ").trim());
|
|
2369
|
-
if (!typeName) continue;
|
|
2370
|
-
parameterTypes.set(normalizeReceiverName(rawName), typeName);
|
|
2371
|
-
}
|
|
2372
|
-
for (const initializer of initializers.split(",")) {
|
|
2373
|
-
const trimmed = initializer.trim();
|
|
2374
|
-
const match = trimmed.match(/([A-Za-z_][\w]*)\s*\(\s*([A-Za-z_][\w]*)\s*\)$/);
|
|
2375
|
-
if (!match) continue;
|
|
2376
|
-
const [, fieldName, sourceName] = match;
|
|
2377
|
-
if (!fieldName || !sourceName) continue;
|
|
2378
|
-
const typeName = parameterTypes.get(normalizeReceiverName(sourceName));
|
|
2379
|
-
if (!typeName) continue;
|
|
2380
|
-
bindings.set(normalizeReceiverName(fieldName), typeName);
|
|
2381
|
-
}
|
|
2382
|
-
return bindings;
|
|
2383
|
-
}
|
|
2384
|
-
function parseRubyReceiverBindings(db, source) {
|
|
2385
|
-
const bindings = /* @__PURE__ */ new Map();
|
|
2386
|
-
for (const match of source.matchAll(/@([A-Za-z_][\w]*)\s*=\s*([A-Za-z_][\w]*)/g)) {
|
|
2387
|
-
const ivarName = `@${match[1]}`;
|
|
2388
|
-
const localName = match[2];
|
|
2389
|
-
const typeName = inferTypeNameFromReceiver(db, localName ?? match[1] ?? "");
|
|
2390
|
-
if (typeName) {
|
|
2391
|
-
bindings.set(ivarName, typeName);
|
|
2392
|
-
bindings.set(match[1], typeName);
|
|
2393
|
-
}
|
|
2394
|
-
}
|
|
2395
|
-
return bindings;
|
|
2396
|
-
}
|
|
2397
|
-
function stripGenericType(typeName) {
|
|
2398
|
-
return (typeName ?? "").replace(/<.*$/, "").replace(/^.*\./, "").trim();
|
|
2399
|
-
}
|
|
2400
|
-
function normalizeReceiverName(receiverName) {
|
|
2401
|
-
return receiverName.replace(/^@/, "").replace(/^this(?:\.|->)/, "").replace(/^_+/, "").replace(/_+$/, "").trim();
|
|
2402
|
-
}
|
|
2403
|
-
function normalizeLookupName(value) {
|
|
2404
|
-
return value.replace(/[^A-Za-z0-9]+/g, "").toLowerCase();
|
|
2405
|
-
}
|
|
2406
|
-
function isLikelyTestPath(relativePath) {
|
|
2407
|
-
return /(^|\/)(tests?|__tests__)\//.test(relativePath) || /\.(?:spec|test)\./.test(relativePath) || /_test\./.test(relativePath);
|
|
2408
|
-
}
|
|
2409
|
-
function findDefinitionByName(definitions, leaf, parentType, preference) {
|
|
2410
|
-
const candidates = definitions.filter((definition) => definition.leaf === leaf && definition.parentTypeName === parentType);
|
|
2411
|
-
if (candidates.length === 0) {
|
|
2412
|
-
return null;
|
|
2413
|
-
}
|
|
2414
|
-
for (const preferred of preference) {
|
|
2415
|
-
const match = candidates.find((candidate) => preferred === "function" ? candidate.isFunctionLike : candidate.isTypeLike);
|
|
2416
|
-
if (match) {
|
|
2417
|
-
return match;
|
|
2418
|
-
}
|
|
2419
|
-
}
|
|
2420
|
-
return candidates[0] ?? null;
|
|
2421
|
-
}
|
|
2422
|
-
function findLooseProjectDefinitionByTypeAndLeaf(db, typeName, calleeName) {
|
|
2423
|
-
const rows = db.all(
|
|
2424
|
-
`SELECT
|
|
2425
|
-
gs.id,
|
|
2426
|
-
gs.symbol,
|
|
2427
|
-
c.document_id,
|
|
2428
|
-
MIN(c.start_line) AS start_line,
|
|
2429
|
-
MAX(c.end_line) AS end_line,
|
|
2430
|
-
d.relative_path,
|
|
2431
|
-
gs.kind,
|
|
2432
|
-
gs.documentation,
|
|
2433
|
-
gs.enclosing_symbol
|
|
2434
|
-
FROM global_symbols gs
|
|
2435
|
-
JOIN mentions m ON m.symbol_id = gs.id
|
|
2436
|
-
JOIN chunks c ON c.id = m.chunk_id
|
|
2437
|
-
JOIN documents d ON d.id = c.document_id
|
|
2438
|
-
WHERE gs.symbol LIKE ?
|
|
2439
|
-
AND gs.symbol LIKE ?
|
|
2440
|
-
${db.pathExclusionsFor("d")}
|
|
2441
|
-
GROUP BY gs.id, gs.symbol, c.document_id, d.relative_path, gs.kind, gs.documentation, gs.enclosing_symbol`,
|
|
2442
|
-
`%${typeName}%`,
|
|
2443
|
-
`%${calleeName}%`
|
|
2444
|
-
);
|
|
2445
|
-
const normalizedType = normalizeLookupName(typeName);
|
|
2446
|
-
const candidates = rows.filter((row2) => leafName(row2.symbol) === calleeName).filter((row2) => {
|
|
2447
|
-
const parentType = parentTypeName(row2.symbol);
|
|
2448
|
-
return parentType === typeName || row2.symbol.includes(typeName) || normalizeLookupName(parentType ?? "").includes(normalizedType);
|
|
2449
|
-
}).sort((left, right) => {
|
|
2450
|
-
const leftTest = isLikelyTestPath(left.relative_path) ? 1 : 0;
|
|
2451
|
-
const rightTest = isLikelyTestPath(right.relative_path) ? 1 : 0;
|
|
2452
|
-
return leftTest - rightTest || left.relative_path.localeCompare(right.relative_path) || left.start_line - right.start_line;
|
|
2453
|
-
});
|
|
2454
|
-
const row = candidates[0];
|
|
2455
|
-
if (!row) {
|
|
2456
|
-
return null;
|
|
2457
|
-
}
|
|
2458
|
-
return {
|
|
2459
|
-
symbolId: row.id,
|
|
2460
|
-
symbol: row.symbol,
|
|
2461
|
-
documentId: row.document_id,
|
|
2462
|
-
startLine: row.start_line,
|
|
2463
|
-
endLine: row.end_line,
|
|
2464
|
-
relativePath: row.relative_path,
|
|
2465
|
-
leaf: leafName(row.symbol),
|
|
2466
|
-
parentTypeName: parentTypeName(row.symbol),
|
|
2467
|
-
isFunctionLike: isFunctionLikeSymbol(row.symbol),
|
|
2468
|
-
isTypeLike: leafSuffix(row.symbol) === "type",
|
|
2469
|
-
kind: row.kind,
|
|
2470
|
-
documentation: row.documentation,
|
|
2471
|
-
enclosingSymbol: row.enclosing_symbol
|
|
2472
|
-
};
|
|
2473
|
-
}
|
|
2474
|
-
function hasUniqueLeafDefinition(db, leaf, symbolId) {
|
|
2475
|
-
const rows = db.all(
|
|
2476
|
-
`SELECT id, symbol
|
|
2477
|
-
FROM global_symbols
|
|
2478
|
-
WHERE symbol LIKE ?
|
|
2479
|
-
LIMIT 50`,
|
|
2480
|
-
`%${leaf}%`
|
|
2481
|
-
);
|
|
2482
|
-
let count = 0;
|
|
2483
|
-
for (const row of rows) {
|
|
2484
|
-
if (leafName(row.symbol) !== leaf) continue;
|
|
2485
|
-
count++;
|
|
2486
|
-
if (count > 1 && row.id !== symbolId) {
|
|
2487
|
-
return false;
|
|
2488
|
-
}
|
|
2489
|
-
}
|
|
2490
|
-
return count === 1;
|
|
2491
|
-
}
|
|
2492
|
-
function parentTypeName(rawSymbol) {
|
|
2493
|
-
const parsed = parseSymbol(rawSymbol);
|
|
2494
|
-
if ("kind" in parsed) {
|
|
2495
|
-
return null;
|
|
2496
|
-
}
|
|
2497
|
-
for (let index = parsed.descriptors.length - 2; index >= 0; index--) {
|
|
2498
|
-
const descriptor = parsed.descriptors[index];
|
|
2499
|
-
if (descriptor?.suffix === "type") {
|
|
2500
|
-
return descriptor.name;
|
|
2501
|
-
}
|
|
2502
|
-
}
|
|
2503
|
-
return null;
|
|
2504
|
-
}
|
|
2505
|
-
var DOCUMENT_LANGUAGE_TABLE = [
|
|
2506
|
-
{ languages: ["python"], extensionPattern: /\.(?:py|pyi)$/ },
|
|
2507
|
-
{ languages: ["typescript", "javascript"], extensionPattern: /\.(?:[cm]?[jt]sx?)$/ },
|
|
2508
|
-
{ languages: ["java"], extensionPattern: /\.java$/ },
|
|
2509
|
-
{ languages: ["kotlin"], extensionPattern: /\.(?:kt|kts)$/ },
|
|
2510
|
-
{ languages: ["scala"], extensionPattern: /\.scala$/ },
|
|
2511
|
-
{ languages: ["C#"], extensionPattern: /\.cs$/ },
|
|
2512
|
-
{ languages: ["Visual Basic"], extensionPattern: /\.vb$/ },
|
|
2513
|
-
{ languages: ["CPP"], extensionPattern: /\.(?:cc|cpp|cxx|hpp|hh|hxx)$/ },
|
|
2514
|
-
{ languages: ["Rust"], extensionPattern: /\.rs$/ },
|
|
2515
|
-
{ languages: ["ruby"], extensionPattern: /\.rb$/ },
|
|
2516
|
-
{ languages: ["Dart"], extensionPattern: /\.dart$/ },
|
|
2517
|
-
{ languages: ["PHP"], extensionPattern: /\.php$/ }
|
|
2518
|
-
];
|
|
2519
|
-
function isDocumentLanguage(db, relativePath, entry) {
|
|
2520
|
-
const row = db.get(
|
|
2521
|
-
`SELECT language FROM documents WHERE relative_path = ? LIMIT 1`,
|
|
2522
|
-
relativePath
|
|
2523
|
-
);
|
|
2524
|
-
return entry.languages.includes(row?.language ?? "") || entry.extensionPattern.test(relativePath);
|
|
2525
|
-
}
|
|
2526
|
-
function isPythonDocument(db, relativePath) {
|
|
2527
|
-
return isDocumentLanguage(db, relativePath, DOCUMENT_LANGUAGE_TABLE[0]);
|
|
2528
|
-
}
|
|
2529
|
-
function applyLimit(values, limit) {
|
|
2530
|
-
return typeof limit === "number" ? values.slice(0, limit) : values;
|
|
2531
|
-
}
|
|
2532
|
-
function resolveDocumentCandidates(db, filePattern, opts) {
|
|
2533
|
-
const normalizedPattern = normalizeLookupPath(filePattern);
|
|
2534
|
-
if (!normalizedPattern) {
|
|
2535
|
-
return [];
|
|
2536
|
-
}
|
|
2537
|
-
const rows = db.all(
|
|
2538
|
-
`SELECT relative_path
|
|
2539
|
-
FROM documents
|
|
2540
|
-
WHERE 1 = 1
|
|
2541
|
-
${db.pathExclusionsFor("documents")}
|
|
2542
|
-
ORDER BY relative_path`
|
|
2543
|
-
);
|
|
2544
|
-
const scored = rows.filter((row) => !db.isIgnored(row.relative_path)).map((row) => ({
|
|
2545
|
-
relativePath: row.relative_path,
|
|
2546
|
-
score: scoreDocumentPath(row.relative_path, normalizedPattern)
|
|
2547
|
-
})).filter((row) => row.score > 0).sort((a, b) => b.score - a.score || a.relativePath.localeCompare(b.relativePath));
|
|
2548
|
-
if (scored.length === 0) {
|
|
2549
|
-
const symbolMatch = findFirstSymbolMatch(db, filePattern);
|
|
2550
|
-
if (!symbolMatch || db.isIgnored(symbolMatch.relativePath)) {
|
|
2551
|
-
return [];
|
|
2552
|
-
}
|
|
2553
|
-
return [{
|
|
2554
|
-
relativePath: symbolMatch.relativePath,
|
|
2555
|
-
score: 700
|
|
2556
|
-
}];
|
|
2557
|
-
}
|
|
2558
|
-
const exactish = scored.filter((row) => row.score >= 800);
|
|
2559
|
-
if (exactish.length > 0) {
|
|
2560
|
-
return opts.allowMultiple ? exactish : [exactish[0]];
|
|
2561
|
-
}
|
|
2562
|
-
return opts.allowMultiple ? scored : [scored[0]];
|
|
2563
|
-
}
|
|
2564
|
-
function scoreDocumentPath(relativePath, rawPattern) {
|
|
2565
|
-
const normalizedPath = normalizeLookupPath(relativePath);
|
|
2566
|
-
const pathBase = basename2(normalizedPath);
|
|
2567
|
-
const patternBase = basename2(rawPattern);
|
|
2568
|
-
let score = 0;
|
|
2569
|
-
if (normalizedPath === rawPattern) score += 1200;
|
|
2570
|
-
if (normalizedPath.endsWith(`/${rawPattern}`)) score += 1100;
|
|
2571
|
-
if (pathBase === patternBase) score += 900;
|
|
2572
|
-
if (normalizedPath.startsWith(`${rawPattern}/`)) score += 850;
|
|
2573
|
-
if (normalizedPath.includes(rawPattern)) score += 250;
|
|
2574
|
-
return score;
|
|
2575
|
-
}
|
|
2576
|
-
function normalizeLookupPath(filePattern) {
|
|
2577
|
-
return filePattern.trim().replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "").replace(/\/+$/, "");
|
|
2578
|
-
}
|
|
2579
|
-
|
|
2580
|
-
export {
|
|
2581
|
-
getSourceImports,
|
|
2582
|
-
getSourceExports,
|
|
2583
|
-
getSourceText,
|
|
2584
|
-
TEST_FILE_PATTERNS,
|
|
2585
|
-
TEST_SUPPORT_PATH_PATTERNS,
|
|
2586
|
-
buildFileDepGraph,
|
|
2587
|
-
findFirstSymbolMatch,
|
|
2588
|
-
findExactSymbolMatch,
|
|
2589
|
-
resolveIndexedFile,
|
|
2590
|
-
resolveIndexedPaths,
|
|
2591
|
-
getCalleeRowsForSymbol,
|
|
2592
|
-
getCallerRowsForSymbol,
|
|
2593
|
-
getSourceReferenceSites,
|
|
2594
|
-
getResolvedReferenceSites,
|
|
2595
|
-
findEnclosingDefinition,
|
|
2596
|
-
getDefinitionsForFile,
|
|
2597
|
-
getAllDefinitions,
|
|
2598
|
-
getScopedDefinitions
|
|
2599
|
-
};
|
|
2600
|
-
//# sourceMappingURL=chunk-OMCRXXDX.js.map
|