@aiderdesk/tree-sitter-utils 0.1.0 → 0.2.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/BENCHMARK.md +112 -0
- package/README.md +34 -0
- package/dist/__tests__/mention-extractor.test.d.ts +2 -0
- package/dist/__tests__/mention-extractor.test.d.ts.map +1 -0
- package/dist/__tests__/tree-renderer.test.d.ts +2 -0
- package/dist/__tests__/tree-renderer.test.d.ts.map +1 -0
- package/dist/cache-manager.d.ts +7 -4
- package/dist/cache-manager.d.ts.map +1 -1
- package/dist/dependency-index.d.ts +9 -0
- package/dist/dependency-index.d.ts.map +1 -0
- package/dist/file-utils.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +670 -487
- package/dist/index.js.map +4 -4
- package/dist/mention-extractor.d.ts +4 -0
- package/dist/mention-extractor.d.ts.map +1 -0
- package/dist/repo-map-service.d.ts +28 -0
- package/dist/repo-map-service.d.ts.map +1 -0
- package/dist/repo-map-worker.d.ts +2 -0
- package/dist/repo-map-worker.d.ts.map +1 -0
- package/dist/repo-map-worker.js +907 -0
- package/dist/repo-map-worker.js.map +7 -0
- package/dist/symbol-extractor.d.ts +6 -2
- package/dist/symbol-extractor.d.ts.map +1 -1
- package/dist/tree-renderer.d.ts +1 -1
- package/dist/tree-renderer.d.ts.map +1 -1
- package/dist/tree-sitter-analyzer.d.ts +10 -4
- package/dist/tree-sitter-analyzer.d.ts.map +1 -1
- package/dist/tree-sitter-parser.d.ts +6 -5
- package/dist/tree-sitter-parser.d.ts.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +11 -8
- package/resources/queries/c_sharp-tags.scm +9 -4
|
@@ -0,0 +1,907 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
|
+
for (let key of __getOwnPropNames(from))
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
19
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
24
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
25
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
26
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
27
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
28
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
29
|
+
mod
|
|
30
|
+
));
|
|
31
|
+
|
|
32
|
+
// src/language-detector.ts
|
|
33
|
+
var language_detector_exports = {};
|
|
34
|
+
__export(language_detector_exports, {
|
|
35
|
+
detectLanguage: () => detectLanguage,
|
|
36
|
+
getSupportedExtensions: () => getSupportedExtensions,
|
|
37
|
+
getSupportedLanguages: () => getSupportedLanguages,
|
|
38
|
+
isLanguageSupported: () => isLanguageSupported
|
|
39
|
+
});
|
|
40
|
+
function detectLanguage(filePath) {
|
|
41
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
42
|
+
return LANGUAGE_MAP[ext] || null;
|
|
43
|
+
}
|
|
44
|
+
function isLanguageSupported(filePath) {
|
|
45
|
+
return detectLanguage(filePath) !== null;
|
|
46
|
+
}
|
|
47
|
+
function getSupportedExtensions() {
|
|
48
|
+
return Object.keys(LANGUAGE_MAP);
|
|
49
|
+
}
|
|
50
|
+
function getSupportedLanguages() {
|
|
51
|
+
return [...new Set(Object.values(LANGUAGE_MAP))];
|
|
52
|
+
}
|
|
53
|
+
var path, LANGUAGE_MAP;
|
|
54
|
+
var init_language_detector = __esm({
|
|
55
|
+
"src/language-detector.ts"() {
|
|
56
|
+
"use strict";
|
|
57
|
+
path = __toESM(require("path"));
|
|
58
|
+
LANGUAGE_MAP = {
|
|
59
|
+
".py": "python",
|
|
60
|
+
".js": "javascript",
|
|
61
|
+
".jsx": "javascript",
|
|
62
|
+
".ts": "typescript",
|
|
63
|
+
".tsx": "tsx",
|
|
64
|
+
".mjs": "javascript",
|
|
65
|
+
".cjs": "javascript",
|
|
66
|
+
".java": "java",
|
|
67
|
+
".go": "go",
|
|
68
|
+
".rs": "rust",
|
|
69
|
+
".c": "c",
|
|
70
|
+
".cpp": "cpp",
|
|
71
|
+
".cc": "cpp",
|
|
72
|
+
".cxx": "cpp",
|
|
73
|
+
".h": "c",
|
|
74
|
+
".hpp": "cpp",
|
|
75
|
+
".hxx": "cpp",
|
|
76
|
+
".cs": "c_sharp",
|
|
77
|
+
".dart": "dart",
|
|
78
|
+
".hs": "haskell",
|
|
79
|
+
".lhs": "haskell",
|
|
80
|
+
".jl": "julia",
|
|
81
|
+
".ml": "ocaml",
|
|
82
|
+
".mli": "ocaml_interface",
|
|
83
|
+
".php": "php",
|
|
84
|
+
".rb": "ruby",
|
|
85
|
+
".rake": "ruby",
|
|
86
|
+
".gemspec": "ruby",
|
|
87
|
+
".scala": "scala",
|
|
88
|
+
".sc": "scala"
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// src/repo-map-worker.ts
|
|
94
|
+
var import_worker_threads = require("worker_threads");
|
|
95
|
+
|
|
96
|
+
// src/tree-sitter-analyzer.ts
|
|
97
|
+
var path9 = __toESM(require("path"));
|
|
98
|
+
|
|
99
|
+
// src/tree-sitter-parser.ts
|
|
100
|
+
var fs2 = __toESM(require("fs/promises"));
|
|
101
|
+
var path4 = __toESM(require("path"));
|
|
102
|
+
var import_web_tree_sitter = require("web-tree-sitter");
|
|
103
|
+
init_language_detector();
|
|
104
|
+
|
|
105
|
+
// src/wasm-downloader.ts
|
|
106
|
+
var fs = __toESM(require("fs/promises"));
|
|
107
|
+
var path2 = __toESM(require("path"));
|
|
108
|
+
var os = __toESM(require("os"));
|
|
109
|
+
|
|
110
|
+
// src/logger.ts
|
|
111
|
+
var defaultLogger = {
|
|
112
|
+
info: (message, ...args) => console.log(`[TreeSitterUtils] ${message}`, ...args),
|
|
113
|
+
warn: (message, ...args) => console.warn(`[TreeSitterUtils] ${message}`, ...args),
|
|
114
|
+
error: (message, ...args) => console.error(`[TreeSitterUtils] ${message}`, ...args),
|
|
115
|
+
debug: (message, ...args) => {
|
|
116
|
+
if (process.env.DEBUG_TREE_SITTER) {
|
|
117
|
+
console.debug(`[TreeSitterUtils] ${message}`, ...args);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
var currentLogger = defaultLogger;
|
|
122
|
+
var logger_default = currentLogger;
|
|
123
|
+
|
|
124
|
+
// src/wasm-downloader.ts
|
|
125
|
+
var LANGUAGE_WASM_MAP = {
|
|
126
|
+
python: {
|
|
127
|
+
url: "https://unpkg.com/tree-sitter-python/tree-sitter-python.wasm",
|
|
128
|
+
file: "tree-sitter-python.wasm"
|
|
129
|
+
},
|
|
130
|
+
javascript: {
|
|
131
|
+
url: "https://unpkg.com/tree-sitter-javascript/tree-sitter-javascript.wasm",
|
|
132
|
+
file: "tree-sitter-javascript.wasm"
|
|
133
|
+
},
|
|
134
|
+
typescript: {
|
|
135
|
+
url: "https://unpkg.com/tree-sitter-typescript/tree-sitter-typescript.wasm",
|
|
136
|
+
file: "tree-sitter-typescript.wasm"
|
|
137
|
+
},
|
|
138
|
+
tsx: {
|
|
139
|
+
url: "https://unpkg.com/tree-sitter-typescript/tree-sitter-tsx.wasm",
|
|
140
|
+
file: "tree-sitter-tsx.wasm"
|
|
141
|
+
},
|
|
142
|
+
java: {
|
|
143
|
+
url: "https://unpkg.com/tree-sitter-java/tree-sitter-java.wasm",
|
|
144
|
+
file: "tree-sitter-java.wasm"
|
|
145
|
+
},
|
|
146
|
+
go: {
|
|
147
|
+
url: "https://unpkg.com/tree-sitter-go/tree-sitter-go.wasm",
|
|
148
|
+
file: "tree-sitter-go.wasm"
|
|
149
|
+
},
|
|
150
|
+
rust: {
|
|
151
|
+
url: "https://unpkg.com/tree-sitter-rust/tree-sitter-rust.wasm",
|
|
152
|
+
file: "tree-sitter-rust.wasm"
|
|
153
|
+
},
|
|
154
|
+
c: {
|
|
155
|
+
url: "https://unpkg.com/tree-sitter-c/tree-sitter-c.wasm",
|
|
156
|
+
file: "tree-sitter-c.wasm"
|
|
157
|
+
},
|
|
158
|
+
cpp: {
|
|
159
|
+
url: "https://unpkg.com/tree-sitter-cpp/tree-sitter-cpp.wasm",
|
|
160
|
+
file: "tree-sitter-cpp.wasm"
|
|
161
|
+
},
|
|
162
|
+
ruby: {
|
|
163
|
+
url: "https://unpkg.com/tree-sitter-ruby/tree-sitter-ruby.wasm",
|
|
164
|
+
file: "tree-sitter-ruby.wasm"
|
|
165
|
+
},
|
|
166
|
+
php: {
|
|
167
|
+
url: "https://unpkg.com/tree-sitter-php/tree-sitter-php.wasm",
|
|
168
|
+
file: "tree-sitter-php.wasm"
|
|
169
|
+
},
|
|
170
|
+
scala: {
|
|
171
|
+
url: "https://unpkg.com/tree-sitter-scala/tree-sitter-scala.wasm",
|
|
172
|
+
file: "tree-sitter-scala.wasm"
|
|
173
|
+
},
|
|
174
|
+
dart: {
|
|
175
|
+
url: "https://unpkg.com/tree-sitter-dart/tree-sitter-dart.wasm",
|
|
176
|
+
file: "tree-sitter-dart.wasm"
|
|
177
|
+
},
|
|
178
|
+
ocaml: {
|
|
179
|
+
url: "https://unpkg.com/tree-sitter-ocaml/tree-sitter-ocaml.wasm",
|
|
180
|
+
file: "tree-sitter-ocaml.wasm"
|
|
181
|
+
},
|
|
182
|
+
ocaml_interface: {
|
|
183
|
+
url: "https://unpkg.com/tree-sitter-ocaml/tree-sitter-ocaml_interface.wasm",
|
|
184
|
+
file: "tree-sitter-ocaml_interface.wasm"
|
|
185
|
+
},
|
|
186
|
+
c_sharp: {
|
|
187
|
+
url: "https://unpkg.com/tree-sitter-c-sharp@0.23.5/tree-sitter-c_sharp.wasm",
|
|
188
|
+
file: "tree-sitter-c_sharp-0.23.5.wasm"
|
|
189
|
+
},
|
|
190
|
+
haskell: {
|
|
191
|
+
url: "https://unpkg.com/tree-sitter-haskell/tree-sitter-haskell.wasm",
|
|
192
|
+
file: "tree-sitter-haskell.wasm"
|
|
193
|
+
},
|
|
194
|
+
julia: {
|
|
195
|
+
url: "https://unpkg.com/tree-sitter-julia/tree-sitter-julia.wasm",
|
|
196
|
+
file: "tree-sitter-julia.wasm"
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
function getCacheDir() {
|
|
200
|
+
const platform2 = os.platform();
|
|
201
|
+
const homeDir = os.homedir();
|
|
202
|
+
if (platform2 === "darwin") {
|
|
203
|
+
return path2.join(homeDir, "Library", "Caches", "aiderdesk", "tree-sitter");
|
|
204
|
+
} else if (platform2 === "win32") {
|
|
205
|
+
return path2.join(process.env.APPDATA || path2.join(homeDir, "AppData", "Roaming"), "aiderdesk", "tree-sitter");
|
|
206
|
+
} else {
|
|
207
|
+
return path2.join(homeDir, ".cache", "aiderdesk", "tree-sitter");
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
async function downloadWasmForLanguage(language) {
|
|
211
|
+
const config = LANGUAGE_WASM_MAP[language];
|
|
212
|
+
if (!config) {
|
|
213
|
+
logger_default.warn(`No WASM configuration for language: ${language}`);
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
const cacheDir = getCacheDir();
|
|
217
|
+
const wasmPath = path2.join(cacheDir, "wasm", config.file);
|
|
218
|
+
try {
|
|
219
|
+
try {
|
|
220
|
+
const buffer2 = await fs.readFile(wasmPath);
|
|
221
|
+
logger_default.debug(`Loaded WASM for ${language} from cache: ${wasmPath}`);
|
|
222
|
+
return buffer2;
|
|
223
|
+
} catch {
|
|
224
|
+
}
|
|
225
|
+
logger_default.info(`Downloading WASM for ${language} from ${config.url}`);
|
|
226
|
+
const response = await fetch(config.url, { redirect: "follow" });
|
|
227
|
+
if (!response.ok) {
|
|
228
|
+
throw new Error(`Failed to download ${language} WASM: ${response.status} ${response.statusText}`);
|
|
229
|
+
}
|
|
230
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
231
|
+
await fs.mkdir(path2.dirname(wasmPath), { recursive: true });
|
|
232
|
+
await fs.writeFile(wasmPath, buffer);
|
|
233
|
+
logger_default.info(`Downloaded and cached WASM for ${language} (${Math.round(buffer.byteLength / 1024)} KB)`);
|
|
234
|
+
return buffer;
|
|
235
|
+
} catch (error) {
|
|
236
|
+
logger_default.error(`Failed to download WASM for ${language}:`, error);
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/constants.ts
|
|
242
|
+
var path3 = __toESM(require("path"));
|
|
243
|
+
var import_url = require("url");
|
|
244
|
+
var import_meta = {};
|
|
245
|
+
var getDirname = () => {
|
|
246
|
+
if (typeof __dirname !== "undefined") {
|
|
247
|
+
return __dirname;
|
|
248
|
+
}
|
|
249
|
+
return path3.dirname((0, import_url.fileURLToPath)(import_meta.url));
|
|
250
|
+
};
|
|
251
|
+
var dirName = getDirname();
|
|
252
|
+
var RESOURCES_DIR = path3.join(dirName, "..", "resources");
|
|
253
|
+
var QUERIES_DIR = path3.join(RESOURCES_DIR, "queries");
|
|
254
|
+
var WASM_DIR = path3.join(RESOURCES_DIR, "wasm");
|
|
255
|
+
|
|
256
|
+
// src/tree-sitter-parser.ts
|
|
257
|
+
var runtimeInitialization;
|
|
258
|
+
var TreeSitterParser = class {
|
|
259
|
+
parser = null;
|
|
260
|
+
languages = /* @__PURE__ */ new Map();
|
|
261
|
+
initialization;
|
|
262
|
+
queue = Promise.resolve();
|
|
263
|
+
initialize() {
|
|
264
|
+
this.initialization ??= this.initializeParser();
|
|
265
|
+
return this.initialization;
|
|
266
|
+
}
|
|
267
|
+
async initializeParser() {
|
|
268
|
+
runtimeInitialization ??= import_web_tree_sitter.Parser.init();
|
|
269
|
+
await runtimeInitialization;
|
|
270
|
+
this.parser = new import_web_tree_sitter.Parser();
|
|
271
|
+
}
|
|
272
|
+
loadLanguage(name) {
|
|
273
|
+
let pending = this.languages.get(name);
|
|
274
|
+
if (!pending) {
|
|
275
|
+
pending = (async () => {
|
|
276
|
+
const buffer = await downloadWasmForLanguage(name);
|
|
277
|
+
if (!buffer) {
|
|
278
|
+
throw new Error(`No WASM available for ${name}`);
|
|
279
|
+
}
|
|
280
|
+
const language = await import_web_tree_sitter.Language.load(buffer);
|
|
281
|
+
const source = await fs2.readFile(path4.join(QUERIES_DIR, `${name}-tags.scm`), "utf-8");
|
|
282
|
+
const query = new import_web_tree_sitter.Query(language, source);
|
|
283
|
+
if (name === "c_sharp") {
|
|
284
|
+
for (const capture of query.captureNames) {
|
|
285
|
+
if (!capture.startsWith("name.")) query.disableCapture(capture);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return { language, query };
|
|
289
|
+
})();
|
|
290
|
+
this.languages.set(name, pending);
|
|
291
|
+
void pending.catch(() => this.languages.delete(name));
|
|
292
|
+
}
|
|
293
|
+
return pending;
|
|
294
|
+
}
|
|
295
|
+
parseFile(filePath, rootDir, compactReferences = false) {
|
|
296
|
+
const result = this.queue.then(() => this.parse(filePath, rootDir, compactReferences));
|
|
297
|
+
this.queue = result.catch(() => void 0);
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
300
|
+
async parse(filePath, rootDir, compactReferences = false) {
|
|
301
|
+
await this.initialize();
|
|
302
|
+
const name = detectLanguage(filePath);
|
|
303
|
+
if (!name || !this.parser) {
|
|
304
|
+
return [];
|
|
305
|
+
}
|
|
306
|
+
const { language, query } = await this.loadLanguage(name);
|
|
307
|
+
const source = await fs2.readFile(filePath, "utf-8");
|
|
308
|
+
this.parser.setLanguage(language);
|
|
309
|
+
const tree = this.parser.parse(source);
|
|
310
|
+
if (!tree) {
|
|
311
|
+
throw new Error(`Unable to parse ${filePath}`);
|
|
312
|
+
}
|
|
313
|
+
try {
|
|
314
|
+
const tags = [];
|
|
315
|
+
const references = /* @__PURE__ */ new Map();
|
|
316
|
+
const relative5 = rootDir ? path4.relative(rootDir, filePath).replaceAll(path4.sep, "/") : filePath;
|
|
317
|
+
for (const match of query.matches(tree.rootNode)) {
|
|
318
|
+
for (const capture of match.captures) {
|
|
319
|
+
if (capture.name.startsWith("name.")) {
|
|
320
|
+
const kind = capture.name.includes("definition") ? "def" : "ref";
|
|
321
|
+
const name2 = capture.node.text;
|
|
322
|
+
const previous = compactReferences && kind === "ref" ? references.get(name2) : void 0;
|
|
323
|
+
if (previous) {
|
|
324
|
+
previous.count = (previous.count ?? 1) + 1;
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
const tag = {
|
|
328
|
+
rel_fname: relative5,
|
|
329
|
+
fname: filePath,
|
|
330
|
+
line: capture.node.startPosition.row,
|
|
331
|
+
// Detach sliced node text so cached tags cannot retain entire source files.
|
|
332
|
+
name: Buffer.from(name2, "utf8").toString("utf8"),
|
|
333
|
+
kind
|
|
334
|
+
};
|
|
335
|
+
tags.push(tag);
|
|
336
|
+
if (compactReferences && kind === "ref") references.set(tag.name, tag);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return tags;
|
|
341
|
+
} finally {
|
|
342
|
+
tree.delete();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
async dispose() {
|
|
346
|
+
await this.queue;
|
|
347
|
+
for (const pending of this.languages.values()) {
|
|
348
|
+
try {
|
|
349
|
+
const { query } = await pending;
|
|
350
|
+
query.delete();
|
|
351
|
+
} catch {
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
this.languages.clear();
|
|
355
|
+
this.parser?.delete();
|
|
356
|
+
this.parser = null;
|
|
357
|
+
this.initialization = void 0;
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// src/symbol-extractor.ts
|
|
362
|
+
var fs3 = __toESM(require("fs/promises"));
|
|
363
|
+
var path5 = __toESM(require("path"));
|
|
364
|
+
init_language_detector();
|
|
365
|
+
var SymbolExtractor = class {
|
|
366
|
+
constructor(parser) {
|
|
367
|
+
this.parser = parser;
|
|
368
|
+
}
|
|
369
|
+
cacheManager = null;
|
|
370
|
+
memory = /* @__PURE__ */ new Map();
|
|
371
|
+
lastRoot;
|
|
372
|
+
lastFiles = "";
|
|
373
|
+
revision = 0;
|
|
374
|
+
setCacheManager(cacheManager) {
|
|
375
|
+
this.cacheManager = cacheManager;
|
|
376
|
+
}
|
|
377
|
+
async extractTags(files, rootDir, signal, compactReferences = false) {
|
|
378
|
+
const allTags = [];
|
|
379
|
+
const supported = files.filter(isLanguageSupported);
|
|
380
|
+
const fileKey = JSON.stringify([compactReferences, supported]);
|
|
381
|
+
if (this.lastRoot !== rootDir || this.lastFiles !== fileKey) {
|
|
382
|
+
this.revision++;
|
|
383
|
+
this.memory.clear();
|
|
384
|
+
this.lastRoot = rootDir;
|
|
385
|
+
this.lastFiles = fileKey;
|
|
386
|
+
}
|
|
387
|
+
for (let i = 0; i < supported.length; i += 50) {
|
|
388
|
+
signal?.throwIfAborted();
|
|
389
|
+
const results = await Promise.all(
|
|
390
|
+
supported.slice(i, i + 50).map(async (file) => {
|
|
391
|
+
try {
|
|
392
|
+
const before = await fs3.stat(file);
|
|
393
|
+
const fingerprint = `${compactReferences}:${before.mtimeMs}:${before.ctimeMs}:${before.size}`;
|
|
394
|
+
const memory = this.memory.get(file);
|
|
395
|
+
if (this.cacheManager && memory?.fingerprint === fingerprint) {
|
|
396
|
+
return memory.tags;
|
|
397
|
+
}
|
|
398
|
+
this.revision++;
|
|
399
|
+
const cached = await this.cacheManager?.getFileCache(file);
|
|
400
|
+
let tags;
|
|
401
|
+
if (cached?.fingerprint === fingerprint) {
|
|
402
|
+
const relative5 = rootDir ? path5.relative(rootDir, file).replaceAll(path5.sep, "/") : file;
|
|
403
|
+
tags = cached.tags.map((tag) => ({
|
|
404
|
+
...tag,
|
|
405
|
+
rel_fname: relative5
|
|
406
|
+
}));
|
|
407
|
+
} else {
|
|
408
|
+
tags = await this.parser.parseFile(file, rootDir, compactReferences);
|
|
409
|
+
const after = await fs3.stat(file);
|
|
410
|
+
if (`${compactReferences}:${after.mtimeMs}:${after.ctimeMs}:${after.size}` !== fingerprint) {
|
|
411
|
+
return tags;
|
|
412
|
+
}
|
|
413
|
+
await this.cacheManager?.setFileCache(file, before.mtimeMs, tags, fingerprint);
|
|
414
|
+
}
|
|
415
|
+
this.memory.set(file, { fingerprint, tags });
|
|
416
|
+
return tags;
|
|
417
|
+
} catch (error) {
|
|
418
|
+
this.revision++;
|
|
419
|
+
this.memory.delete(file);
|
|
420
|
+
logger_default.error(`[SymbolExtractor] Error processing ${file}:`, error);
|
|
421
|
+
return [];
|
|
422
|
+
}
|
|
423
|
+
})
|
|
424
|
+
);
|
|
425
|
+
for (const tags of results) {
|
|
426
|
+
for (const tag of tags) allTags.push(tag);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
signal?.throwIfAborted();
|
|
430
|
+
return allTags;
|
|
431
|
+
}
|
|
432
|
+
filterDefinitions(tags) {
|
|
433
|
+
return tags.filter((tag) => tag.kind === "def");
|
|
434
|
+
}
|
|
435
|
+
filterReferences(tags) {
|
|
436
|
+
return tags.filter((tag) => tag.kind === "ref");
|
|
437
|
+
}
|
|
438
|
+
groupTagsByFile(tags) {
|
|
439
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
440
|
+
for (const tag of tags) {
|
|
441
|
+
const fileTags = grouped.get(tag.rel_fname) || [];
|
|
442
|
+
fileTags.push(tag);
|
|
443
|
+
grouped.set(tag.rel_fname, fileTags);
|
|
444
|
+
}
|
|
445
|
+
return grouped;
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
// src/dependency-index.ts
|
|
450
|
+
var DependencyIndex = class {
|
|
451
|
+
definitions = [];
|
|
452
|
+
relations = [];
|
|
453
|
+
outbound = /* @__PURE__ */ new Map();
|
|
454
|
+
constructor(tags) {
|
|
455
|
+
const definitions = /* @__PURE__ */ new Map();
|
|
456
|
+
const references = /* @__PURE__ */ new Map();
|
|
457
|
+
for (const tag of tags) {
|
|
458
|
+
if (tag.kind === "def") {
|
|
459
|
+
this.definitions.push(tag);
|
|
460
|
+
let files = definitions.get(tag.name);
|
|
461
|
+
if (!files) definitions.set(tag.name, files = /* @__PURE__ */ new Set());
|
|
462
|
+
files.add(tag.rel_fname);
|
|
463
|
+
this.outbound.set(tag.rel_fname, 0);
|
|
464
|
+
} else {
|
|
465
|
+
let counts = references.get(tag.name);
|
|
466
|
+
if (!counts) references.set(tag.name, counts = /* @__PURE__ */ new Map());
|
|
467
|
+
counts.set(tag.rel_fname, (counts.get(tag.rel_fname) ?? 0) + (tag.count ?? 1));
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
for (const [name, definers] of definitions) {
|
|
471
|
+
const counts = references.get(name);
|
|
472
|
+
if (!counts) continue;
|
|
473
|
+
let multiplier = 1;
|
|
474
|
+
if (/^[a-z][a-z0-9_]*$/.test(name) && name.includes("_") || /^[a-z][a-z0-9-]*$/.test(name) && name.includes("-") || /^[a-z][a-zA-Z0-9]*$/.test(name) && /[A-Z]/.test(name))
|
|
475
|
+
multiplier *= 10;
|
|
476
|
+
if (name.startsWith("_")) multiplier *= 0.1;
|
|
477
|
+
if (definers.size > 5) multiplier *= 0.1;
|
|
478
|
+
const sources = /* @__PURE__ */ new Map();
|
|
479
|
+
for (const [file, count] of counts) {
|
|
480
|
+
const targets = definers.size - Number(definers.has(file));
|
|
481
|
+
if (!targets) continue;
|
|
482
|
+
const weight = multiplier * Math.sqrt(count);
|
|
483
|
+
sources.set(file, weight);
|
|
484
|
+
this.outbound.set(file, (this.outbound.get(file) ?? 0) + weight * targets);
|
|
485
|
+
}
|
|
486
|
+
if (sources.size) this.relations.push({ definers, sources });
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
rank(mentionedFiles = /* @__PURE__ */ new Set(), mentionedIdents = /* @__PURE__ */ new Set()) {
|
|
490
|
+
const nodes = [...this.outbound.keys()];
|
|
491
|
+
const count = nodes.length;
|
|
492
|
+
if (!count) return [];
|
|
493
|
+
const mentionedDefinitions = new Set(this.definitions.filter((tag) => mentionedIdents.has(tag.name)).map((tag) => tag.rel_fname));
|
|
494
|
+
const weights = new Map(nodes.map((node) => [node, Number(mentionedFiles.has(node)) + Number(mentionedDefinitions.has(node))]));
|
|
495
|
+
const total = [...weights.values()].reduce((sum, weight) => sum + weight, 0);
|
|
496
|
+
const personalization = new Map(nodes.map((node) => [node, total ? weights.get(node) / total : 1 / count]));
|
|
497
|
+
let ranks = new Map(nodes.map((node) => [node, 1 / count]));
|
|
498
|
+
for (let iteration = 0; iteration < 100; iteration++) {
|
|
499
|
+
let dangling = 0;
|
|
500
|
+
for (const node of nodes) {
|
|
501
|
+
if (!this.outbound.get(node)) dangling += ranks.get(node);
|
|
502
|
+
}
|
|
503
|
+
const base = 0.85 * dangling + 0.15;
|
|
504
|
+
const next = new Map(nodes.map((node) => [node, base * personalization.get(node)]));
|
|
505
|
+
for (const { definers, sources } of this.relations) {
|
|
506
|
+
let incoming = 0;
|
|
507
|
+
for (const [source, weight] of sources) {
|
|
508
|
+
incoming += 0.85 * ranks.get(source) * weight / this.outbound.get(source);
|
|
509
|
+
}
|
|
510
|
+
for (const target of definers) {
|
|
511
|
+
const self = sources.get(target);
|
|
512
|
+
const contribution = incoming - (self ? 0.85 * ranks.get(target) * self / this.outbound.get(target) : 0);
|
|
513
|
+
next.set(target, next.get(target) + contribution);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
let error = 0;
|
|
517
|
+
for (const node of nodes) error += Math.abs(next.get(node) - ranks.get(node));
|
|
518
|
+
ranks = next;
|
|
519
|
+
if (error < count * 1e-6) break;
|
|
520
|
+
if (iteration === 99) throw new Error("Repository map PageRank did not converge");
|
|
521
|
+
}
|
|
522
|
+
return this.definitions.map((tag) => ({
|
|
523
|
+
rel_fname: tag.rel_fname,
|
|
524
|
+
name: tag.name,
|
|
525
|
+
line: tag.line,
|
|
526
|
+
rank: ranks.get(tag.rel_fname) * (this.outbound.get(tag.rel_fname) ? 2 : 1)
|
|
527
|
+
})).sort((a, b) => b.rank - a.rank);
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
// src/tree-renderer.ts
|
|
532
|
+
var fs4 = __toESM(require("fs/promises"));
|
|
533
|
+
var path6 = __toESM(require("path"));
|
|
534
|
+
var TreeRenderer = class {
|
|
535
|
+
async render(rankedDefinitions, chatFiles, maxLines, root = process.cwd()) {
|
|
536
|
+
const filtered = rankedDefinitions.filter((def) => !chatFiles.has(def.rel_fname));
|
|
537
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
538
|
+
for (const def of filtered) {
|
|
539
|
+
const fileDefs = byFile.get(def.rel_fname) || [];
|
|
540
|
+
fileDefs.push(def);
|
|
541
|
+
byFile.set(def.rel_fname, fileDefs);
|
|
542
|
+
}
|
|
543
|
+
const maximumRanks = /* @__PURE__ */ new Map();
|
|
544
|
+
for (const def of filtered) maximumRanks.set(def.rel_fname, Math.max(maximumRanks.get(def.rel_fname) ?? -Infinity, def.rank));
|
|
545
|
+
const sortedFiles = Array.from(byFile.entries()).sort((a, b) => {
|
|
546
|
+
const maxRankA = maximumRanks.get(a[0]);
|
|
547
|
+
const maxRankB = maximumRanks.get(b[0]);
|
|
548
|
+
return maxRankB - maxRankA;
|
|
549
|
+
});
|
|
550
|
+
const totalFiles = sortedFiles.length;
|
|
551
|
+
if (!maxLines) {
|
|
552
|
+
return this.renderAllFiles(sortedFiles, root);
|
|
553
|
+
}
|
|
554
|
+
if (totalFiles * 2 > maxLines) {
|
|
555
|
+
return this.renderFileListOnly(sortedFiles, maxLines);
|
|
556
|
+
}
|
|
557
|
+
const headerLines = totalFiles;
|
|
558
|
+
const availableLines = maxLines - headerLines;
|
|
559
|
+
const linesPerFile = Math.max(1, Math.floor(availableLines / totalFiles));
|
|
560
|
+
return this.renderWithLineLimit(sortedFiles, linesPerFile, root);
|
|
561
|
+
}
|
|
562
|
+
async renderAllFiles(sortedFiles, root) {
|
|
563
|
+
let output = "";
|
|
564
|
+
for (const [file, definitions] of sortedFiles) {
|
|
565
|
+
output += `
|
|
566
|
+
${file}:
|
|
567
|
+
`;
|
|
568
|
+
definitions.sort((a, b) => a.line - b.line);
|
|
569
|
+
const lines = Array.from(new Set(definitions.map((d) => d.line)));
|
|
570
|
+
const treeLines = await this.renderFileTree(path6.resolve(root, file), lines, Infinity);
|
|
571
|
+
output += treeLines;
|
|
572
|
+
}
|
|
573
|
+
return output.trim();
|
|
574
|
+
}
|
|
575
|
+
renderFileListOnly(sortedFiles, maxLines) {
|
|
576
|
+
let output = "";
|
|
577
|
+
const filesToShow = sortedFiles.slice(0, maxLines - 1);
|
|
578
|
+
for (const [file] of filesToShow) {
|
|
579
|
+
output += `${file}
|
|
580
|
+
`;
|
|
581
|
+
}
|
|
582
|
+
if (sortedFiles.length > filesToShow.length) {
|
|
583
|
+
output += "...\n";
|
|
584
|
+
}
|
|
585
|
+
return output.trim();
|
|
586
|
+
}
|
|
587
|
+
async renderWithLineLimit(sortedFiles, linesPerFile, root) {
|
|
588
|
+
let output = "";
|
|
589
|
+
for (const [file, definitions] of sortedFiles) {
|
|
590
|
+
definitions.sort((a, b) => {
|
|
591
|
+
if (a.rank !== b.rank) {
|
|
592
|
+
return b.rank - a.rank;
|
|
593
|
+
}
|
|
594
|
+
return a.line - b.line;
|
|
595
|
+
});
|
|
596
|
+
const uniqueLines = Array.from(new Set(definitions.map((d) => d.line)));
|
|
597
|
+
const limitedLines = uniqueLines.slice(0, linesPerFile);
|
|
598
|
+
const isTruncated = uniqueLines.length > linesPerFile;
|
|
599
|
+
output += `${file}:
|
|
600
|
+
`;
|
|
601
|
+
const treeLines = await this.renderFileTree(path6.resolve(root, file), limitedLines, linesPerFile, isTruncated);
|
|
602
|
+
output += treeLines;
|
|
603
|
+
}
|
|
604
|
+
return output.trim();
|
|
605
|
+
}
|
|
606
|
+
async renderFileTree(filePath, lines, maxLines, isTruncated = false) {
|
|
607
|
+
if (lines.length === 0) {
|
|
608
|
+
return "";
|
|
609
|
+
}
|
|
610
|
+
try {
|
|
611
|
+
const content = await fs4.readFile(filePath, "utf-8");
|
|
612
|
+
const fileLines = content.split("\n");
|
|
613
|
+
let output = "";
|
|
614
|
+
const maxContentLines = isTruncated ? maxLines - 1 : maxLines;
|
|
615
|
+
const linesToRender = lines.slice(0, maxContentLines);
|
|
616
|
+
for (let i = 0; i < linesToRender.length; i++) {
|
|
617
|
+
const lineNum = linesToRender[i];
|
|
618
|
+
const lineContent = fileLines[lineNum]?.trim() || "";
|
|
619
|
+
if (lineContent) {
|
|
620
|
+
const truncated = lineContent.length > 100 ? lineContent.substring(0, 97) + "..." : lineContent;
|
|
621
|
+
const isLast = i === linesToRender.length - 1 && !isTruncated;
|
|
622
|
+
const prefix = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
623
|
+
output += `${prefix}${truncated}
|
|
624
|
+
`;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
if (isTruncated) {
|
|
628
|
+
output += "\u2514\u2500\u2500 ...\n";
|
|
629
|
+
}
|
|
630
|
+
return output;
|
|
631
|
+
} catch (error) {
|
|
632
|
+
logger_default.error(`[TreeRenderer] Error reading file ${filePath}:`, error);
|
|
633
|
+
return "";
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
// src/cache-manager.ts
|
|
639
|
+
var import_node_sqlite = require("node:sqlite");
|
|
640
|
+
var fs5 = __toESM(require("fs/promises"));
|
|
641
|
+
var path7 = __toESM(require("path"));
|
|
642
|
+
var os2 = __toESM(require("os"));
|
|
643
|
+
var getCacheDbPath = () => {
|
|
644
|
+
const home = os2.homedir();
|
|
645
|
+
const directory = process.platform === "darwin" ? path7.join(home, "Library", "Caches") : process.platform === "win32" ? process.env.APPDATA || path7.join(home, "AppData", "Roaming") : path7.join(home, ".cache");
|
|
646
|
+
return path7.join(directory, "aiderdesk", "tree-sitter", "cache.db");
|
|
647
|
+
};
|
|
648
|
+
var CacheManager = class {
|
|
649
|
+
constructor(dbPath = getCacheDbPath()) {
|
|
650
|
+
this.dbPath = dbPath;
|
|
651
|
+
}
|
|
652
|
+
db = null;
|
|
653
|
+
select;
|
|
654
|
+
insert;
|
|
655
|
+
async initialize() {
|
|
656
|
+
try {
|
|
657
|
+
await fs5.mkdir(path7.dirname(this.dbPath), { recursive: true });
|
|
658
|
+
this.db = new import_node_sqlite.DatabaseSync(this.dbPath);
|
|
659
|
+
this.db.exec("PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA busy_timeout = 1000;");
|
|
660
|
+
this.db.exec(`
|
|
661
|
+
CREATE TABLE IF NOT EXISTS file_cache_v3 (
|
|
662
|
+
file_path TEXT PRIMARY KEY,
|
|
663
|
+
mtime REAL NOT NULL,
|
|
664
|
+
fingerprint TEXT NOT NULL,
|
|
665
|
+
tags TEXT NOT NULL
|
|
666
|
+
)
|
|
667
|
+
`);
|
|
668
|
+
this.select = this.db.prepare("SELECT mtime, fingerprint, tags FROM file_cache_v3 WHERE file_path = ?");
|
|
669
|
+
this.insert = this.db.prepare(`
|
|
670
|
+
INSERT INTO file_cache_v3 (file_path, mtime, fingerprint, tags) VALUES (?, ?, ?, ?)
|
|
671
|
+
ON CONFLICT(file_path) DO UPDATE SET mtime = excluded.mtime, fingerprint = excluded.fingerprint, tags = excluded.tags
|
|
672
|
+
`);
|
|
673
|
+
} catch (error) {
|
|
674
|
+
logger_default.warn("[CacheManager] Persistent cache unavailable:", error);
|
|
675
|
+
this.close();
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
async getFileCache(file) {
|
|
679
|
+
try {
|
|
680
|
+
const result = this.select?.get(file);
|
|
681
|
+
if (!result) return null;
|
|
682
|
+
const stored = JSON.parse(result.tags);
|
|
683
|
+
return {
|
|
684
|
+
...result,
|
|
685
|
+
tags: stored.tags.map(([line, name, kind, count]) => ({
|
|
686
|
+
fname: file,
|
|
687
|
+
rel_fname: stored.relative,
|
|
688
|
+
line,
|
|
689
|
+
name,
|
|
690
|
+
kind,
|
|
691
|
+
count
|
|
692
|
+
}))
|
|
693
|
+
};
|
|
694
|
+
} catch (error) {
|
|
695
|
+
logger_default.warn(`[CacheManager] Failed to read ${file}:`, error);
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
async setFileCache(file, mtime, tags, fingerprint = String(mtime)) {
|
|
700
|
+
try {
|
|
701
|
+
this.insert?.run(
|
|
702
|
+
file,
|
|
703
|
+
mtime,
|
|
704
|
+
fingerprint,
|
|
705
|
+
JSON.stringify({
|
|
706
|
+
relative: tags[0]?.rel_fname ?? file,
|
|
707
|
+
tags: tags.map((tag) => [tag.line, tag.name, tag.kind, tag.count])
|
|
708
|
+
})
|
|
709
|
+
);
|
|
710
|
+
} catch (error) {
|
|
711
|
+
logger_default.warn(`[CacheManager] Failed to cache ${file}:`, error);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
async getFileMtime(file) {
|
|
715
|
+
try {
|
|
716
|
+
return (await fs5.stat(file)).mtimeMs;
|
|
717
|
+
} catch {
|
|
718
|
+
return null;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
async clearCache() {
|
|
722
|
+
this.db?.exec("DELETE FROM file_cache_v3");
|
|
723
|
+
}
|
|
724
|
+
close() {
|
|
725
|
+
this.select = void 0;
|
|
726
|
+
this.insert = void 0;
|
|
727
|
+
this.db?.close();
|
|
728
|
+
this.db = null;
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
// src/file-utils.ts
|
|
733
|
+
var path8 = __toESM(require("path"));
|
|
734
|
+
var import_glob = require("glob");
|
|
735
|
+
var import_minimatch = require("minimatch");
|
|
736
|
+
var import_simple_git = require("simple-git");
|
|
737
|
+
var DEFAULT_IGNORE_PATTERNS = [
|
|
738
|
+
"node_modules/**",
|
|
739
|
+
".git/**",
|
|
740
|
+
"dist/**",
|
|
741
|
+
"build/**",
|
|
742
|
+
".next/**",
|
|
743
|
+
"out/**",
|
|
744
|
+
"coverage/**",
|
|
745
|
+
"*.min.js",
|
|
746
|
+
"*.min.css",
|
|
747
|
+
".DS_Store",
|
|
748
|
+
"Thumbs.db",
|
|
749
|
+
"*.log",
|
|
750
|
+
"*.lock",
|
|
751
|
+
"package-lock.json",
|
|
752
|
+
"yarn.lock",
|
|
753
|
+
"pnpm-lock.yaml"
|
|
754
|
+
];
|
|
755
|
+
var getAllFilesFromGit = async (baseDir) => {
|
|
756
|
+
try {
|
|
757
|
+
const result = await (0, import_simple_git.simpleGit)(baseDir).raw(["ls-files", "-z"]);
|
|
758
|
+
return result.split(String.fromCharCode(0)).filter(Boolean).map((file) => path8.resolve(baseDir, file));
|
|
759
|
+
} catch (error) {
|
|
760
|
+
logger_default.debug("Git discovery unavailable, falling back to filesystem", error);
|
|
761
|
+
return null;
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
var getAllFiles = async (baseDir, excludePatterns = []) => {
|
|
765
|
+
const ignore = [...DEFAULT_IGNORE_PATTERNS, ...excludePatterns];
|
|
766
|
+
const matchers = ignore.map((pattern) => ({
|
|
767
|
+
regex: new import_minimatch.Minimatch(pattern, { dot: true }).makeRe(),
|
|
768
|
+
basename: !pattern.includes("/")
|
|
769
|
+
}));
|
|
770
|
+
const gitFiles = await getAllFilesFromGit(baseDir);
|
|
771
|
+
if (gitFiles !== null) {
|
|
772
|
+
return gitFiles.filter((file) => {
|
|
773
|
+
const relative5 = path8.relative(baseDir, file).split(path8.sep).join("/");
|
|
774
|
+
const basename2 = path8.basename(file);
|
|
775
|
+
return !matchers.some((matcher) => matcher.regex && matcher.regex.test(matcher.basename ? basename2 : relative5));
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
try {
|
|
779
|
+
return await (0, import_glob.glob)("**/*", { cwd: baseDir, nodir: true, absolute: true, dot: false, ignore });
|
|
780
|
+
} catch (error) {
|
|
781
|
+
logger_default.error("Failed to discover repository files:", error);
|
|
782
|
+
return [];
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
|
|
786
|
+
// src/tree-sitter-analyzer.ts
|
|
787
|
+
var TreeSitterAnalyzer = class {
|
|
788
|
+
parser = new TreeSitterParser();
|
|
789
|
+
symbolExtractor = new SymbolExtractor(this.parser);
|
|
790
|
+
treeRenderer = new TreeRenderer();
|
|
791
|
+
cacheManager;
|
|
792
|
+
initialization;
|
|
793
|
+
queue = Promise.resolve();
|
|
794
|
+
graph;
|
|
795
|
+
revision = -1;
|
|
796
|
+
outputs = /* @__PURE__ */ new Map();
|
|
797
|
+
constructor(options = {}) {
|
|
798
|
+
this.cacheManager = new CacheManager(options.cachePath);
|
|
799
|
+
this.symbolExtractor.setCacheManager(this.cacheManager);
|
|
800
|
+
}
|
|
801
|
+
initialize() {
|
|
802
|
+
this.initialization ??= Promise.all([this.parser.initialize(), this.cacheManager.initialize()]).then(() => void 0);
|
|
803
|
+
return this.initialization;
|
|
804
|
+
}
|
|
805
|
+
getRepoMap(options) {
|
|
806
|
+
const result = this.queue.then(() => this.generateRepoMap(options));
|
|
807
|
+
this.queue = result.catch(() => void 0);
|
|
808
|
+
return result;
|
|
809
|
+
}
|
|
810
|
+
async generateRepoMap(options) {
|
|
811
|
+
options.signal?.throwIfAborted();
|
|
812
|
+
await this.initialize();
|
|
813
|
+
const root = path9.resolve(options.root);
|
|
814
|
+
const normalize = (files2 = /* @__PURE__ */ new Set()) => new Set([...files2].map((file) => path9.relative(root, path9.resolve(root, file)).replaceAll(path9.sep, "/")));
|
|
815
|
+
const mentionedFiles = normalize(options.mentionedFiles);
|
|
816
|
+
const chatFiles = normalize(options.chatFiles);
|
|
817
|
+
const mentionedIdents = options.mentionedIdents ?? /* @__PURE__ */ new Set();
|
|
818
|
+
const files = options.files?.length ? options.files.map((file) => path9.resolve(root, file)) : await getAllFiles(root, options.excludePatterns);
|
|
819
|
+
options.signal?.throwIfAborted();
|
|
820
|
+
const tags = await this.symbolExtractor.extractTags(files, root, options.signal, true);
|
|
821
|
+
if (!tags.length) return "";
|
|
822
|
+
if (this.revision !== this.symbolExtractor.revision) {
|
|
823
|
+
this.graph = new DependencyIndex(tags);
|
|
824
|
+
this.revision = this.symbolExtractor.revision;
|
|
825
|
+
this.outputs.clear();
|
|
826
|
+
}
|
|
827
|
+
if (options.prepareOnly) return "";
|
|
828
|
+
const key = JSON.stringify([[...mentionedFiles].sort(), [...mentionedIdents].sort(), [...chatFiles].sort(), options.maxLines]);
|
|
829
|
+
const cached = this.outputs.get(key);
|
|
830
|
+
if (cached !== void 0) return cached;
|
|
831
|
+
options.signal?.throwIfAborted();
|
|
832
|
+
const ranked = this.graph.rank(mentionedFiles, mentionedIdents);
|
|
833
|
+
options.signal?.throwIfAborted();
|
|
834
|
+
const output = await this.treeRenderer.render(ranked, chatFiles, options.maxLines, root);
|
|
835
|
+
options.signal?.throwIfAborted();
|
|
836
|
+
if (this.outputs.size >= 8) this.outputs.delete(this.outputs.keys().next().value);
|
|
837
|
+
this.outputs.set(key, output);
|
|
838
|
+
return output;
|
|
839
|
+
}
|
|
840
|
+
extractSymbols(filePaths, options) {
|
|
841
|
+
const result = this.queue.then(async () => {
|
|
842
|
+
await this.initialize();
|
|
843
|
+
if (options?.useCache === false) this.symbolExtractor.setCacheManager(null);
|
|
844
|
+
try {
|
|
845
|
+
const tags = await this.symbolExtractor.extractTags(filePaths, options?.rootDir);
|
|
846
|
+
return tags.map((tag) => ({
|
|
847
|
+
name: tag.name,
|
|
848
|
+
kind: tag.kind,
|
|
849
|
+
line: tag.line,
|
|
850
|
+
file: tag.fname,
|
|
851
|
+
relativeFile: tag.rel_fname
|
|
852
|
+
}));
|
|
853
|
+
} finally {
|
|
854
|
+
this.symbolExtractor.setCacheManager(this.cacheManager);
|
|
855
|
+
}
|
|
856
|
+
});
|
|
857
|
+
this.queue = result.catch(() => void 0);
|
|
858
|
+
return result;
|
|
859
|
+
}
|
|
860
|
+
async parseFile(filePath) {
|
|
861
|
+
await this.initialize();
|
|
862
|
+
const { detectLanguage: detectLanguage2 } = await Promise.resolve().then(() => (init_language_detector(), language_detector_exports));
|
|
863
|
+
return {
|
|
864
|
+
tags: await this.parser.parseFile(filePath),
|
|
865
|
+
file: filePath,
|
|
866
|
+
language: detectLanguage2(filePath) || "unknown"
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
async dispose() {
|
|
870
|
+
await this.queue;
|
|
871
|
+
await this.parser.dispose();
|
|
872
|
+
this.cacheManager.close();
|
|
873
|
+
this.graph = void 0;
|
|
874
|
+
this.outputs.clear();
|
|
875
|
+
}
|
|
876
|
+
};
|
|
877
|
+
|
|
878
|
+
// src/repo-map-worker.ts
|
|
879
|
+
var analyzers = /* @__PURE__ */ new Map();
|
|
880
|
+
var configuration = import_worker_threads.workerData;
|
|
881
|
+
import_worker_threads.parentPort.on("message", async (request) => {
|
|
882
|
+
try {
|
|
883
|
+
const root = request.options.root;
|
|
884
|
+
let analyzer = analyzers.get(root);
|
|
885
|
+
if (!analyzer) {
|
|
886
|
+
if (analyzers.size >= 2) {
|
|
887
|
+
const oldest = analyzers.keys().next().value;
|
|
888
|
+
await analyzers.get(oldest).dispose();
|
|
889
|
+
analyzers.delete(oldest);
|
|
890
|
+
}
|
|
891
|
+
analyzer = new TreeSitterAnalyzer(configuration);
|
|
892
|
+
}
|
|
893
|
+
analyzers.delete(root);
|
|
894
|
+
analyzers.set(root, analyzer);
|
|
895
|
+
const output = await analyzer.getRepoMap(request.options);
|
|
896
|
+
import_worker_threads.parentPort.postMessage({
|
|
897
|
+
id: request.id,
|
|
898
|
+
output
|
|
899
|
+
});
|
|
900
|
+
} catch (error) {
|
|
901
|
+
import_worker_threads.parentPort.postMessage({
|
|
902
|
+
id: request.id,
|
|
903
|
+
error: String(error)
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
});
|
|
907
|
+
//# sourceMappingURL=repo-map-worker.js.map
|