dsh-code-index 0.9.0 → 0.10.0
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/CHANGELOG.md +28 -2
- package/README.md +54 -24
- package/README.zh.md +45 -23
- package/dist/index.d.ts +25 -6
- package/dist/index.js +234 -55
- package/dist/index.js.map +1 -1
- package/grammars/ARKTS-LICENSE +43 -0
- package/grammars/README.md +25 -0
- package/grammars/tree-sitter-arkts.wasm +0 -0
- package/package.json +2 -1
- package/scripts/process-desktop-demo.py +88 -0
- package/scripts/release-smoke.mjs +62 -6
- package/scripts/render-agent-demo.py +87 -0
package/dist/index.js
CHANGED
|
@@ -6,10 +6,12 @@ import path4 from "path";
|
|
|
6
6
|
import { readFile } from "fs/promises";
|
|
7
7
|
import { createRequire } from "module";
|
|
8
8
|
import path from "path";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
9
10
|
import { Language, Parser, Query } from "web-tree-sitter";
|
|
10
11
|
var require2 = createRequire(import.meta.url);
|
|
11
12
|
var WASM_DIR = path.dirname(require2.resolve("tree-sitter-wasms/out/tree-sitter-typescript.wasm"));
|
|
12
13
|
var GRAMMAR_NAMES = {
|
|
14
|
+
arkts: "tree-sitter-arkts",
|
|
13
15
|
typescript: "tree-sitter-typescript",
|
|
14
16
|
javascript: "tree-sitter-javascript",
|
|
15
17
|
python: "tree-sitter-python",
|
|
@@ -20,6 +22,7 @@ var GRAMMAR_NAMES = {
|
|
|
20
22
|
c: "tree-sitter-c"
|
|
21
23
|
};
|
|
22
24
|
var EXT_TO_LANG = {
|
|
25
|
+
".ets": "arkts",
|
|
23
26
|
".ts": "typescript",
|
|
24
27
|
".tsx": "typescript",
|
|
25
28
|
".mts": "typescript",
|
|
@@ -51,6 +54,19 @@ function languageForFile(filePath) {
|
|
|
51
54
|
return EXT_TO_LANG[ext] ?? null;
|
|
52
55
|
}
|
|
53
56
|
var QUERIES = {
|
|
57
|
+
arkts: `
|
|
58
|
+
(struct_declaration) @class
|
|
59
|
+
(function_declaration) @function
|
|
60
|
+
(generator_function_declaration) @function
|
|
61
|
+
(method_definition) @method
|
|
62
|
+
(class_declaration) @class
|
|
63
|
+
(abstract_class_declaration) @class
|
|
64
|
+
(interface_declaration) @interface
|
|
65
|
+
(type_alias_declaration) @type
|
|
66
|
+
(enum_declaration) @enum
|
|
67
|
+
(variable_declarator) @variable
|
|
68
|
+
(public_field_definition) @field
|
|
69
|
+
`,
|
|
54
70
|
typescript: `
|
|
55
71
|
(function_declaration) @function
|
|
56
72
|
(generator_function_declaration) @function
|
|
@@ -132,6 +148,7 @@ var CAPTURE_KINDS = {
|
|
|
132
148
|
module: { kind: "module" }
|
|
133
149
|
};
|
|
134
150
|
var IMPORT_QUERIES = {
|
|
151
|
+
arkts: `(import_statement) @import (export_statement) @reexport`,
|
|
135
152
|
typescript: `
|
|
136
153
|
(import_statement) @import
|
|
137
154
|
(export_statement) @reexport
|
|
@@ -163,6 +180,12 @@ var IMPORT_QUERIES = {
|
|
|
163
180
|
`
|
|
164
181
|
};
|
|
165
182
|
var CALL_QUERIES = {
|
|
183
|
+
arkts: `
|
|
184
|
+
(call_expression function: (identifier) @call)
|
|
185
|
+
(call_expression function: (member_expression property: (property_identifier) @call))
|
|
186
|
+
(arkui_component_expression function: (identifier) @call)
|
|
187
|
+
(new_expression constructor: (identifier) @call)
|
|
188
|
+
`,
|
|
166
189
|
typescript: `
|
|
167
190
|
(call_expression function: (identifier) @call)
|
|
168
191
|
(call_expression function: (member_expression property: (property_identifier) @call))
|
|
@@ -201,6 +224,7 @@ var CALL_QUERIES = {
|
|
|
201
224
|
`
|
|
202
225
|
};
|
|
203
226
|
var CALLABLE_NODES = {
|
|
227
|
+
arkts: /* @__PURE__ */ new Set(["function_declaration", "generator_function_declaration", "method_definition", "function_expression", "arrow_function"]),
|
|
204
228
|
typescript: /* @__PURE__ */ new Set([
|
|
205
229
|
"function_declaration",
|
|
206
230
|
"generator_function_declaration",
|
|
@@ -239,7 +263,7 @@ function getLanguage(id) {
|
|
|
239
263
|
let entry = languageCache.get(id);
|
|
240
264
|
if (!entry) {
|
|
241
265
|
entry = getParser().then(async () => {
|
|
242
|
-
const grammarPath = path.join(WASM_DIR, `${GRAMMAR_NAMES[id]}.wasm`);
|
|
266
|
+
const grammarPath = id === "arkts" ? fileURLToPath(new URL("../grammars/tree-sitter-arkts.wasm", import.meta.url)) : path.join(WASM_DIR, `${GRAMMAR_NAMES[id]}.wasm`);
|
|
243
267
|
const bytes = await readFile(grammarPath);
|
|
244
268
|
const lang = await Language.load(bytes);
|
|
245
269
|
new Query(lang, QUERIES[id]).delete();
|
|
@@ -264,6 +288,7 @@ async function extractAll(code, id, file = "") {
|
|
|
264
288
|
const tree = parser.parse(code);
|
|
265
289
|
if (!tree) throw new Error(`tree-sitter parse returned null for a ${id} source`);
|
|
266
290
|
try {
|
|
291
|
+
const diagnostic = parseDiagnostic(tree.rootNode);
|
|
267
292
|
const drafts = [];
|
|
268
293
|
const symbolQuery = new Query(lang, QUERIES[id]);
|
|
269
294
|
try {
|
|
@@ -272,6 +297,7 @@ async function extractAll(code, id, file = "") {
|
|
|
272
297
|
const def = CAPTURE_KINDS[cap.name];
|
|
273
298
|
if (!def) continue;
|
|
274
299
|
const node = cap.node;
|
|
300
|
+
if (id === "arkts" && !reliableArktsNode(node)) continue;
|
|
275
301
|
if (def.kind === "variable" && !isModuleLevelVariable(id, node)) continue;
|
|
276
302
|
const name2 = nameOf(id, node);
|
|
277
303
|
if (!name2) continue;
|
|
@@ -279,10 +305,11 @@ async function extractAll(code, id, file = "") {
|
|
|
279
305
|
symbol: {
|
|
280
306
|
name: name2,
|
|
281
307
|
kind: kindFor(id, node, def.kind),
|
|
282
|
-
line: node.startPosition.row + 1,
|
|
308
|
+
line: id === "arkts" ? arktsDeclarationStart(node) : node.startPosition.row + 1,
|
|
283
309
|
endLine: node.endPosition.row + 1,
|
|
284
310
|
exported: isExported(id, node),
|
|
285
|
-
signature: signatureFor(id, node)
|
|
311
|
+
signature: signatureFor(id, node),
|
|
312
|
+
...id === "arkts" ? { decorators: arktsDecorators(node) } : {}
|
|
286
313
|
},
|
|
287
314
|
node
|
|
288
315
|
});
|
|
@@ -298,6 +325,7 @@ async function extractAll(code, id, file = "") {
|
|
|
298
325
|
const importQuery = new Query(lang, IMPORT_QUERIES[id]);
|
|
299
326
|
try {
|
|
300
327
|
for (const cap of importQuery.captures(tree.rootNode)) {
|
|
328
|
+
if (id === "arkts" && !reliableArktsNode(cap.node)) continue;
|
|
301
329
|
for (const info of importInfoFor(id, cap.node)) importDetails.push(info);
|
|
302
330
|
}
|
|
303
331
|
} finally {
|
|
@@ -309,6 +337,7 @@ async function extractAll(code, id, file = "") {
|
|
|
309
337
|
try {
|
|
310
338
|
const seen = /* @__PURE__ */ new Set();
|
|
311
339
|
for (const cap of callQuery.captures(tree.rootNode)) {
|
|
340
|
+
if (id === "arkts" && (!reliableArktsNode(cap.node) || isArkuiModifier(cap.node) || isArktsMetadataCall(cap.node))) continue;
|
|
312
341
|
const name2 = cap.node.text.trim();
|
|
313
342
|
if (!name2) continue;
|
|
314
343
|
const line = cap.node.startPosition.row + 1;
|
|
@@ -318,7 +347,14 @@ async function extractAll(code, id, file = "") {
|
|
|
318
347
|
const key = `${from}|${name2}|${line}`;
|
|
319
348
|
if (seen.has(key)) continue;
|
|
320
349
|
seen.add(key);
|
|
321
|
-
calls.push({
|
|
350
|
+
calls.push({
|
|
351
|
+
name: name2,
|
|
352
|
+
line,
|
|
353
|
+
from,
|
|
354
|
+
fromId,
|
|
355
|
+
qualifier: qualifierOf(cap.node),
|
|
356
|
+
...id === "arkts" && isArkuiCall(cap.node) ? { context: "arkui" } : {}
|
|
357
|
+
});
|
|
322
358
|
}
|
|
323
359
|
} finally {
|
|
324
360
|
callQuery.delete();
|
|
@@ -327,11 +363,85 @@ async function extractAll(code, id, file = "") {
|
|
|
327
363
|
symbols.sort(
|
|
328
364
|
(a, b) => a.line - b.line || a.endLine - b.endLine || a.kind.localeCompare(b.kind) || a.name.localeCompare(b.name)
|
|
329
365
|
);
|
|
330
|
-
|
|
366
|
+
if (diagnostic.status === "partial" && !symbols.length && !importDetails.length) diagnostic.status = "failed";
|
|
367
|
+
return { symbols, imports, importDetails, calls, diagnostic };
|
|
331
368
|
} finally {
|
|
332
369
|
tree.delete();
|
|
333
370
|
}
|
|
334
371
|
}
|
|
372
|
+
function parseDiagnostic(root) {
|
|
373
|
+
const diagnostic = { status: root.hasError ? "partial" : "parsed", errorCount: 0, missingCount: 0, issues: [] };
|
|
374
|
+
if (!root.hasError) return diagnostic;
|
|
375
|
+
const stack = [root];
|
|
376
|
+
while (stack.length) {
|
|
377
|
+
const node = stack.pop();
|
|
378
|
+
if (node.type === "ERROR" || node.isMissing) {
|
|
379
|
+
if (node.isMissing) diagnostic.missingCount++;
|
|
380
|
+
else diagnostic.errorCount++;
|
|
381
|
+
if (diagnostic.issues.length < 3) diagnostic.issues.push({ kind: node.isMissing ? "MISSING" : "ERROR", line: node.startPosition.row + 1 });
|
|
382
|
+
}
|
|
383
|
+
for (let i = node.childCount - 1; i >= 0; i--) {
|
|
384
|
+
const child = node.child(i);
|
|
385
|
+
if (child) stack.push(child);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return diagnostic;
|
|
389
|
+
}
|
|
390
|
+
function reliableArktsNode(node) {
|
|
391
|
+
if (node.hasError) return false;
|
|
392
|
+
for (let parent = node.parent; parent; parent = parent.parent) {
|
|
393
|
+
if (parent.type === "ERROR") return false;
|
|
394
|
+
if (["struct_declaration", "class_declaration", "function_declaration", "method_definition"].includes(parent.type) && parent.hasError) return false;
|
|
395
|
+
}
|
|
396
|
+
return true;
|
|
397
|
+
}
|
|
398
|
+
function arktsDecoratorNodes(node) {
|
|
399
|
+
const nodes = node.namedChildren.filter((child) => child?.type === "decorator");
|
|
400
|
+
for (let prev = node.previousNamedSibling; prev?.type === "decorator"; prev = prev.previousNamedSibling) nodes.unshift(prev);
|
|
401
|
+
if (node.parent?.type === "export_statement") {
|
|
402
|
+
nodes.unshift(...node.parent.namedChildren.filter((child) => child?.type === "decorator"));
|
|
403
|
+
}
|
|
404
|
+
return [...new Map(nodes.map((decorator) => [decorator.id, decorator])).values()];
|
|
405
|
+
}
|
|
406
|
+
function arktsDecorators(node) {
|
|
407
|
+
return arktsDecoratorNodes(node).map((decorator) => collapseSpace(decorator.text));
|
|
408
|
+
}
|
|
409
|
+
function arktsDeclarationStart(node) {
|
|
410
|
+
return Math.min(node.startPosition.row, ...arktsDecoratorNodes(node).map((decorator) => decorator.startPosition.row)) + 1;
|
|
411
|
+
}
|
|
412
|
+
function isArkuiCall(node) {
|
|
413
|
+
if (node.parent?.type === "arkui_component_expression") return true;
|
|
414
|
+
if (node.parent?.type !== "call_expression") return false;
|
|
415
|
+
if (!/^[A-Z]/.test(node.text)) return false;
|
|
416
|
+
for (let parent = node.parent; parent; parent = parent.parent) {
|
|
417
|
+
if (parent.type === "arkui_children") return true;
|
|
418
|
+
if (parent.type === "arrow_function" || parent.type === "function_expression") return false;
|
|
419
|
+
if (parent.type === "method_definition" || parent.type === "function_declaration") {
|
|
420
|
+
return parent.type === "method_definition" && nameOf("arkts", parent) === "build" && parent.parent?.type === "struct_body" || arktsDecorators(parent).some((text) => /^@Builder\b/.test(text));
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return false;
|
|
424
|
+
}
|
|
425
|
+
function isArkuiModifier(node) {
|
|
426
|
+
if (node.parent?.type !== "member_expression") return false;
|
|
427
|
+
let receiver = node.parent.childForFieldName("object");
|
|
428
|
+
while (receiver?.type === "call_expression") {
|
|
429
|
+
const callee = receiver.childForFieldName("function");
|
|
430
|
+
if (callee?.type === "identifier") return isArkuiCall(callee);
|
|
431
|
+
if (callee?.type !== "member_expression") return false;
|
|
432
|
+
receiver = callee.childForFieldName("object");
|
|
433
|
+
}
|
|
434
|
+
return receiver?.type === "arkui_component_expression" || receiver?.type === "leading_dot_expression";
|
|
435
|
+
}
|
|
436
|
+
function isArktsMetadataCall(node) {
|
|
437
|
+
for (let parent = node.parent; parent; parent = parent.parent) {
|
|
438
|
+
if (parent.type === "decorator") return true;
|
|
439
|
+
if (parent.type === "arguments") return false;
|
|
440
|
+
if (parent.type === "leading_dot_expression") return true;
|
|
441
|
+
if (CALLABLE_NODES.arkts.has(parent.type)) return false;
|
|
442
|
+
}
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
335
445
|
function dedupeDrafts(drafts) {
|
|
336
446
|
const sorted = [...drafts].sort(
|
|
337
447
|
(a, b) => a.symbol.line - b.symbol.line || a.node.startIndex - b.node.startIndex
|
|
@@ -882,6 +992,10 @@ function signatureFor(id, node) {
|
|
|
882
992
|
}
|
|
883
993
|
function rawSignature(id, node) {
|
|
884
994
|
const name2 = nameOf(id, node);
|
|
995
|
+
if (id === "arkts" && node.type === "struct_declaration") return `struct ${name2}`;
|
|
996
|
+
if (id === "arkts" && node.type === "public_field_definition") {
|
|
997
|
+
return `${name2}${collapseSpace(node.childForFieldName("type")?.text ?? "")}`;
|
|
998
|
+
}
|
|
885
999
|
const params = node.childForFieldName?.("parameters") ?? node.namedChildren.find(
|
|
886
1000
|
(c) => c?.type === "formal_parameters" || c?.type === "method_parameters" || c?.type === "parameters" || c?.type === "parameter_list"
|
|
887
1001
|
);
|
|
@@ -939,7 +1053,7 @@ function isExported(id, node) {
|
|
|
939
1053
|
}
|
|
940
1054
|
for (let parent = node.parent; parent; parent = parent.parent) {
|
|
941
1055
|
if (parent.type === "export_statement") return true;
|
|
942
|
-
if (parent.type === "statement_block" || parent.type === "class_body") return false;
|
|
1056
|
+
if (parent.type === "statement_block" || parent.type === "class_body" || parent.type === "struct_body") return false;
|
|
943
1057
|
if (parent.type === "program") return false;
|
|
944
1058
|
}
|
|
945
1059
|
return false;
|
|
@@ -1030,6 +1144,7 @@ var DEFAULT_EXCLUDED_DIRS = [
|
|
|
1030
1144
|
".dsh-code-index"
|
|
1031
1145
|
];
|
|
1032
1146
|
var SUPPORTED_EXTS = /* @__PURE__ */ new Set([
|
|
1147
|
+
".ets",
|
|
1033
1148
|
".ts",
|
|
1034
1149
|
".tsx",
|
|
1035
1150
|
".mts",
|
|
@@ -1056,32 +1171,38 @@ var SUPPORTED_EXTS = /* @__PURE__ */ new Set([
|
|
|
1056
1171
|
".inl",
|
|
1057
1172
|
".c"
|
|
1058
1173
|
]);
|
|
1059
|
-
function
|
|
1060
|
-
|
|
1174
|
+
function ignoredByGitLayers(layers, relativePath, isDirectory) {
|
|
1175
|
+
let ignored = false;
|
|
1176
|
+
for (const { base, matcher } of layers) {
|
|
1177
|
+
const scoped = base ? path2.posix.relative(base, relativePath) : relativePath;
|
|
1178
|
+
if (!scoped || scoped === ".." || scoped.startsWith("../")) continue;
|
|
1179
|
+
const result = matcher.test(isDirectory ? `${scoped.replace(/\/$/, "")}/` : scoped);
|
|
1180
|
+
if (result.ignored || result.unignored) ignored = result.ignored;
|
|
1181
|
+
}
|
|
1182
|
+
return ignored;
|
|
1183
|
+
}
|
|
1184
|
+
function isGitIgnoredPath(root, relativePath, cache = /* @__PURE__ */ new Map(), isDirectory = relativePath.endsWith("/")) {
|
|
1185
|
+
const rel = relativePath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/$/, "");
|
|
1061
1186
|
if (!rel) return false;
|
|
1062
|
-
const
|
|
1063
|
-
|
|
1064
|
-
|
|
1187
|
+
const parts = rel.split("/");
|
|
1188
|
+
const layers = [];
|
|
1189
|
+
for (let depth = 0; depth < parts.length; depth += 1) {
|
|
1190
|
+
const base = parts.slice(0, depth).join("/");
|
|
1065
1191
|
const ignoreFile = path2.join(root, base, ".gitignore");
|
|
1066
|
-
let fileStat;
|
|
1067
1192
|
try {
|
|
1068
|
-
fileStat = statSync(ignoreFile);
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
const previous = cache.get(ignoreFile);
|
|
1073
|
-
let matcher = previous?.matcher;
|
|
1074
|
-
if (!previous || previous.mtimeMs !== fileStat.mtimeMs || previous.size !== fileStat.size) {
|
|
1075
|
-
try {
|
|
1193
|
+
const fileStat = statSync(ignoreFile);
|
|
1194
|
+
const previous = cache.get(ignoreFile);
|
|
1195
|
+
let matcher = previous?.matcher;
|
|
1196
|
+
if (!previous || previous.mtimeMs !== fileStat.mtimeMs || previous.size !== fileStat.size) {
|
|
1076
1197
|
matcher = ignore().add(readFileSync(ignoreFile, "utf8"));
|
|
1077
1198
|
cache.set(ignoreFile, { mtimeMs: fileStat.mtimeMs, size: fileStat.size, matcher });
|
|
1078
|
-
} catch {
|
|
1079
|
-
cache.delete(ignoreFile);
|
|
1080
|
-
continue;
|
|
1081
1199
|
}
|
|
1200
|
+
if (matcher) layers.push({ base, matcher });
|
|
1201
|
+
} catch {
|
|
1202
|
+
cache.delete(ignoreFile);
|
|
1082
1203
|
}
|
|
1083
|
-
const
|
|
1084
|
-
if (
|
|
1204
|
+
const probe = parts.slice(0, depth + 1).join("/");
|
|
1205
|
+
if (ignoredByGitLayers(layers, probe, depth < parts.length - 1 || isDirectory)) return true;
|
|
1085
1206
|
}
|
|
1086
1207
|
return false;
|
|
1087
1208
|
}
|
|
@@ -1097,10 +1218,6 @@ async function scanRepo(root, options = {}) {
|
|
|
1097
1218
|
layers = [...inherited, { base: relDir, matcher: ignore().add(rules) }];
|
|
1098
1219
|
} catch {
|
|
1099
1220
|
}
|
|
1100
|
-
const ignoredByGit = (relativePath) => layers.some(({ base, matcher }) => {
|
|
1101
|
-
const scoped = base ? path2.posix.relative(base, relativePath) : relativePath;
|
|
1102
|
-
return scoped !== ".." && !scoped.startsWith("../") && matcher.ignores(scoped);
|
|
1103
|
-
});
|
|
1104
1221
|
let entries;
|
|
1105
1222
|
try {
|
|
1106
1223
|
entries = await readdir(absDir, { withFileTypes: true });
|
|
@@ -1112,8 +1229,8 @@ async function scanRepo(root, options = {}) {
|
|
|
1112
1229
|
const rel = relDir ? `${relDir}/${entry.name}` : entry.name;
|
|
1113
1230
|
if (entry.isDirectory()) {
|
|
1114
1231
|
if (excluded.has(entry.name)) continue;
|
|
1115
|
-
if (!
|
|
1116
|
-
} else if (entry.isFile() && !
|
|
1232
|
+
if (!ignoredByGitLayers(layers, rel, true)) queue.push([abs, rel, layers]);
|
|
1233
|
+
} else if (entry.isFile() && !ignoredByGitLayers(layers, rel, false) && SUPPORTED_EXTS.has(path2.extname(entry.name).toLowerCase())) {
|
|
1117
1234
|
try {
|
|
1118
1235
|
const st = await stat(abs);
|
|
1119
1236
|
results.push({ abs, rel, mtimeMs: st.mtimeMs });
|
|
@@ -1132,7 +1249,7 @@ import path3 from "path";
|
|
|
1132
1249
|
import { createHash, randomUUID } from "crypto";
|
|
1133
1250
|
|
|
1134
1251
|
// src/types.ts
|
|
1135
|
-
var REPO_INDEX_SCHEMA_VERSION =
|
|
1252
|
+
var REPO_INDEX_SCHEMA_VERSION = 3;
|
|
1136
1253
|
function symbolCount(index) {
|
|
1137
1254
|
return index.files.reduce((n, f) => n + f.symbols.length, 0);
|
|
1138
1255
|
}
|
|
@@ -1196,7 +1313,7 @@ async function buildIndex(root, options = {}, previous = null, forcePaths = /* @
|
|
|
1196
1313
|
batch.map(async (f) => {
|
|
1197
1314
|
const lang = languageForFile(f.abs);
|
|
1198
1315
|
if (!lang) return null;
|
|
1199
|
-
if (prevMtime.get(f.rel) === f.mtimeMs && !forcePaths.has(f.rel) && !forcePaths.has("*")) {
|
|
1316
|
+
if (prevMtime.get(f.rel) === f.mtimeMs && prevByPath.get(f.rel)?.diagnostic?.status !== "failed" && !forcePaths.has(f.rel) && !forcePaths.has("*")) {
|
|
1200
1317
|
const cached = prevByPath.get(f.rel);
|
|
1201
1318
|
return {
|
|
1202
1319
|
...cached,
|
|
@@ -1208,9 +1325,33 @@ async function buildIndex(root, options = {}, previous = null, forcePaths = /* @
|
|
|
1208
1325
|
try {
|
|
1209
1326
|
code = await readFile4(f.abs, "utf8");
|
|
1210
1327
|
} catch {
|
|
1211
|
-
return
|
|
1328
|
+
return {
|
|
1329
|
+
path: f.rel,
|
|
1330
|
+
lang,
|
|
1331
|
+
mtimeMs: f.mtimeMs,
|
|
1332
|
+
symbols: [],
|
|
1333
|
+
imports: [],
|
|
1334
|
+
importDetails: [],
|
|
1335
|
+
calls: [],
|
|
1336
|
+
diagnostic: { status: "failed", errorCount: 0, missingCount: 0, issues: [], reason: "source-unavailable" }
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
let extracted;
|
|
1340
|
+
try {
|
|
1341
|
+
extracted = await extractAll(code, lang, f.rel);
|
|
1342
|
+
} catch {
|
|
1343
|
+
return {
|
|
1344
|
+
path: f.rel,
|
|
1345
|
+
lang,
|
|
1346
|
+
mtimeMs: f.mtimeMs,
|
|
1347
|
+
symbols: [],
|
|
1348
|
+
imports: [],
|
|
1349
|
+
importDetails: [],
|
|
1350
|
+
calls: [],
|
|
1351
|
+
diagnostic: { status: "failed", errorCount: 0, missingCount: 0, issues: [], reason: "parser-failed" }
|
|
1352
|
+
};
|
|
1212
1353
|
}
|
|
1213
|
-
const { symbols, imports, importDetails, calls } =
|
|
1354
|
+
const { symbols, imports, importDetails, calls, diagnostic } = extracted;
|
|
1214
1355
|
return {
|
|
1215
1356
|
path: f.rel,
|
|
1216
1357
|
lang,
|
|
@@ -1218,7 +1359,8 @@ async function buildIndex(root, options = {}, previous = null, forcePaths = /* @
|
|
|
1218
1359
|
symbols: symbols.map((s) => ({ ...s, file: f.rel })),
|
|
1219
1360
|
imports,
|
|
1220
1361
|
importDetails,
|
|
1221
|
-
calls
|
|
1362
|
+
calls,
|
|
1363
|
+
diagnostic
|
|
1222
1364
|
};
|
|
1223
1365
|
})
|
|
1224
1366
|
);
|
|
@@ -1266,11 +1408,12 @@ function indexesEqual(left, right) {
|
|
|
1266
1408
|
return false;
|
|
1267
1409
|
}
|
|
1268
1410
|
if (file.symbols.length !== other.symbols.length) return false;
|
|
1411
|
+
if (JSON.stringify(file.diagnostic) !== JSON.stringify(other.diagnostic)) return false;
|
|
1269
1412
|
if (!callsEqual(file.calls, other.calls)) return false;
|
|
1270
1413
|
if (!importsEqual(file.importDetails, other.importDetails)) return false;
|
|
1271
1414
|
return file.symbols.every((symbol, symbolIndex) => {
|
|
1272
1415
|
const candidate = other.symbols[symbolIndex];
|
|
1273
|
-
return candidate !== void 0 && symbol.id === candidate.id && symbol.name === candidate.name && symbol.kind === candidate.kind && symbol.file === candidate.file && symbol.line === candidate.line && symbol.endLine === candidate.endLine && symbol.exported === candidate.exported && symbol.signature === candidate.signature && symbol.ordinal === candidate.ordinal && scopesEqual(symbol.scope, candidate.scope);
|
|
1416
|
+
return candidate !== void 0 && symbol.id === candidate.id && symbol.name === candidate.name && symbol.kind === candidate.kind && symbol.file === candidate.file && symbol.line === candidate.line && symbol.endLine === candidate.endLine && symbol.exported === candidate.exported && symbol.signature === candidate.signature && symbol.ordinal === candidate.ordinal && JSON.stringify(symbol.decorators) === JSON.stringify(candidate.decorators) && scopesEqual(symbol.scope, candidate.scope);
|
|
1274
1417
|
});
|
|
1275
1418
|
});
|
|
1276
1419
|
}
|
|
@@ -1302,7 +1445,7 @@ function callsEqual(left, right) {
|
|
|
1302
1445
|
if (a.length !== b.length) return false;
|
|
1303
1446
|
return a.every((call, index) => {
|
|
1304
1447
|
const other = b[index];
|
|
1305
|
-
return other !== void 0 && call.name === other.name && call.line === other.line && call.from === other.from && call.fromId === other.fromId && call.qualifier === other.qualifier;
|
|
1448
|
+
return other !== void 0 && call.name === other.name && call.line === other.line && call.from === other.from && call.fromId === other.fromId && call.qualifier === other.qualifier && call.context === other.context;
|
|
1306
1449
|
});
|
|
1307
1450
|
}
|
|
1308
1451
|
async function findRepoRoot(startDir, maxLevels = 12) {
|
|
@@ -1320,6 +1463,21 @@ async function findRepoRoot(startDir, maxLevels = 12) {
|
|
|
1320
1463
|
return null;
|
|
1321
1464
|
}
|
|
1322
1465
|
|
|
1466
|
+
// src/integrity.ts
|
|
1467
|
+
function indexIntegrityWarnings(index) {
|
|
1468
|
+
const affected = index.files.filter((file) => file.diagnostic && file.diagnostic.status !== "parsed");
|
|
1469
|
+
if (!affected.length) return [];
|
|
1470
|
+
const partial = affected.filter((file) => file.diagnostic.status === "partial").length;
|
|
1471
|
+
return [
|
|
1472
|
+
`index incomplete: ${partial} partial, ${affected.length - partial} failed; inspect code_index`,
|
|
1473
|
+
...affected.slice(0, 3).map((file) => {
|
|
1474
|
+
const diagnostic = file.diagnostic;
|
|
1475
|
+
const location = diagnostic.issues[0]?.line;
|
|
1476
|
+
return `${file.path.slice(0, 100)}${location ? `:${location}` : ""}: ${diagnostic.status} (${diagnostic.reason ?? `${diagnostic.errorCount} ERROR, ${diagnostic.missingCount} MISSING`})`;
|
|
1477
|
+
})
|
|
1478
|
+
];
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1323
1481
|
// src/repomap.ts
|
|
1324
1482
|
import path5 from "path";
|
|
1325
1483
|
var KIND_WEIGHT = {
|
|
@@ -1334,7 +1492,7 @@ var KIND_WEIGHT = {
|
|
|
1334
1492
|
import: 0.05,
|
|
1335
1493
|
module: 0.3
|
|
1336
1494
|
};
|
|
1337
|
-
var TEST_PATH_RE = /(^|\/)(tests?|__tests__)(\/|$)|\.(?:spec|test)\.[cm]?[jt]sx
|
|
1495
|
+
var TEST_PATH_RE = /(^|\/)(tests?|__tests__)(\/|$)|\.(?:spec|test)\.(?:[cm]?[jt]sx?|ets)$|(^|\/)(?:test_[^/]+\.py|[^/]+_test\.(?:py|go))$/;
|
|
1338
1496
|
function scoreFile(file) {
|
|
1339
1497
|
let score = 0;
|
|
1340
1498
|
for (const sym of file.symbols) {
|
|
@@ -1426,6 +1584,7 @@ function referencePageRank(files, density) {
|
|
|
1426
1584
|
}
|
|
1427
1585
|
function resolveImportDetailed(spec, fromPath, fileSet) {
|
|
1428
1586
|
if (!spec) return { target: null, suffixSkipped: false };
|
|
1587
|
+
if (/^@(ohos|kit)\./.test(spec)) return { target: null, suffixSkipped: false };
|
|
1429
1588
|
if (spec.startsWith("./") || spec.startsWith("../")) {
|
|
1430
1589
|
const base = path5.posix.normalize(path5.posix.join(path5.posix.dirname(fromPath), spec));
|
|
1431
1590
|
return { target: candidateFor(base, fileSet), suffixSkipped: false };
|
|
@@ -1442,11 +1601,13 @@ function resolveImport(spec, fromPath, fileSet) {
|
|
|
1442
1601
|
}
|
|
1443
1602
|
function candidateFor(base, fileSet) {
|
|
1444
1603
|
if (fileSet.has(base)) return base;
|
|
1604
|
+
if (fileSet.has(`${base}.ets`) && [...SUPPORTED_EXTS].some((ext) => ext !== ".ets" && fileSet.has(`${base}${ext}`))) return null;
|
|
1445
1605
|
for (const ext of SUPPORTED_EXTS) {
|
|
1446
1606
|
if (fileSet.has(`${base}${ext}`)) return `${base}${ext}`;
|
|
1447
1607
|
}
|
|
1448
1608
|
const aliased = tsSourceForJsSpecifier(base, fileSet);
|
|
1449
1609
|
if (aliased) return aliased;
|
|
1610
|
+
if (fileSet.has(`${base}/index.ets`) && [...SUPPORTED_EXTS].some((ext) => ext !== ".ets" && fileSet.has(`${base}/index${ext}`))) return null;
|
|
1450
1611
|
for (const ext of SUPPORTED_EXTS) {
|
|
1451
1612
|
if (fileSet.has(`${base}/index${ext}`)) return `${base}/index${ext}`;
|
|
1452
1613
|
if (fileSet.has(`${base}/__init__${ext}`)) return `${base}/__init__${ext}`;
|
|
@@ -1512,20 +1673,31 @@ function symbolRefs(index, symbol, limit = 200) {
|
|
|
1512
1673
|
const defs = table.get(symbol) ?? [];
|
|
1513
1674
|
const callers = [];
|
|
1514
1675
|
const calleeFirstLine = /* @__PURE__ */ new Map();
|
|
1676
|
+
const arktsTargets = /* @__PURE__ */ new Map();
|
|
1677
|
+
const graph = index.files.some((file) => file.lang === "arkts") ? buildReferenceGraph(index) : null;
|
|
1678
|
+
const arktsEdgesBySite = /* @__PURE__ */ new Map();
|
|
1679
|
+
if (graph) for (const edge of [...graph.outgoing.values()].flat().concat(graph.moduleLevelEdges)) {
|
|
1680
|
+
const key = `${edge.callSite.file}\0${edge.callSite.line}\0${edge.name}\0${edge.sourceId}`;
|
|
1681
|
+
const rows = arktsEdgesBySite.get(key) ?? [];
|
|
1682
|
+
rows.push(edge);
|
|
1683
|
+
arktsEdgesBySite.set(key, rows);
|
|
1684
|
+
}
|
|
1515
1685
|
for (const file of index.files) {
|
|
1516
1686
|
for (const call of file.calls ?? []) {
|
|
1517
|
-
|
|
1687
|
+
const resolved = file.lang === "arkts" && graph ? (arktsEdgesBySite.get(`${file.path}\0${call.line}\0${call.name}\0${call.fromId}`) ?? []).map((edge) => graph.symbolsById.get(edge.targetId)) : null;
|
|
1688
|
+
if (resolved ? resolved.some((target) => target.name === symbol) : call.name === symbol) {
|
|
1518
1689
|
callers.push({ file: file.path, line: call.line, from: call.from });
|
|
1519
1690
|
}
|
|
1520
1691
|
if (call.from === symbol && !calleeFirstLine.has(call.name)) {
|
|
1521
1692
|
calleeFirstLine.set(call.name, call.line);
|
|
1522
1693
|
}
|
|
1694
|
+
if (call.from === symbol) arktsTargets.set(call.name, dedupeSymbols([...arktsTargets.get(call.name) ?? [], ...resolved ?? table.get(call.name) ?? []]));
|
|
1523
1695
|
}
|
|
1524
1696
|
}
|
|
1525
1697
|
callers.sort(
|
|
1526
1698
|
(a, b) => a.file.localeCompare(b.file) || a.line - b.line || a.from.localeCompare(b.from)
|
|
1527
1699
|
);
|
|
1528
|
-
const callees = [...calleeFirstLine.entries()].map(([name2, line]) => ({ name: name2, line, targets: table.get(name2) ?? [] })).sort((a, b) => a.line - b.line || a.name.localeCompare(b.name));
|
|
1700
|
+
const callees = [...calleeFirstLine.entries()].map(([name2, line]) => ({ name: name2, line, targets: arktsTargets.get(name2) ?? table.get(name2) ?? [] })).sort((a, b) => a.line - b.line || a.name.localeCompare(b.name));
|
|
1529
1701
|
return {
|
|
1530
1702
|
defs,
|
|
1531
1703
|
callers: callers.slice(0, limit),
|
|
@@ -1572,7 +1744,7 @@ function resolveCallTargetsFor(fileSet, file, call, symbolsByName, symbolsByFile
|
|
|
1572
1744
|
const qualified = call.qualifier !== null;
|
|
1573
1745
|
const imports = (file.importDetails ?? []).filter((info) => info.kind !== "reexport");
|
|
1574
1746
|
if (!qualified) {
|
|
1575
|
-
const locals = named.filter((s) => s.file === file.path && lexicallyVisible(s, call));
|
|
1747
|
+
const locals = named.filter((s) => s.file === file.path && lexicallyVisible(s, call) && (call.context !== "arkui" || s.signature.startsWith("struct ") || s.decorators?.some((text) => /^@Builder\b/.test(text))));
|
|
1576
1748
|
if (locals.length > 0) {
|
|
1577
1749
|
const unique2 = locals.length === 1;
|
|
1578
1750
|
const owned = unique2 && isStructuralOwner(call, locals[0]);
|
|
@@ -1602,7 +1774,7 @@ function resolveCallTargetsFor(fileSet, file, call, symbolsByName, symbolsByFile
|
|
|
1602
1774
|
}
|
|
1603
1775
|
if (!isExplicitBinding(binding)) continue;
|
|
1604
1776
|
for (const def of symbolsByName.get(binding.imported) ?? []) {
|
|
1605
|
-
if (def.file === target) bound.push(def);
|
|
1777
|
+
if (def.file === target && (file.lang !== "arkts" && !target.endsWith(".ets") || def.exported && def.scope.length === 0)) bound.push(def);
|
|
1606
1778
|
}
|
|
1607
1779
|
}
|
|
1608
1780
|
}
|
|
@@ -1610,6 +1782,7 @@ function resolveCallTargetsFor(fileSet, file, call, symbolsByName, symbolsByFile
|
|
|
1610
1782
|
const label = bound.length === 1 && directBinding && !defaultBinding ? "exact" : "import-scoped";
|
|
1611
1783
|
return toEdges(bound, call, file.path, "explicit-import-binding", label, maxCandidates);
|
|
1612
1784
|
}
|
|
1785
|
+
if (call.context === "arkui") return [];
|
|
1613
1786
|
const resolvedModules = /* @__PURE__ */ new Set();
|
|
1614
1787
|
for (const info of imports) {
|
|
1615
1788
|
const target = resolveImport(info.specifier, file.path, fileSet);
|
|
@@ -1824,7 +1997,7 @@ function findCycles(index) {
|
|
|
1824
1997
|
return components;
|
|
1825
1998
|
}
|
|
1826
1999
|
var ENTRY_RE = /(^|\/)(index|main|cli|app|server|mod|lib|__init__)\.[A-Za-z0-9]+$|(^|\/)(bin|cmd|scripts)\/|\.config\.[A-Za-z0-9]+$/;
|
|
1827
|
-
var TEST_RE = /(^|\/)(tests?|__tests__)(\/|$)|\.(?:spec|test)\.[cm]?[jt]sx
|
|
2000
|
+
var TEST_RE = /(^|\/)(tests?|__tests__)(\/|$)|\.(?:spec|test)\.(?:[cm]?[jt]sx?|ets)$|(^|\/)(?:test_[^/]+\.py|[^/]+_test\.(?:py|go))$/;
|
|
1828
2001
|
function findOrphanModules(index) {
|
|
1829
2002
|
const { nodes, edges } = buildModuleGraph(index);
|
|
1830
2003
|
const incoming = /* @__PURE__ */ new Map();
|
|
@@ -2136,7 +2309,7 @@ var LABEL_STRENGTH = {
|
|
|
2136
2309
|
};
|
|
2137
2310
|
var ENTRY_KINDS = /* @__PURE__ */ new Set(["function", "method", "class"]);
|
|
2138
2311
|
var ENTRY_FILE_RE = /(^|\/)(main|index|cli|server|app)\.[^./]+$/;
|
|
2139
|
-
var TEST_RE2 = /(^|\/)(tests?|__tests__)(\/|$)|\.(?:spec|test)\.[cm]?[jt]sx
|
|
2312
|
+
var TEST_RE2 = /(^|\/)(tests?|__tests__)(\/|$)|\.(?:spec|test)\.(?:[cm]?[jt]sx?|ets)$|(^|\/)(?:test_[^/]+\.py|[^/]+_test\.(?:py|go))$/;
|
|
2140
2313
|
function changeContextInputFromArgs(args, root) {
|
|
2141
2314
|
const hasDiff = typeof args.diff === "string" && args.diff.trim().length > 0;
|
|
2142
2315
|
const hasFiles = Array.isArray(args.files) && args.files.length > 0;
|
|
@@ -2186,7 +2359,7 @@ async function buildChangeContext(index, input, options = {}) {
|
|
|
2186
2359
|
const fileSet = new Set(filesByPath.keys());
|
|
2187
2360
|
const incomingCount = /* @__PURE__ */ new Map();
|
|
2188
2361
|
for (const [id, edges] of graph.incoming) incomingCount.set(id, edges.length);
|
|
2189
|
-
const warnings =
|
|
2362
|
+
const warnings = indexIntegrityWarnings(index);
|
|
2190
2363
|
const changedByKey = /* @__PURE__ */ new Map();
|
|
2191
2364
|
const unmapped = [];
|
|
2192
2365
|
const changedFiles = /* @__PURE__ */ new Set();
|
|
@@ -2263,6 +2436,7 @@ async function buildChangeContext(index, input, options = {}) {
|
|
|
2263
2436
|
const lang = languageForFile(filePath);
|
|
2264
2437
|
if (!lang) return null;
|
|
2265
2438
|
const extracted = await extractAll(content, lang, filePath);
|
|
2439
|
+
if (extracted.diagnostic.status !== "parsed") warnings.push(`base ${filePath.slice(0, 100)}: ${extracted.diagnostic.status} parse; change mapping incomplete`);
|
|
2266
2440
|
return extracted.symbols;
|
|
2267
2441
|
})();
|
|
2268
2442
|
baseSymbolsCache.set(filePath, pending);
|
|
@@ -2928,7 +3102,7 @@ async function selectContextPack(index, context, budgetChars, maxFiles = 12) {
|
|
|
2928
3102
|
const graph = buildReferenceGraph(index);
|
|
2929
3103
|
const candidates = [];
|
|
2930
3104
|
const seen = /* @__PURE__ */ new Set();
|
|
2931
|
-
const gaps = [];
|
|
3105
|
+
const gaps = [...context.warnings];
|
|
2932
3106
|
const unavailable = /* @__PURE__ */ new Set();
|
|
2933
3107
|
const source = async (symbol, kind, reason, extra = {}, focus) => {
|
|
2934
3108
|
const side = extra.side ?? "current";
|
|
@@ -3044,7 +3218,7 @@ async function selectContextPack(index, context, budgetChars, maxFiles = 12) {
|
|
|
3044
3218
|
task: context.task.slice(0, 120),
|
|
3045
3219
|
route: context.route.kind,
|
|
3046
3220
|
items: [],
|
|
3047
|
-
gaps: ["budget omitted some evidence"],
|
|
3221
|
+
gaps: context.warnings.some((warning) => warning.startsWith("index incomplete:")) ? ["index incomplete; inspect code_index"] : ["budget omitted some evidence"],
|
|
3048
3222
|
budget: { budgetChars, usedChars: 0, packChars: 0, truncated: context.task.length > 120 }
|
|
3049
3223
|
};
|
|
3050
3224
|
const fits = () => {
|
|
@@ -3053,7 +3227,10 @@ async function selectContextPack(index, context, budgetChars, maxFiles = 12) {
|
|
|
3053
3227
|
};
|
|
3054
3228
|
if (!fits()) {
|
|
3055
3229
|
pack.task = "";
|
|
3056
|
-
|
|
3230
|
+
fits();
|
|
3231
|
+
}
|
|
3232
|
+
if (!fits()) {
|
|
3233
|
+
pack.gaps = pack.gaps.filter((gap) => gap.startsWith("index incomplete"));
|
|
3057
3234
|
fits();
|
|
3058
3235
|
}
|
|
3059
3236
|
if (!fits()) throw new Error("budget too small for repository identity");
|
|
@@ -3073,7 +3250,7 @@ async function selectContextPack(index, context, budgetChars, maxFiles = 12) {
|
|
|
3073
3250
|
}
|
|
3074
3251
|
if (!added) pack.budget.truncated = true;
|
|
3075
3252
|
}
|
|
3076
|
-
if (!pack.budget.truncated) pack.gaps =
|
|
3253
|
+
if (!pack.budget.truncated) pack.gaps = pack.gaps.filter((gap) => gap.startsWith("index incomplete"));
|
|
3077
3254
|
for (const item of pack.items) if (item.file && unavailable.has(`${item.side}:${item.file}`)) {
|
|
3078
3255
|
gaps.push(`source unavailable or changed after indexing: ${item.file} (${item.side})`);
|
|
3079
3256
|
}
|
|
@@ -3091,7 +3268,7 @@ async function selectContextPack(index, context, budgetChars, maxFiles = 12) {
|
|
|
3091
3268
|
}
|
|
3092
3269
|
|
|
3093
3270
|
// src/context.ts
|
|
3094
|
-
var TEST_PATH_RE2 = /(^|\/)(tests?|__tests__)(\/|$)|\.(?:spec|test)\.[cm]?[jt]sx
|
|
3271
|
+
var TEST_PATH_RE2 = /(^|\/)(tests?|__tests__)(\/|$)|\.(?:spec|test)\.(?:[cm]?[jt]sx?|ets)$|(^|\/)(?:test_[^/]+\.py|[^/]+_test\.(?:py|go))$/i;
|
|
3095
3272
|
var FILE_TOKEN_RE = /[A-Za-z0-9_./\\-]+\.[A-Za-z0-9]+/g;
|
|
3096
3273
|
var CODE_TOKEN_RE = /[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)?/g;
|
|
3097
3274
|
var STOP_WORDS = /* @__PURE__ */ new Set(
|
|
@@ -3357,7 +3534,7 @@ async function buildTaskContext(index, task, options = {}) {
|
|
|
3357
3534
|
const maxSymbols = clamp2(options.maxSymbols, 1, 50, 10);
|
|
3358
3535
|
const route = routeTask(task, index);
|
|
3359
3536
|
let changeContext = null;
|
|
3360
|
-
const warnings =
|
|
3537
|
+
const warnings = indexIntegrityWarnings(index);
|
|
3361
3538
|
if (route.includeChangeContext) {
|
|
3362
3539
|
try {
|
|
3363
3540
|
changeContext = await buildChangeContext(index, {
|
|
@@ -3645,9 +3822,9 @@ var RepoContextManager = class {
|
|
|
3645
3822
|
attachWatcher(context) {
|
|
3646
3823
|
if (!this.options.watch) return;
|
|
3647
3824
|
const options = this.options.indexOptions();
|
|
3648
|
-
const ignored = (absolute) => {
|
|
3825
|
+
const ignored = (absolute, stats) => {
|
|
3649
3826
|
const rel = path9.relative(context.root, absolute);
|
|
3650
|
-
return rel === ".." || rel.startsWith(`..${path9.sep}`) || path9.isAbsolute(rel) || ignoredRelativePath(rel, options) || isGitIgnoredPath(context.root, rel, context.ignoreMatcherCache);
|
|
3827
|
+
return rel === ".." || rel.startsWith(`..${path9.sep}`) || path9.isAbsolute(rel) || ignoredRelativePath(rel, options) || stats !== void 0 && isGitIgnoredPath(context.root, rel, context.ignoreMatcherCache, stats.isDirectory());
|
|
3651
3828
|
};
|
|
3652
3829
|
const watcher = chokidar.watch(context.root, {
|
|
3653
3830
|
ignored,
|
|
@@ -3784,7 +3961,8 @@ var tools = [
|
|
|
3784
3961
|
`files indexed: ${index.files.length}`,
|
|
3785
3962
|
`symbols: ${total}`,
|
|
3786
3963
|
`languages: ${[...langs].join(", ")}`,
|
|
3787
|
-
index.files.length === 0 ? "no supported files found" : "index up to date"
|
|
3964
|
+
index.files.length === 0 ? "no supported files found" : "index up to date",
|
|
3965
|
+
...indexIntegrityWarnings(index)
|
|
3788
3966
|
].join("\n");
|
|
3789
3967
|
} catch (error) {
|
|
3790
3968
|
return `code_index: ${error.message ?? String(error)}`;
|
|
@@ -4322,6 +4500,7 @@ export {
|
|
|
4322
4500
|
findCycles,
|
|
4323
4501
|
findOrphanModules,
|
|
4324
4502
|
findRepoRoot,
|
|
4503
|
+
indexIntegrityWarnings,
|
|
4325
4504
|
inject,
|
|
4326
4505
|
languageForFile,
|
|
4327
4506
|
loadIndex,
|