pybao-cli 1.3.83 → 1.3.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/REPL-QBOTHGXR.js +47 -0
- package/dist/{acp-OWNA2GOE.js → acp-HZUGOVHE.js} +39 -38
- package/dist/acp-HZUGOVHE.js.map +7 -0
- package/dist/{agentsValidate-UC7SD3JY.js → agentsValidate-O7HQSJAA.js} +10 -8
- package/dist/agentsValidate-O7HQSJAA.js.map +7 -0
- package/dist/{ask-6ESDLRIC.js → ask-PI5RK5UV.js} +32 -27
- package/dist/{ask-6ESDLRIC.js.map → ask-PI5RK5UV.js.map} +1 -1
- package/dist/{autoUpdater-FAKGXOY4.js → autoUpdater-VMLAPV65.js} +3 -3
- package/dist/blockParser-QPLEX5NJ.js +9 -0
- package/dist/{chunk-HMCPTTVR.js → chunk-26JCV35N.js} +3 -3
- package/dist/{chunk-WU4VY7XT.js → chunk-3HDF767A.js} +2 -2
- package/dist/{chunk-BPZ44BIE.js → chunk-63AZKM53.js} +1 -1
- package/dist/chunk-ARPANTL5.js +1834 -0
- package/dist/chunk-ARPANTL5.js.map +7 -0
- package/dist/{chunk-3BP3TGGN.js → chunk-DSILT3OP.js} +1 -1
- package/dist/{chunk-3BP3TGGN.js.map → chunk-DSILT3OP.js.map} +1 -1
- package/dist/{chunk-NNNU5K5N.js → chunk-DXCSQJBL.js} +8 -3
- package/dist/chunk-DXCSQJBL.js.map +7 -0
- package/dist/{chunk-XJDZTRBL.js → chunk-GQQLSBYO.js} +3 -3
- package/dist/{chunk-WCR6QGXF.js → chunk-H4AQENXV.js} +4 -4
- package/dist/{chunk-MFNDDM3V.js → chunk-JLQQYGR3.js} +1 -1
- package/dist/{chunk-NIKESYZF.js → chunk-LSR2E3UK.js} +7 -5
- package/dist/chunk-LSR2E3UK.js.map +7 -0
- package/dist/{chunk-JGJCX75W.js → chunk-MHA6SEN4.js} +41 -13
- package/dist/chunk-MHA6SEN4.js.map +7 -0
- package/dist/{chunk-B3MDT4F6.js → chunk-MNAVHXSW.js} +5 -5
- package/dist/{chunk-SYHWDS75.js → chunk-MNLKFR45.js} +5 -6
- package/dist/chunk-MNLKFR45.js.map +7 -0
- package/dist/{chunk-ZZO2YNJG.js → chunk-NBCFJBBX.js} +5 -4
- package/dist/chunk-NBCFJBBX.js.map +7 -0
- package/dist/{chunk-MSWBIZZC.js → chunk-NIKYII4N.js} +8 -6
- package/dist/chunk-NIKYII4N.js.map +7 -0
- package/dist/{chunk-XC4AJGEW.js → chunk-NPB2GG6W.js} +1 -1
- package/dist/{chunk-FOQKIQ27.js → chunk-OLZLEJQ6.js} +3 -3
- package/dist/{chunk-43ZJOQDX.js → chunk-Q53PYUIA.js} +1127 -1300
- package/dist/chunk-Q53PYUIA.js.map +7 -0
- package/dist/{blockParser-CFQE5IAN.js → chunk-QWIBSCDN.js} +1 -1
- package/dist/{blockParser-CFQE5IAN.js.map → chunk-QWIBSCDN.js.map} +1 -1
- package/dist/{chunk-SSPEL7MF.js → chunk-RZB3QGK6.js} +3 -3
- package/dist/{chunk-6VOYUVQU.js → chunk-S4CSO2ZH.js} +7 -7
- package/dist/chunk-SIEX5WPV.js +364 -0
- package/dist/chunk-SIEX5WPV.js.map +7 -0
- package/dist/{chunk-TNJTMN23.js → chunk-TKDXONVW.js} +1 -1
- package/dist/{chunk-ZEMFBGPE.js → chunk-TPDQPJ6C.js} +19 -15
- package/dist/chunk-TPDQPJ6C.js.map +7 -0
- package/dist/{chunk-HCPU4TQ6.js → chunk-UEU4FXHS.js} +1 -1
- package/dist/{chunk-SWY5TLOX.js → chunk-UF7NCSRO.js} +159 -1232
- package/dist/chunk-UF7NCSRO.js.map +7 -0
- package/dist/{chunk-L62UDJSU.js → chunk-XHAFEGQ4.js} +341 -67
- package/dist/chunk-XHAFEGQ4.js.map +7 -0
- package/dist/chunk-XI4LTVYT.js +17 -0
- package/dist/chunk-XI4LTVYT.js.map +7 -0
- package/dist/chunk-XTZG3RFQ.js +1832 -0
- package/dist/chunk-XTZG3RFQ.js.map +7 -0
- package/dist/{chunk-HIH43UEF.js → chunk-YO4DT6YI.js} +4 -4
- package/dist/chunk-YO4DT6YI.js.map +7 -0
- package/dist/{chunk-FH5VPA4M.js → chunk-YV7IG5XX.js} +1 -1
- package/dist/{chunk-CPOVPGCU.js → chunk-ZBVG3UAU.js} +7 -7
- package/dist/{cli-EASV6IY7.js → cli-5PLJTPHW.js} +206 -91
- package/dist/cli-5PLJTPHW.js.map +7 -0
- package/dist/commands-B2WZOGHH.js +51 -0
- package/dist/{config-53WMBY35.js → config-SHEWOPFK.js} +4 -4
- package/dist/{context-GNSAANSR.js → context-LL2UPKAI.js} +5 -5
- package/dist/{customCommands-TI7KC7QR.js → customCommands-35Z3QFJ7.js} +4 -4
- package/dist/{env-7GAPXLOS.js → env-NQFQMCTK.js} +16 -4
- package/dist/file-KM744MET.js +44 -0
- package/dist/index.js +3 -3
- package/dist/{chunk-FL4UY5X6.js → llm-VLOERBXP.js} +68 -32
- package/dist/llm-VLOERBXP.js.map +7 -0
- package/dist/{llmLazy-JT5FOMWS.js → llmLazy-RBH7MNLY.js} +1 -1
- package/dist/{loader-FVGZ4Q4O.js → loader-7WST3AYZ.js} +4 -4
- package/dist/lsp-OXNZT5ZE.js +17 -0
- package/dist/{lspAnchor-2ESTEKDW.js → lspAnchor-DZZU6UQ6.js} +8 -10
- package/dist/lspAnchor-DZZU6UQ6.js.map +7 -0
- package/dist/{mcp-DXRJFV2Z.js → mcp-X7RYSZ6L.js} +7 -7
- package/dist/{mentionProcessor-24DFMNUM.js → mentionProcessor-ROLDGNA5.js} +5 -5
- package/dist/{messages-44UB3QCF.js → messages-UJS72R2W.js} +1 -1
- package/dist/{model-2MO4JM7C.js → model-V7H3W6Y7.js} +5 -5
- package/dist/{openai-IW2EECJN.js → openai-KGZKMMS6.js} +5 -5
- package/dist/{outputStyles-OFVM7RO6.js → outputStyles-7RUAZ6BO.js} +4 -4
- package/dist/{pluginRuntime-L2ORBVPC.js → pluginRuntime-UZ55REAW.js} +6 -6
- package/dist/{pluginValidation-S5YXDUDC.js → pluginValidation-GTXVXBWB.js} +6 -6
- package/dist/prompts-4VTBEFY3.js +53 -0
- package/dist/{pybAgentSessionLoad-NM2FSUHI.js → pybAgentSessionLoad-4JMXEYFZ.js} +4 -4
- package/dist/{pybAgentSessionResume-R6DFJVOS.js → pybAgentSessionResume-BO24O2NE.js} +4 -4
- package/dist/{pybAgentStreamJsonSession-IYC73GWN.js → pybAgentStreamJsonSession-ZPRBH4EV.js} +1 -1
- package/dist/{pybHooks-BNGLUIBL.js → pybHooks-2PL5ZVBP.js} +4 -4
- package/dist/query-YHFKIZJZ.js +55 -0
- package/dist/registry-6M7CIUP6.js +47 -0
- package/dist/{ripgrep-QZG3RMUD.js → ripgrep-GC4KZFGD.js} +3 -3
- package/dist/{skillMarketplace-IRUF6M5M.js → skillMarketplace-TCNFPAN3.js} +3 -3
- package/dist/{state-LSDUZCQZ.js → state-64M4LRYH.js} +2 -2
- package/dist/{theme-QVGQSEFJ.js → theme-KNA2WSIO.js} +5 -5
- package/dist/{toolPermissionSettings-U4P7K6VA.js → toolPermissionSettings-HIIWLCI7.js} +6 -6
- package/dist/toolPermissionSettings-HIIWLCI7.js.map +7 -0
- package/dist/tools-TIELMHQI.js +52 -0
- package/dist/tools-TIELMHQI.js.map +7 -0
- package/dist/{userInput-EP4A6TDN.js → userInput-LB5SZ6IS.js} +34 -29
- package/dist/{userInput-EP4A6TDN.js.map → userInput-LB5SZ6IS.js.map} +1 -1
- package/package.json +1 -1
- package/dist/REPL-HVMZDW6I.js +0 -42
- package/dist/acp-OWNA2GOE.js.map +0 -7
- package/dist/agentsValidate-UC7SD3JY.js.map +0 -7
- package/dist/chunk-43ZJOQDX.js.map +0 -7
- package/dist/chunk-FL4UY5X6.js.map +0 -7
- package/dist/chunk-HIH43UEF.js.map +0 -7
- package/dist/chunk-JGJCX75W.js.map +0 -7
- package/dist/chunk-L62UDJSU.js.map +0 -7
- package/dist/chunk-MSWBIZZC.js.map +0 -7
- package/dist/chunk-NIKESYZF.js.map +0 -7
- package/dist/chunk-NNNU5K5N.js.map +0 -7
- package/dist/chunk-PBPAGOFN.js +0 -929
- package/dist/chunk-PBPAGOFN.js.map +0 -7
- package/dist/chunk-SWY5TLOX.js.map +0 -7
- package/dist/chunk-SYHWDS75.js.map +0 -7
- package/dist/chunk-ZEMFBGPE.js.map +0 -7
- package/dist/chunk-ZZO2YNJG.js.map +0 -7
- package/dist/cli-EASV6IY7.js.map +0 -7
- package/dist/commands-LHZ4KLGY.js +0 -46
- package/dist/llm-24PI2Z6V.js +0 -80
- package/dist/lspAnchor-2ESTEKDW.js.map +0 -7
- package/dist/manager-B7QOH5CJ.js +0 -13
- package/dist/prompts-7PXTK55Q.js +0 -48
- package/dist/query-OLRMI42V.js +0 -50
- package/dist/tools-LSBTQYU4.js +0 -51
- /package/dist/{REPL-HVMZDW6I.js.map → REPL-QBOTHGXR.js.map} +0 -0
- /package/dist/{autoUpdater-FAKGXOY4.js.map → autoUpdater-VMLAPV65.js.map} +0 -0
- /package/dist/{commands-LHZ4KLGY.js.map → blockParser-QPLEX5NJ.js.map} +0 -0
- /package/dist/{chunk-HMCPTTVR.js.map → chunk-26JCV35N.js.map} +0 -0
- /package/dist/{chunk-WU4VY7XT.js.map → chunk-3HDF767A.js.map} +0 -0
- /package/dist/{chunk-BPZ44BIE.js.map → chunk-63AZKM53.js.map} +0 -0
- /package/dist/{chunk-XJDZTRBL.js.map → chunk-GQQLSBYO.js.map} +0 -0
- /package/dist/{chunk-WCR6QGXF.js.map → chunk-H4AQENXV.js.map} +0 -0
- /package/dist/{chunk-MFNDDM3V.js.map → chunk-JLQQYGR3.js.map} +0 -0
- /package/dist/{chunk-B3MDT4F6.js.map → chunk-MNAVHXSW.js.map} +0 -0
- /package/dist/{chunk-XC4AJGEW.js.map → chunk-NPB2GG6W.js.map} +0 -0
- /package/dist/{chunk-FOQKIQ27.js.map → chunk-OLZLEJQ6.js.map} +0 -0
- /package/dist/{chunk-SSPEL7MF.js.map → chunk-RZB3QGK6.js.map} +0 -0
- /package/dist/{chunk-6VOYUVQU.js.map → chunk-S4CSO2ZH.js.map} +0 -0
- /package/dist/{chunk-TNJTMN23.js.map → chunk-TKDXONVW.js.map} +0 -0
- /package/dist/{chunk-HCPU4TQ6.js.map → chunk-UEU4FXHS.js.map} +0 -0
- /package/dist/{chunk-FH5VPA4M.js.map → chunk-YV7IG5XX.js.map} +0 -0
- /package/dist/{chunk-CPOVPGCU.js.map → chunk-ZBVG3UAU.js.map} +0 -0
- /package/dist/{config-53WMBY35.js.map → commands-B2WZOGHH.js.map} +0 -0
- /package/dist/{context-GNSAANSR.js.map → config-SHEWOPFK.js.map} +0 -0
- /package/dist/{customCommands-TI7KC7QR.js.map → context-LL2UPKAI.js.map} +0 -0
- /package/dist/{env-7GAPXLOS.js.map → customCommands-35Z3QFJ7.js.map} +0 -0
- /package/dist/{llm-24PI2Z6V.js.map → env-NQFQMCTK.js.map} +0 -0
- /package/dist/{llmLazy-JT5FOMWS.js.map → file-KM744MET.js.map} +0 -0
- /package/dist/{loader-FVGZ4Q4O.js.map → llmLazy-RBH7MNLY.js.map} +0 -0
- /package/dist/{manager-B7QOH5CJ.js.map → loader-7WST3AYZ.js.map} +0 -0
- /package/dist/{mcp-DXRJFV2Z.js.map → lsp-OXNZT5ZE.js.map} +0 -0
- /package/dist/{messages-44UB3QCF.js.map → mcp-X7RYSZ6L.js.map} +0 -0
- /package/dist/{mentionProcessor-24DFMNUM.js.map → mentionProcessor-ROLDGNA5.js.map} +0 -0
- /package/dist/{model-2MO4JM7C.js.map → messages-UJS72R2W.js.map} +0 -0
- /package/dist/{openai-IW2EECJN.js.map → model-V7H3W6Y7.js.map} +0 -0
- /package/dist/{outputStyles-OFVM7RO6.js.map → openai-KGZKMMS6.js.map} +0 -0
- /package/dist/{pluginValidation-S5YXDUDC.js.map → outputStyles-7RUAZ6BO.js.map} +0 -0
- /package/dist/{pluginRuntime-L2ORBVPC.js.map → pluginRuntime-UZ55REAW.js.map} +0 -0
- /package/dist/{prompts-7PXTK55Q.js.map → pluginValidation-GTXVXBWB.js.map} +0 -0
- /package/dist/{pybAgentSessionLoad-NM2FSUHI.js.map → prompts-4VTBEFY3.js.map} +0 -0
- /package/dist/{pybAgentSessionResume-R6DFJVOS.js.map → pybAgentSessionLoad-4JMXEYFZ.js.map} +0 -0
- /package/dist/{pybHooks-BNGLUIBL.js.map → pybAgentSessionResume-BO24O2NE.js.map} +0 -0
- /package/dist/{pybAgentStreamJsonSession-IYC73GWN.js.map → pybAgentStreamJsonSession-ZPRBH4EV.js.map} +0 -0
- /package/dist/{query-OLRMI42V.js.map → pybHooks-2PL5ZVBP.js.map} +0 -0
- /package/dist/{ripgrep-QZG3RMUD.js.map → query-YHFKIZJZ.js.map} +0 -0
- /package/dist/{skillMarketplace-IRUF6M5M.js.map → registry-6M7CIUP6.js.map} +0 -0
- /package/dist/{state-LSDUZCQZ.js.map → ripgrep-GC4KZFGD.js.map} +0 -0
- /package/dist/{theme-QVGQSEFJ.js.map → skillMarketplace-TCNFPAN3.js.map} +0 -0
- /package/dist/{toolPermissionSettings-U4P7K6VA.js.map → state-64M4LRYH.js.map} +0 -0
- /package/dist/{tools-LSBTQYU4.js.map → theme-KNA2WSIO.js.map} +0 -0
|
@@ -0,0 +1,1834 @@
|
|
|
1
|
+
import { createRequire as __pybCreateRequire } from "node:module";
|
|
2
|
+
const require = __pybCreateRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
LspServerRegistry,
|
|
5
|
+
findNearestRoot
|
|
6
|
+
} from "./chunk-XTZG3RFQ.js";
|
|
7
|
+
import {
|
|
8
|
+
levenshtein
|
|
9
|
+
} from "./chunk-UZ34JEUK.js";
|
|
10
|
+
import {
|
|
11
|
+
getCwd
|
|
12
|
+
} from "./chunk-XHAFEGQ4.js";
|
|
13
|
+
|
|
14
|
+
// src/lsp/index.ts
|
|
15
|
+
import { extname as extname2 } from "path";
|
|
16
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
17
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
18
|
+
import * as http from "http";
|
|
19
|
+
|
|
20
|
+
// src/tools/search/LspTool/client/generic.ts
|
|
21
|
+
import { createMessageConnection, StreamMessageReader, StreamMessageWriter } from "vscode-jsonrpc/node.js";
|
|
22
|
+
import { spawn } from "child_process";
|
|
23
|
+
import { pathToFileURL, fileURLToPath } from "url";
|
|
24
|
+
import { EventEmitter } from "events";
|
|
25
|
+
var DiagnosticsEventBus = class {
|
|
26
|
+
debounceMs;
|
|
27
|
+
lastEventAt = /* @__PURE__ */ new Map();
|
|
28
|
+
debounceTimers = /* @__PURE__ */ new Map();
|
|
29
|
+
waiting = /* @__PURE__ */ new Map();
|
|
30
|
+
constructor(options) {
|
|
31
|
+
this.debounceMs = options?.debounceMs ?? 150;
|
|
32
|
+
}
|
|
33
|
+
publish(uri) {
|
|
34
|
+
const now = Date.now();
|
|
35
|
+
this.lastEventAt.set(uri, now);
|
|
36
|
+
const existing = this.debounceTimers.get(uri);
|
|
37
|
+
if (existing) {
|
|
38
|
+
clearTimeout(existing);
|
|
39
|
+
}
|
|
40
|
+
const timer = setTimeout(() => {
|
|
41
|
+
this.debounceTimers.delete(uri);
|
|
42
|
+
const waiters = this.waiting.get(uri);
|
|
43
|
+
if (!waiters || waiters.size === 0) return;
|
|
44
|
+
for (const resolver of Array.from(waiters)) {
|
|
45
|
+
resolver();
|
|
46
|
+
}
|
|
47
|
+
}, this.debounceMs);
|
|
48
|
+
this.debounceTimers.set(uri, timer);
|
|
49
|
+
}
|
|
50
|
+
waitForIdle(uri, timeoutMs) {
|
|
51
|
+
const last = this.lastEventAt.get(uri);
|
|
52
|
+
if (last !== void 0 && Date.now() - last >= this.debounceMs) {
|
|
53
|
+
return Promise.resolve();
|
|
54
|
+
}
|
|
55
|
+
return new Promise((resolve) => {
|
|
56
|
+
let done = false;
|
|
57
|
+
const finish = () => {
|
|
58
|
+
if (done) return;
|
|
59
|
+
done = true;
|
|
60
|
+
clearTimeout(timeout);
|
|
61
|
+
const waiters2 = this.waiting.get(uri);
|
|
62
|
+
if (waiters2) {
|
|
63
|
+
waiters2.delete(finish);
|
|
64
|
+
if (waiters2.size === 0) this.waiting.delete(uri);
|
|
65
|
+
}
|
|
66
|
+
resolve();
|
|
67
|
+
};
|
|
68
|
+
const waiters = this.waiting.get(uri) ?? /* @__PURE__ */ new Set();
|
|
69
|
+
waiters.add(finish);
|
|
70
|
+
this.waiting.set(uri, waiters);
|
|
71
|
+
const timeout = setTimeout(() => {
|
|
72
|
+
finish();
|
|
73
|
+
}, timeoutMs);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
var GenericLspClient = class {
|
|
78
|
+
constructor(serverCommand, serverArgs, cwd, rootPath, extraEnv, initializationOptions) {
|
|
79
|
+
this.rootPath = rootPath;
|
|
80
|
+
this.extraEnv = extraEnv;
|
|
81
|
+
this.initializationOptions = initializationOptions;
|
|
82
|
+
const env = {
|
|
83
|
+
...process.env,
|
|
84
|
+
// Remove potentially conflicting variables
|
|
85
|
+
NODE_OPTIONS: void 0,
|
|
86
|
+
TS_NODE_PROJECT: void 0,
|
|
87
|
+
ELECTRON_RUN_AS_NODE: void 0,
|
|
88
|
+
...this.extraEnv
|
|
89
|
+
};
|
|
90
|
+
const options = {
|
|
91
|
+
cwd,
|
|
92
|
+
stdio: "pipe",
|
|
93
|
+
env,
|
|
94
|
+
shell: false
|
|
95
|
+
};
|
|
96
|
+
this.process = spawn(serverCommand, serverArgs, options);
|
|
97
|
+
this.process.stderr.on("data", (data) => {
|
|
98
|
+
console.error(`[LSP Stderr] ${data}`);
|
|
99
|
+
});
|
|
100
|
+
this.connection = createMessageConnection(
|
|
101
|
+
new StreamMessageReader(this.process.stdout),
|
|
102
|
+
new StreamMessageWriter(this.process.stdin)
|
|
103
|
+
);
|
|
104
|
+
this.connection.onNotification((method, params) => {
|
|
105
|
+
if (method === "window/logMessage" || method === "$/progress") {
|
|
106
|
+
console.log(`[LSP Notification] ${method}:`, JSON.stringify(params));
|
|
107
|
+
}
|
|
108
|
+
if (method === "textDocument/publishDiagnostics") {
|
|
109
|
+
try {
|
|
110
|
+
const payload = params;
|
|
111
|
+
const uri = payload.uri ?? "";
|
|
112
|
+
let filePath = uri;
|
|
113
|
+
if (uri.startsWith("file://")) {
|
|
114
|
+
filePath = fileURLToPath(uri);
|
|
115
|
+
}
|
|
116
|
+
const normalizedUri = this.normalizeUri(filePath);
|
|
117
|
+
this.diagnosticsMap.set(normalizedUri, payload.diagnostics ?? []);
|
|
118
|
+
this.diagnosticsBus.publish(normalizedUri);
|
|
119
|
+
this.diagnosticEvents.emit("diagnostics", { uri, filePath, diagnostics: payload.diagnostics ?? [] });
|
|
120
|
+
} catch (e) {
|
|
121
|
+
console.error("[LSP] Error processing diagnostics:", e);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
this.connection.onRequest((method, params) => {
|
|
126
|
+
if (method === "workspace/configuration") {
|
|
127
|
+
const payload = params;
|
|
128
|
+
const items = Array.isArray(payload.items) ? payload.items : [];
|
|
129
|
+
return items.map((item) => {
|
|
130
|
+
const section = String(item?.section ?? "");
|
|
131
|
+
if (section === "yaml" || section.startsWith("yaml.")) {
|
|
132
|
+
return { schemaStore: { enable: false }, schemas: {} };
|
|
133
|
+
}
|
|
134
|
+
return {};
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
if (method === "client/registerCapability") {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
});
|
|
142
|
+
this.connection.listen();
|
|
143
|
+
}
|
|
144
|
+
connection;
|
|
145
|
+
process;
|
|
146
|
+
capabilities;
|
|
147
|
+
diagnosticEvents = new EventEmitter();
|
|
148
|
+
diagnosticsBus = new DiagnosticsEventBus();
|
|
149
|
+
diagnosticsMap = /* @__PURE__ */ new Map();
|
|
150
|
+
normalizeUri(filePath) {
|
|
151
|
+
let uri = pathToFileURL(filePath).href;
|
|
152
|
+
if (process.platform === "win32") {
|
|
153
|
+
uri = uri.replace(/^file:\/\/\/([a-zA-Z]):\//, (match, drive) => {
|
|
154
|
+
return `file:///${drive.toLowerCase()}%3A/`;
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
return uri;
|
|
158
|
+
}
|
|
159
|
+
async initialize() {
|
|
160
|
+
const rootUri = this.normalizeUri(this.rootPath);
|
|
161
|
+
console.log("[GenericLspClient] Initializing with normalized rootUri:", rootUri);
|
|
162
|
+
const params = {
|
|
163
|
+
processId: process.pid,
|
|
164
|
+
rootUri,
|
|
165
|
+
capabilities: {
|
|
166
|
+
textDocument: {
|
|
167
|
+
synchronization: {
|
|
168
|
+
didOpen: true,
|
|
169
|
+
didChange: true,
|
|
170
|
+
willSave: false,
|
|
171
|
+
willSaveWaitUntil: false,
|
|
172
|
+
didSave: true
|
|
173
|
+
},
|
|
174
|
+
hover: {
|
|
175
|
+
contentFormat: ["markdown", "plaintext"]
|
|
176
|
+
},
|
|
177
|
+
definition: {},
|
|
178
|
+
references: {}
|
|
179
|
+
},
|
|
180
|
+
workspace: {
|
|
181
|
+
workspaceFolders: true
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
workspaceFolders: [
|
|
185
|
+
{
|
|
186
|
+
name: "workspace",
|
|
187
|
+
uri: this.normalizeUri(this.rootPath)
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
};
|
|
191
|
+
if (this.initializationOptions && Object.keys(this.initializationOptions).length > 0) {
|
|
192
|
+
params.initializationOptions = this.initializationOptions;
|
|
193
|
+
}
|
|
194
|
+
const result = await this.connection.sendRequest("initialize", params);
|
|
195
|
+
await this.connection.sendNotification("initialized", {});
|
|
196
|
+
await this.connection.sendNotification("workspace/didChangeConfiguration", {
|
|
197
|
+
settings: { yaml: { schemaStore: { enable: false }, schemas: {} } }
|
|
198
|
+
});
|
|
199
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
200
|
+
this.capabilities = result.capabilities;
|
|
201
|
+
return result;
|
|
202
|
+
}
|
|
203
|
+
async shutdown() {
|
|
204
|
+
try {
|
|
205
|
+
await this.connection.sendRequest("shutdown");
|
|
206
|
+
await this.connection.sendNotification("exit");
|
|
207
|
+
} catch (error) {
|
|
208
|
+
}
|
|
209
|
+
this.connection.dispose();
|
|
210
|
+
this.process.kill();
|
|
211
|
+
}
|
|
212
|
+
async goToDefinition(filePath, line, character) {
|
|
213
|
+
const params = {
|
|
214
|
+
textDocument: { uri: this.normalizeUri(filePath) },
|
|
215
|
+
position: { line: line - 1, character: character - 1 }
|
|
216
|
+
};
|
|
217
|
+
return this.connection.sendRequest("textDocument/definition", params);
|
|
218
|
+
}
|
|
219
|
+
async findReferences(filePath, line, character) {
|
|
220
|
+
const params = {
|
|
221
|
+
textDocument: { uri: this.normalizeUri(filePath) },
|
|
222
|
+
position: { line: line - 1, character: character - 1 },
|
|
223
|
+
context: { includeDeclaration: true }
|
|
224
|
+
};
|
|
225
|
+
return this.connection.sendRequest("textDocument/references", params);
|
|
226
|
+
}
|
|
227
|
+
async hover(filePath, line, character) {
|
|
228
|
+
const params = {
|
|
229
|
+
textDocument: { uri: this.normalizeUri(filePath) },
|
|
230
|
+
position: { line: line - 1, character: character - 1 }
|
|
231
|
+
};
|
|
232
|
+
return this.connection.sendRequest("textDocument/hover", params);
|
|
233
|
+
}
|
|
234
|
+
async documentSymbol(filePath) {
|
|
235
|
+
const params = {
|
|
236
|
+
textDocument: { uri: this.normalizeUri(filePath) }
|
|
237
|
+
};
|
|
238
|
+
return this.connection.sendRequest("textDocument/documentSymbol", params);
|
|
239
|
+
}
|
|
240
|
+
async workspaceSymbol(query) {
|
|
241
|
+
const params = {
|
|
242
|
+
query
|
|
243
|
+
};
|
|
244
|
+
return this.connection.sendRequest("workspace/symbol", params);
|
|
245
|
+
}
|
|
246
|
+
async goToImplementation(filePath, line, character) {
|
|
247
|
+
const params = {
|
|
248
|
+
textDocument: { uri: this.normalizeUri(filePath) },
|
|
249
|
+
position: { line: line - 1, character: character - 1 }
|
|
250
|
+
};
|
|
251
|
+
return this.connection.sendRequest("textDocument/implementation", params);
|
|
252
|
+
}
|
|
253
|
+
async prepareCallHierarchy(filePath, line, character) {
|
|
254
|
+
const params = {
|
|
255
|
+
textDocument: { uri: this.normalizeUri(filePath) },
|
|
256
|
+
position: { line: line - 1, character: character - 1 }
|
|
257
|
+
};
|
|
258
|
+
return this.connection.sendRequest("textDocument/prepareCallHierarchy", params);
|
|
259
|
+
}
|
|
260
|
+
async incomingCalls(item) {
|
|
261
|
+
const params = { item };
|
|
262
|
+
return this.connection.sendRequest("callHierarchy/incomingCalls", params);
|
|
263
|
+
}
|
|
264
|
+
async outgoingCalls(item) {
|
|
265
|
+
const params = { item };
|
|
266
|
+
return this.connection.sendRequest("callHierarchy/outgoingCalls", params);
|
|
267
|
+
}
|
|
268
|
+
async didOpen(filePath, content, languageId) {
|
|
269
|
+
const params = {
|
|
270
|
+
textDocument: {
|
|
271
|
+
uri: this.normalizeUri(filePath),
|
|
272
|
+
languageId,
|
|
273
|
+
version: 1,
|
|
274
|
+
text: content
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
await this.connection.sendNotification("textDocument/didOpen", params);
|
|
278
|
+
}
|
|
279
|
+
async didChange(filePath, content, version) {
|
|
280
|
+
const params = {
|
|
281
|
+
textDocument: {
|
|
282
|
+
uri: this.normalizeUri(filePath),
|
|
283
|
+
version
|
|
284
|
+
},
|
|
285
|
+
contentChanges: [{ text: content }]
|
|
286
|
+
};
|
|
287
|
+
await this.connection.sendNotification("textDocument/didChange", params);
|
|
288
|
+
}
|
|
289
|
+
async didClose(filePath) {
|
|
290
|
+
const params = {
|
|
291
|
+
textDocument: {
|
|
292
|
+
uri: this.normalizeUri(filePath)
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
await this.connection.sendNotification("textDocument/didClose", params);
|
|
296
|
+
}
|
|
297
|
+
async waitForReadiness(filePath, timeoutMs = 3e3) {
|
|
298
|
+
const normalizedUri = this.normalizeUri(filePath);
|
|
299
|
+
await this.diagnosticsBus.waitForIdle(normalizedUri, timeoutMs);
|
|
300
|
+
}
|
|
301
|
+
on(event, handler) {
|
|
302
|
+
this.diagnosticEvents.on(event, handler);
|
|
303
|
+
}
|
|
304
|
+
off(event, handler) {
|
|
305
|
+
this.diagnosticEvents.off(event, handler);
|
|
306
|
+
}
|
|
307
|
+
getDiagnostics(filePath) {
|
|
308
|
+
return this.diagnosticsMap.get(this.normalizeUri(filePath)) || [];
|
|
309
|
+
}
|
|
310
|
+
getDiagnosticsSnapshot() {
|
|
311
|
+
const summaries = [];
|
|
312
|
+
for (const [uri, diagnostics] of this.diagnosticsMap.entries()) {
|
|
313
|
+
let filePath = uri;
|
|
314
|
+
try {
|
|
315
|
+
if (uri.startsWith("file://")) {
|
|
316
|
+
filePath = fileURLToPath(uri);
|
|
317
|
+
}
|
|
318
|
+
} catch {
|
|
319
|
+
filePath = uri;
|
|
320
|
+
}
|
|
321
|
+
const summary = {
|
|
322
|
+
uri,
|
|
323
|
+
filePath,
|
|
324
|
+
count: diagnostics.length,
|
|
325
|
+
errors: 0,
|
|
326
|
+
warnings: 0,
|
|
327
|
+
infos: 0,
|
|
328
|
+
hints: 0
|
|
329
|
+
};
|
|
330
|
+
for (const diagnostic of diagnostics) {
|
|
331
|
+
const severity = diagnostic?.severity;
|
|
332
|
+
if (severity === 1) summary.errors += 1;
|
|
333
|
+
else if (severity === 2) summary.warnings += 1;
|
|
334
|
+
else if (severity === 3) summary.infos += 1;
|
|
335
|
+
else if (severity === 4) summary.hints += 1;
|
|
336
|
+
}
|
|
337
|
+
summaries.push(summary);
|
|
338
|
+
}
|
|
339
|
+
return summaries;
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
// src/tools/search/LspTool/client/manager.ts
|
|
344
|
+
import { extname, join, dirname, relative } from "path";
|
|
345
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
346
|
+
import { readdir, readFile } from "fs/promises";
|
|
347
|
+
import { existsSync } from "fs";
|
|
348
|
+
import { minimatch } from "minimatch";
|
|
349
|
+
var LspClientManager = class _LspClientManager {
|
|
350
|
+
static instance;
|
|
351
|
+
static clientFactory = null;
|
|
352
|
+
clients = /* @__PURE__ */ new Map();
|
|
353
|
+
spawning = /* @__PURE__ */ new Map();
|
|
354
|
+
broken = /* @__PURE__ */ new Set();
|
|
355
|
+
constructor() {
|
|
356
|
+
}
|
|
357
|
+
static getInstance() {
|
|
358
|
+
if (!_LspClientManager.instance) {
|
|
359
|
+
_LspClientManager.instance = new _LspClientManager();
|
|
360
|
+
}
|
|
361
|
+
return _LspClientManager.instance;
|
|
362
|
+
}
|
|
363
|
+
static setClientFactoryForTests(factory) {
|
|
364
|
+
_LspClientManager.clientFactory = factory;
|
|
365
|
+
}
|
|
366
|
+
getLanguageId(ext, serverInfo) {
|
|
367
|
+
if (serverInfo?.languageId) return serverInfo.languageId;
|
|
368
|
+
return LspServerRegistry.getInstance().getLanguageIdForExtension(ext);
|
|
369
|
+
}
|
|
370
|
+
async ensureWorkspaceClients(rootPath) {
|
|
371
|
+
try {
|
|
372
|
+
const files = await readdir(rootPath, { withFileTypes: true });
|
|
373
|
+
const extensionMap = /* @__PURE__ */ new Map();
|
|
374
|
+
const fileNames = new Set(files.map((f) => f.name));
|
|
375
|
+
if (fileNames.has("tsconfig.json") || fileNames.has("package.json")) {
|
|
376
|
+
if (!files.some((f) => f.isFile() && /\.(ts|tsx|js|jsx)$/.test(f.name))) {
|
|
377
|
+
try {
|
|
378
|
+
const srcFiles = await readdir(join(rootPath, "src"), { withFileTypes: true });
|
|
379
|
+
const triggerFile = srcFiles.find((f) => f.isFile() && /\.(ts|tsx|js|jsx)$/.test(f.name));
|
|
380
|
+
if (triggerFile) {
|
|
381
|
+
extensionMap.set(extname(triggerFile.name), join(rootPath, "src", triggerFile.name));
|
|
382
|
+
}
|
|
383
|
+
} catch (e) {
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
if (fileNames.has("go.mod")) {
|
|
388
|
+
extensionMap.set(".go", join(rootPath, "go.mod"));
|
|
389
|
+
if (fileNames.has("main.go")) extensionMap.set(".go", join(rootPath, "main.go"));
|
|
390
|
+
}
|
|
391
|
+
if (fileNames.has("Cargo.toml")) {
|
|
392
|
+
try {
|
|
393
|
+
if (existsSync(join(rootPath, "src", "main.rs"))) extensionMap.set(".rs", join(rootPath, "src", "main.rs"));
|
|
394
|
+
else if (existsSync(join(rootPath, "src", "lib.rs"))) extensionMap.set(".rs", join(rootPath, "src", "lib.rs"));
|
|
395
|
+
} catch (e) {
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
for (const file of files) {
|
|
399
|
+
if (file.isFile()) {
|
|
400
|
+
const ext = extname(file.name);
|
|
401
|
+
if (ext && !extensionMap.has(ext)) {
|
|
402
|
+
extensionMap.set(ext, join(rootPath, file.name));
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
for (const [ext, realPath] of extensionMap) {
|
|
407
|
+
const client = await this.getClient(realPath, rootPath);
|
|
408
|
+
if (client) {
|
|
409
|
+
try {
|
|
410
|
+
const content = await readFile(realPath, "utf-8");
|
|
411
|
+
const serverInfo = LspServerRegistry.getInstance().getServerForExtension(ext);
|
|
412
|
+
await client.didOpen(realPath, content, this.getLanguageId(ext, serverInfo ?? void 0));
|
|
413
|
+
await client.waitForReadiness(realPath, 2e3);
|
|
414
|
+
} catch (e) {
|
|
415
|
+
}
|
|
416
|
+
} else {
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
} catch (e) {
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
async getClientForFile(filePath) {
|
|
423
|
+
const rootPath = await this.resolveRootForFile(filePath);
|
|
424
|
+
return this.getClient(filePath, rootPath);
|
|
425
|
+
}
|
|
426
|
+
async resolveRootForFile(filePath) {
|
|
427
|
+
const ext = extname(filePath);
|
|
428
|
+
const serverInfo = LspServerRegistry.getInstance().getServerForExtension(ext);
|
|
429
|
+
if (serverInfo) {
|
|
430
|
+
return this.resolveRootForServer(filePath, serverInfo);
|
|
431
|
+
}
|
|
432
|
+
const cwd = getCwd();
|
|
433
|
+
if (filePath.startsWith(cwd)) {
|
|
434
|
+
return cwd;
|
|
435
|
+
}
|
|
436
|
+
return dirname(filePath);
|
|
437
|
+
}
|
|
438
|
+
async getClient(filePath, rootPath) {
|
|
439
|
+
const ext = extname(filePath);
|
|
440
|
+
const servers = LspServerRegistry.getInstance().getServersForExtension(ext);
|
|
441
|
+
if (servers.length === 0) return null;
|
|
442
|
+
for (const serverInfo of servers) {
|
|
443
|
+
const client = await this.getClientForServer(filePath, serverInfo, rootPath);
|
|
444
|
+
if (client) return client;
|
|
445
|
+
}
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
async getClientsForFile(filePath, rootPath) {
|
|
449
|
+
const ext = extname(filePath);
|
|
450
|
+
const servers = LspServerRegistry.getInstance().getServersForExtension(ext);
|
|
451
|
+
if (servers.length === 0) return [];
|
|
452
|
+
const clients = [];
|
|
453
|
+
for (const serverInfo of servers) {
|
|
454
|
+
const client = await this.getClientForServer(filePath, serverInfo, rootPath);
|
|
455
|
+
if (client) clients.push(client);
|
|
456
|
+
}
|
|
457
|
+
return clients;
|
|
458
|
+
}
|
|
459
|
+
async spawnClient(resolvedRoot, serverInfo, key) {
|
|
460
|
+
const cmd = await serverInfo.getCommand(resolvedRoot);
|
|
461
|
+
if (!cmd) {
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
const safeCwd = /[^\x00-\x7F]/.test(resolvedRoot) ? process.cwd() : resolvedRoot;
|
|
465
|
+
let initOpts = serverInfo.initializationOptions;
|
|
466
|
+
if (serverInfo.id === "pyright" || serverInfo.id === "ty") {
|
|
467
|
+
const { detectPythonVenv } = await import("./registry-6M7CIUP6.js");
|
|
468
|
+
const pythonPath = detectPythonVenv(resolvedRoot);
|
|
469
|
+
if (pythonPath) {
|
|
470
|
+
initOpts = { ...initOpts ?? {}, pythonPath };
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
const nextClient = (_LspClientManager.clientFactory ?? ((...args) => new GenericLspClient(...args)))(
|
|
474
|
+
cmd.command,
|
|
475
|
+
cmd.args,
|
|
476
|
+
safeCwd,
|
|
477
|
+
resolvedRoot,
|
|
478
|
+
serverInfo.env,
|
|
479
|
+
initOpts
|
|
480
|
+
);
|
|
481
|
+
await nextClient.initialize();
|
|
482
|
+
this.clients.set(key, nextClient);
|
|
483
|
+
return nextClient;
|
|
484
|
+
}
|
|
485
|
+
async shutdownAll() {
|
|
486
|
+
for (const client of this.clients.values()) {
|
|
487
|
+
await client.shutdown();
|
|
488
|
+
}
|
|
489
|
+
this.clients.clear();
|
|
490
|
+
this.broken.clear();
|
|
491
|
+
this.spawning.clear();
|
|
492
|
+
}
|
|
493
|
+
async workspaceSymbol(query, rootPath = getCwd(), options) {
|
|
494
|
+
if (this.clients.size === 0) {
|
|
495
|
+
await this.ensureWorkspaceClients(rootPath);
|
|
496
|
+
}
|
|
497
|
+
const results = [];
|
|
498
|
+
for (const client of this.clients.values()) {
|
|
499
|
+
try {
|
|
500
|
+
const symbols = await client.workspaceSymbol(query);
|
|
501
|
+
if (symbols && Array.isArray(symbols)) {
|
|
502
|
+
results.push(...symbols);
|
|
503
|
+
}
|
|
504
|
+
} catch (e) {
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
if (results.length === 0 && query.length > 0) {
|
|
508
|
+
for (const client of this.clients.values()) {
|
|
509
|
+
try {
|
|
510
|
+
const broadSymbols = await client.workspaceSymbol("");
|
|
511
|
+
if (broadSymbols && Array.isArray(broadSymbols)) {
|
|
512
|
+
for (const sym of broadSymbols) {
|
|
513
|
+
const dist = levenshtein(query, sym.name);
|
|
514
|
+
if (dist <= 2 || dist <= query.length * 0.3) {
|
|
515
|
+
results.push(sym);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
} catch (e) {
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
const normalizedResults = results.map((sym) => {
|
|
524
|
+
if (!sym || typeof sym !== "object") return sym;
|
|
525
|
+
if (!sym.range && sym.location?.range) {
|
|
526
|
+
sym.range = sym.location.range;
|
|
527
|
+
}
|
|
528
|
+
if (!sym.uri && sym.location?.uri) {
|
|
529
|
+
sym.uri = sym.location.uri;
|
|
530
|
+
}
|
|
531
|
+
return sym;
|
|
532
|
+
});
|
|
533
|
+
const resolveFilePath = (uri) => {
|
|
534
|
+
try {
|
|
535
|
+
if (uri.startsWith("file://")) {
|
|
536
|
+
return fileURLToPath2(uri);
|
|
537
|
+
}
|
|
538
|
+
} catch {
|
|
539
|
+
}
|
|
540
|
+
return uri;
|
|
541
|
+
};
|
|
542
|
+
const flattenSymbols = (items, output = []) => {
|
|
543
|
+
for (const item of items) {
|
|
544
|
+
if (!item || typeof item !== "object") continue;
|
|
545
|
+
output.push(item);
|
|
546
|
+
if (Array.isArray(item.children) && item.children.length > 0) {
|
|
547
|
+
flattenSymbols(item.children, output);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
return output;
|
|
551
|
+
};
|
|
552
|
+
const missingRange = normalizedResults.filter(
|
|
553
|
+
(sym) => sym && !sym.range && typeof sym.uri === "string" && sym.uri.length > 0
|
|
554
|
+
);
|
|
555
|
+
if (missingRange.length > 0) {
|
|
556
|
+
const byUri = /* @__PURE__ */ new Map();
|
|
557
|
+
for (const sym of missingRange) {
|
|
558
|
+
if (!byUri.has(sym.uri)) byUri.set(sym.uri, []);
|
|
559
|
+
byUri.get(sym.uri).push(sym);
|
|
560
|
+
}
|
|
561
|
+
const documentCache = /* @__PURE__ */ new Map();
|
|
562
|
+
for (const [uri, symbols] of byUri.entries()) {
|
|
563
|
+
const filePath = resolveFilePath(uri);
|
|
564
|
+
let documentSymbols = documentCache.get(filePath);
|
|
565
|
+
if (!documentSymbols) {
|
|
566
|
+
documentSymbols = await this.documentSymbol(filePath, rootPath) ?? [];
|
|
567
|
+
documentCache.set(filePath, documentSymbols);
|
|
568
|
+
}
|
|
569
|
+
const flat = flattenSymbols(documentSymbols);
|
|
570
|
+
for (const sym of symbols) {
|
|
571
|
+
let match = flat.find(
|
|
572
|
+
(item) => item?.name === sym.name && (sym.kind === void 0 || item?.kind === sym.kind)
|
|
573
|
+
);
|
|
574
|
+
if (!match) {
|
|
575
|
+
match = flat.find((item) => item?.name === sym.name);
|
|
576
|
+
}
|
|
577
|
+
const range = match?.range ?? match?.location?.range;
|
|
578
|
+
if (range) {
|
|
579
|
+
sym.range = range;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
const finalResults = normalizedResults.filter((sym) => sym && sym.range && sym.name);
|
|
585
|
+
const seen = /* @__PURE__ */ new Set();
|
|
586
|
+
const deduped = [];
|
|
587
|
+
for (const sym of finalResults) {
|
|
588
|
+
const uri = sym?.location?.uri ?? sym?.uri ?? "";
|
|
589
|
+
const line = sym?.range?.start?.line ?? sym?.location?.range?.start?.line ?? "";
|
|
590
|
+
const character = sym?.range?.start?.character ?? sym?.location?.range?.start?.character ?? "";
|
|
591
|
+
const kind = sym?.kind ?? "";
|
|
592
|
+
const key = `${sym?.name ?? ""}|${kind}|${uri}|${line}|${character}`;
|
|
593
|
+
if (!seen.has(key)) {
|
|
594
|
+
seen.add(key);
|
|
595
|
+
deduped.push(sym);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
let filtered = deduped;
|
|
599
|
+
if (options?.filterKinds && options.filterKinds.length > 0) {
|
|
600
|
+
const allowedKinds = new Set(options.filterKinds);
|
|
601
|
+
filtered = filtered.filter((sym) => allowedKinds.has(sym?.kind));
|
|
602
|
+
}
|
|
603
|
+
if (options?.nameIncludes) {
|
|
604
|
+
const needle = options.nameIncludes.toLowerCase();
|
|
605
|
+
filtered = filtered.filter(
|
|
606
|
+
(sym) => String(sym?.name ?? "").toLowerCase().includes(needle)
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
if (options?.maxItems !== void 0 && filtered.length > options.maxItems) {
|
|
610
|
+
filtered = filtered.slice(0, options.maxItems);
|
|
611
|
+
}
|
|
612
|
+
return filtered;
|
|
613
|
+
}
|
|
614
|
+
async documentSymbol(filePath, rootPath = getCwd()) {
|
|
615
|
+
const client = await this.getClient(filePath, rootPath);
|
|
616
|
+
if (!client) {
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
619
|
+
try {
|
|
620
|
+
const content = await readFile(filePath, "utf-8");
|
|
621
|
+
const ext = extname(filePath);
|
|
622
|
+
const serverInfo = LspServerRegistry.getInstance().getServerForExtension(ext);
|
|
623
|
+
const languageId = this.getLanguageId(ext, serverInfo ?? void 0);
|
|
624
|
+
await client.didOpen(filePath, content, languageId);
|
|
625
|
+
await client.waitForReadiness(filePath, 2e3);
|
|
626
|
+
const symbols = await client.documentSymbol(filePath);
|
|
627
|
+
return Array.isArray(symbols) ? symbols : [];
|
|
628
|
+
} catch (e) {
|
|
629
|
+
console.error(`Failed to get document symbols for ${filePath}:`, e);
|
|
630
|
+
return null;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
async getDiagnostics(filePath, rootPath = getCwd()) {
|
|
634
|
+
const client = await this.getClient(filePath, rootPath);
|
|
635
|
+
if (!client) {
|
|
636
|
+
return [];
|
|
637
|
+
}
|
|
638
|
+
return client.getDiagnostics(filePath);
|
|
639
|
+
}
|
|
640
|
+
async getAllDiagnostics(filePath, rootPath = getCwd()) {
|
|
641
|
+
await this.getClientsForFile(filePath, rootPath);
|
|
642
|
+
const results = [];
|
|
643
|
+
for (const client of this.clients.values()) {
|
|
644
|
+
try {
|
|
645
|
+
const diagnostics = client.getDiagnostics(filePath);
|
|
646
|
+
if (diagnostics && diagnostics.length > 0) {
|
|
647
|
+
results.push(...diagnostics);
|
|
648
|
+
}
|
|
649
|
+
} catch (e) {
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
return results;
|
|
653
|
+
}
|
|
654
|
+
status() {
|
|
655
|
+
const diagnostics = [];
|
|
656
|
+
for (const [key, client] of this.clients.entries()) {
|
|
657
|
+
const snapshot = client.getDiagnosticsSnapshot?.();
|
|
658
|
+
if (Array.isArray(snapshot) && snapshot.length > 0) {
|
|
659
|
+
diagnostics.push({ client: key, entries: snapshot });
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
return {
|
|
663
|
+
clients: Array.from(this.clients.keys()),
|
|
664
|
+
spawning: Array.from(this.spawning.keys()),
|
|
665
|
+
broken: Array.from(this.broken.values()),
|
|
666
|
+
diagnostics
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
// New helper to expose waitForReadiness to clients who only have a manager
|
|
670
|
+
async waitForReadiness(filePath, rootPath = getCwd(), timeoutMs = 2e3) {
|
|
671
|
+
const client = await this.getClientForFile(filePath);
|
|
672
|
+
if (client) {
|
|
673
|
+
await client.waitForReadiness(filePath, timeoutMs);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
matchesServerFilters(filePath, serverInfo) {
|
|
677
|
+
const normalizePath = (value) => value.replace(/\\/g, "/");
|
|
678
|
+
const normalizedFile = normalizePath(filePath);
|
|
679
|
+
const relativeToCwd = normalizePath(relative(getCwd(), filePath));
|
|
680
|
+
const matches = (patterns) => patterns.some((pattern) => {
|
|
681
|
+
if (minimatch(normalizedFile, pattern, { dot: true })) return true;
|
|
682
|
+
if (relativeToCwd && !relativeToCwd.startsWith("..")) {
|
|
683
|
+
return minimatch(relativeToCwd, pattern, { dot: true });
|
|
684
|
+
}
|
|
685
|
+
return false;
|
|
686
|
+
});
|
|
687
|
+
if (serverInfo.rootExclude && matches(serverInfo.rootExclude)) return false;
|
|
688
|
+
if (serverInfo.rootInclude && !matches(serverInfo.rootInclude)) return false;
|
|
689
|
+
return true;
|
|
690
|
+
}
|
|
691
|
+
async resolveRootForServer(filePath, serverInfo) {
|
|
692
|
+
const cwd = getCwd();
|
|
693
|
+
let defaultRoot;
|
|
694
|
+
if (serverInfo.rootMarkers.length > 0) {
|
|
695
|
+
defaultRoot = await findNearestRoot(dirname(filePath), serverInfo.rootMarkers, {
|
|
696
|
+
include: serverInfo.rootInclude,
|
|
697
|
+
exclude: serverInfo.rootExclude,
|
|
698
|
+
branches: serverInfo.rootBranches,
|
|
699
|
+
filePath
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
if (!defaultRoot) {
|
|
703
|
+
defaultRoot = filePath.startsWith(cwd) ? cwd : dirname(filePath);
|
|
704
|
+
}
|
|
705
|
+
if (serverInfo.resolveRoot) {
|
|
706
|
+
const resolved = await serverInfo.resolveRoot(filePath, defaultRoot);
|
|
707
|
+
return resolved || defaultRoot;
|
|
708
|
+
}
|
|
709
|
+
return defaultRoot;
|
|
710
|
+
}
|
|
711
|
+
async getClientForServer(filePath, serverInfo, rootPath) {
|
|
712
|
+
if (!this.matchesServerFilters(filePath, serverInfo)) {
|
|
713
|
+
return null;
|
|
714
|
+
}
|
|
715
|
+
const resolvedRoot = rootPath ?? await this.resolveRootForServer(filePath, serverInfo);
|
|
716
|
+
try {
|
|
717
|
+
const ready = await serverInfo.prepare();
|
|
718
|
+
if (!ready) {
|
|
719
|
+
return null;
|
|
720
|
+
}
|
|
721
|
+
} catch (e) {
|
|
722
|
+
return null;
|
|
723
|
+
}
|
|
724
|
+
const key = `${resolvedRoot}:${serverInfo.id}`;
|
|
725
|
+
if (this.broken.has(key)) {
|
|
726
|
+
return null;
|
|
727
|
+
}
|
|
728
|
+
const existingSpawn = this.spawning.get(key);
|
|
729
|
+
if (existingSpawn) {
|
|
730
|
+
return existingSpawn;
|
|
731
|
+
}
|
|
732
|
+
let client = this.clients.get(key);
|
|
733
|
+
if (!client) {
|
|
734
|
+
const spawnPromise = (async () => {
|
|
735
|
+
try {
|
|
736
|
+
return await this.spawnClient(resolvedRoot, serverInfo, key);
|
|
737
|
+
} catch (error) {
|
|
738
|
+
this.broken.add(key);
|
|
739
|
+
return null;
|
|
740
|
+
} finally {
|
|
741
|
+
this.spawning.delete(key);
|
|
742
|
+
}
|
|
743
|
+
})();
|
|
744
|
+
this.spawning.set(key, spawnPromise);
|
|
745
|
+
client = await spawnPromise;
|
|
746
|
+
}
|
|
747
|
+
return client;
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
|
|
751
|
+
// src/tools/search/LspTool/client/formatters.ts
|
|
752
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
753
|
+
import { relative as relative2 } from "path";
|
|
754
|
+
var SYMBOL_KIND_MAP = {
|
|
755
|
+
1: "File",
|
|
756
|
+
2: "Module",
|
|
757
|
+
3: "Namespace",
|
|
758
|
+
4: "Package",
|
|
759
|
+
5: "Class",
|
|
760
|
+
6: "Method",
|
|
761
|
+
7: "Property",
|
|
762
|
+
8: "Field",
|
|
763
|
+
9: "Constructor",
|
|
764
|
+
10: "Enum",
|
|
765
|
+
11: "Interface",
|
|
766
|
+
12: "Function",
|
|
767
|
+
13: "Variable",
|
|
768
|
+
14: "Constant",
|
|
769
|
+
15: "String",
|
|
770
|
+
16: "Number",
|
|
771
|
+
17: "Boolean",
|
|
772
|
+
18: "Array",
|
|
773
|
+
19: "Object",
|
|
774
|
+
20: "Key",
|
|
775
|
+
21: "Null",
|
|
776
|
+
22: "EnumMember",
|
|
777
|
+
23: "Struct",
|
|
778
|
+
24: "Event",
|
|
779
|
+
25: "Operator",
|
|
780
|
+
26: "TypeParameter"
|
|
781
|
+
};
|
|
782
|
+
function toProjectRelative(filePath) {
|
|
783
|
+
const cwd = getCwd();
|
|
784
|
+
try {
|
|
785
|
+
const rel = relative2(cwd, filePath);
|
|
786
|
+
if (!rel || rel === "") return filePath;
|
|
787
|
+
if (rel.startsWith("..")) return filePath;
|
|
788
|
+
return rel;
|
|
789
|
+
} catch {
|
|
790
|
+
return filePath;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
function formatLocation(uri, range) {
|
|
794
|
+
const filePath = fileURLToPath3(uri);
|
|
795
|
+
return `${toProjectRelative(filePath)}:${range.start.line + 1}:${range.start.character + 1}`;
|
|
796
|
+
}
|
|
797
|
+
function getSymbolKindName(kind) {
|
|
798
|
+
if (typeof kind === "number") return SYMBOL_KIND_MAP[kind] || `Kind(${kind})`;
|
|
799
|
+
if (typeof kind === "string") return kind;
|
|
800
|
+
return "Unknown";
|
|
801
|
+
}
|
|
802
|
+
function formatCallHierarchyItem(item) {
|
|
803
|
+
const kind = getSymbolKindName(item?.kind);
|
|
804
|
+
const range = item?.selectionRange ?? item?.range;
|
|
805
|
+
if (item?.uri && range) {
|
|
806
|
+
return {
|
|
807
|
+
label: `${item.name} (${kind}) at ${formatLocation(item.uri, range)}`,
|
|
808
|
+
uri: item.uri
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
if (item?.uri) {
|
|
812
|
+
return {
|
|
813
|
+
label: `${item.name} (${kind}) at ${toProjectRelative(fileURLToPath3(item.uri))}`,
|
|
814
|
+
uri: item.uri
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
return { label: `${item?.name ?? "Unknown"} (${kind})` };
|
|
818
|
+
}
|
|
819
|
+
function formatCallRanges(uri, ranges) {
|
|
820
|
+
const valid = Array.isArray(ranges) ? ranges : [];
|
|
821
|
+
if (valid.length === 0) return "";
|
|
822
|
+
return valid.map((range) => formatLocation(uri, range)).filter(Boolean).join(", ");
|
|
823
|
+
}
|
|
824
|
+
function formatGenericDefinitionResult(result) {
|
|
825
|
+
if (!result) {
|
|
826
|
+
return { formatted: "No definition found.", resultCount: 0, fileCount: 0 };
|
|
827
|
+
}
|
|
828
|
+
const locations = Array.isArray(result) ? result : [result];
|
|
829
|
+
if (locations.length === 0) {
|
|
830
|
+
return { formatted: "No definition found.", resultCount: 0, fileCount: 0 };
|
|
831
|
+
}
|
|
832
|
+
const fileCount = new Set(locations.map((l) => l.uri)).size;
|
|
833
|
+
if (locations.length === 1) {
|
|
834
|
+
return {
|
|
835
|
+
formatted: `Defined in ${formatLocation(locations[0].uri, locations[0].range)}`,
|
|
836
|
+
resultCount: 1,
|
|
837
|
+
fileCount
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
return {
|
|
841
|
+
formatted: `Found ${locations.length} definitions:
|
|
842
|
+
${locations.map((l) => ` ${formatLocation(l.uri, l.range)}`).join("\n")}`,
|
|
843
|
+
resultCount: locations.length,
|
|
844
|
+
fileCount
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
function formatGenericReferencesResult(result) {
|
|
848
|
+
if (!result || !Array.isArray(result) || result.length === 0) {
|
|
849
|
+
return { formatted: "No references found.", resultCount: 0, fileCount: 0 };
|
|
850
|
+
}
|
|
851
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
852
|
+
for (const ref of result) {
|
|
853
|
+
const file = toProjectRelative(fileURLToPath3(ref.uri));
|
|
854
|
+
if (!grouped.has(file)) grouped.set(file, []);
|
|
855
|
+
grouped.get(file).push(ref);
|
|
856
|
+
}
|
|
857
|
+
const lines = [`Found ${result.length} references across ${grouped.size} files:`];
|
|
858
|
+
for (const [file, refs] of grouped) {
|
|
859
|
+
lines.push(`
|
|
860
|
+
${file}:`);
|
|
861
|
+
for (const ref of refs) {
|
|
862
|
+
lines.push(` Line ${ref.range.start.line + 1}:${ref.range.start.character + 1}`);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
return {
|
|
866
|
+
formatted: lines.join("\n"),
|
|
867
|
+
resultCount: result.length,
|
|
868
|
+
fileCount: grouped.size
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
function formatGenericHoverResult(result, line, character) {
|
|
872
|
+
if (!result || !result.contents) {
|
|
873
|
+
return { formatted: "No hover info.", resultCount: 0, fileCount: 0 };
|
|
874
|
+
}
|
|
875
|
+
let contents = "";
|
|
876
|
+
if (typeof result.contents === "string") {
|
|
877
|
+
contents = result.contents;
|
|
878
|
+
} else if (Array.isArray(result.contents)) {
|
|
879
|
+
contents = result.contents.map((c) => typeof c === "string" ? c : c.value).join("\n");
|
|
880
|
+
} else {
|
|
881
|
+
contents = result.contents.value;
|
|
882
|
+
}
|
|
883
|
+
return {
|
|
884
|
+
formatted: `Hover info at ${line}:${character}:
|
|
885
|
+
|
|
886
|
+
${contents}`,
|
|
887
|
+
resultCount: 1,
|
|
888
|
+
fileCount: 1
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
function formatGenericDocumentSymbolResult(result) {
|
|
892
|
+
if (!result || !Array.isArray(result) || result.length === 0) {
|
|
893
|
+
return { formatted: "No symbols found.", resultCount: 0, fileCount: 0 };
|
|
894
|
+
}
|
|
895
|
+
const lines = result.map((s) => {
|
|
896
|
+
const kind = getSymbolKindName(s.kind);
|
|
897
|
+
return `${s.name} (${kind}) at ${s.range.start.line + 1}:${s.range.start.character + 1}`;
|
|
898
|
+
});
|
|
899
|
+
return {
|
|
900
|
+
formatted: ["Document symbols:", ...lines].join("\n"),
|
|
901
|
+
resultCount: result.length,
|
|
902
|
+
fileCount: 1
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
function formatGenericDiagnosticsResult(diagnostics) {
|
|
906
|
+
if (!diagnostics || diagnostics.length === 0) {
|
|
907
|
+
return { formatted: "No diagnostics found.", resultCount: 0, fileCount: 0 };
|
|
908
|
+
}
|
|
909
|
+
const lines = diagnostics.map((d) => {
|
|
910
|
+
return `[${d.severity === 1 ? "Error" : d.severity === 2 ? "Warning" : "Info"}] Line ${d.range.start.line + 1}: ${d.message}`;
|
|
911
|
+
});
|
|
912
|
+
return {
|
|
913
|
+
formatted: ["Diagnostics:", ...lines].join("\n"),
|
|
914
|
+
resultCount: diagnostics.length,
|
|
915
|
+
fileCount: 1
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
function formatGenericCallHierarchyItemsResult(items) {
|
|
919
|
+
if (!items || !Array.isArray(items) || items.length === 0) {
|
|
920
|
+
return { formatted: "No call hierarchy items found.", resultCount: 0, fileCount: 0 };
|
|
921
|
+
}
|
|
922
|
+
const uris = /* @__PURE__ */ new Set();
|
|
923
|
+
const lines = items.map((item) => {
|
|
924
|
+
const info = formatCallHierarchyItem(item);
|
|
925
|
+
if (info.uri) uris.add(info.uri);
|
|
926
|
+
return `- ${info.label}`;
|
|
927
|
+
});
|
|
928
|
+
return {
|
|
929
|
+
formatted: ["Call hierarchy items:", ...lines].join("\n"),
|
|
930
|
+
resultCount: items.length,
|
|
931
|
+
fileCount: uris.size
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
function formatGenericIncomingCallsResult(calls) {
|
|
935
|
+
if (!calls || !Array.isArray(calls) || calls.length === 0) {
|
|
936
|
+
return { formatted: "No incoming calls found.", resultCount: 0, fileCount: 0 };
|
|
937
|
+
}
|
|
938
|
+
const uris = /* @__PURE__ */ new Set();
|
|
939
|
+
const lines = ["Incoming calls:"];
|
|
940
|
+
for (const call of calls) {
|
|
941
|
+
const info = formatCallHierarchyItem(call?.from);
|
|
942
|
+
if (info.uri) uris.add(info.uri);
|
|
943
|
+
lines.push(`- ${info.label}`);
|
|
944
|
+
if (call?.from?.uri) {
|
|
945
|
+
const ranges = formatCallRanges(call.from.uri, call?.fromRanges ?? []);
|
|
946
|
+
if (ranges) {
|
|
947
|
+
lines.push(` via ${ranges}`);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
return {
|
|
952
|
+
formatted: lines.join("\n"),
|
|
953
|
+
resultCount: calls.length,
|
|
954
|
+
fileCount: uris.size
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
function formatGenericOutgoingCallsResult(calls) {
|
|
958
|
+
if (!calls || !Array.isArray(calls) || calls.length === 0) {
|
|
959
|
+
return { formatted: "No outgoing calls found.", resultCount: 0, fileCount: 0 };
|
|
960
|
+
}
|
|
961
|
+
const uris = /* @__PURE__ */ new Set();
|
|
962
|
+
const lines = ["Outgoing calls:"];
|
|
963
|
+
for (const call of calls) {
|
|
964
|
+
const info = formatCallHierarchyItem(call?.to);
|
|
965
|
+
if (info.uri) uris.add(info.uri);
|
|
966
|
+
lines.push(`- ${info.label}`);
|
|
967
|
+
if (call?.to?.uri) {
|
|
968
|
+
const ranges = formatCallRanges(call.to.uri, call?.fromRanges ?? []);
|
|
969
|
+
if (ranges) {
|
|
970
|
+
lines.push(` via ${ranges}`);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
return {
|
|
975
|
+
formatted: lines.join("\n"),
|
|
976
|
+
resultCount: calls.length,
|
|
977
|
+
fileCount: uris.size
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// src/lsp/formatters.ts
|
|
982
|
+
var severityOrder = {
|
|
983
|
+
1: 1,
|
|
984
|
+
2: 2,
|
|
985
|
+
3: 3,
|
|
986
|
+
4: 4
|
|
987
|
+
};
|
|
988
|
+
function severityLabel(severity) {
|
|
989
|
+
if (severity === 1) return "Error";
|
|
990
|
+
if (severity === 2) return "Warning";
|
|
991
|
+
if (severity === 3) return "Info";
|
|
992
|
+
if (severity === 4) return "Hint";
|
|
993
|
+
return "Unknown";
|
|
994
|
+
}
|
|
995
|
+
function formatDiagnosticsPretty(diagnostics, options = {}) {
|
|
996
|
+
if (!diagnostics || diagnostics.length === 0) return "";
|
|
997
|
+
const items = [...diagnostics].sort((a, b) => {
|
|
998
|
+
const aSeverity = severityOrder[a?.severity ?? 99] ?? 99;
|
|
999
|
+
const bSeverity = severityOrder[b?.severity ?? 99] ?? 99;
|
|
1000
|
+
if (aSeverity !== bSeverity) return aSeverity - bSeverity;
|
|
1001
|
+
const aLine = a?.range?.start?.line ?? 0;
|
|
1002
|
+
const bLine = b?.range?.start?.line ?? 0;
|
|
1003
|
+
if (aLine !== bLine) return aLine - bLine;
|
|
1004
|
+
const aChar = a?.range?.start?.character ?? 0;
|
|
1005
|
+
const bChar = b?.range?.start?.character ?? 0;
|
|
1006
|
+
return aChar - bChar;
|
|
1007
|
+
});
|
|
1008
|
+
const limited = options.maxItems && options.maxItems > 0 ? items.slice(0, options.maxItems) : items;
|
|
1009
|
+
return limited.map((d) => {
|
|
1010
|
+
const line = (d?.range?.start?.line ?? 0) + 1;
|
|
1011
|
+
const character = (d?.range?.start?.character ?? 0) + 1;
|
|
1012
|
+
const label = severityLabel(d?.severity);
|
|
1013
|
+
const message = d?.message ?? "Unknown diagnostic";
|
|
1014
|
+
return `- ${label} ${line}:${character} ${message}`;
|
|
1015
|
+
}).join("\n");
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
// src/utils/tree-sitter/registry.ts
|
|
1019
|
+
import path from "path";
|
|
1020
|
+
var ParserRegistry = class {
|
|
1021
|
+
static extensionMap = {
|
|
1022
|
+
".ts": "typescript",
|
|
1023
|
+
".tsx": "tsx",
|
|
1024
|
+
".js": "typescript",
|
|
1025
|
+
// Use TS parser for JS
|
|
1026
|
+
".jsx": "tsx",
|
|
1027
|
+
".py": "python",
|
|
1028
|
+
".sh": "bash",
|
|
1029
|
+
".bash": "bash",
|
|
1030
|
+
".rs": "rust",
|
|
1031
|
+
".go": "go",
|
|
1032
|
+
".java": "java",
|
|
1033
|
+
".cpp": "cpp",
|
|
1034
|
+
".c": "c",
|
|
1035
|
+
".h": "c",
|
|
1036
|
+
".cc": "cpp",
|
|
1037
|
+
".cs": "c_sharp",
|
|
1038
|
+
".scala": "scala",
|
|
1039
|
+
".sbt": "scala",
|
|
1040
|
+
".sc": "scala",
|
|
1041
|
+
".hs": "haskell",
|
|
1042
|
+
".lhs": "haskell",
|
|
1043
|
+
".jl": "julia",
|
|
1044
|
+
".ml": "ocaml",
|
|
1045
|
+
".mli": "ocaml",
|
|
1046
|
+
".swift": "swift",
|
|
1047
|
+
".nix": "nix",
|
|
1048
|
+
".rb": "ruby",
|
|
1049
|
+
".html": "html",
|
|
1050
|
+
".htm": "html",
|
|
1051
|
+
".css": "css",
|
|
1052
|
+
".json": "json",
|
|
1053
|
+
".jsonc": "json",
|
|
1054
|
+
".yaml": "yaml",
|
|
1055
|
+
".yml": "yaml"
|
|
1056
|
+
};
|
|
1057
|
+
/**
|
|
1058
|
+
* Get the supported language ID for a given filename.
|
|
1059
|
+
* Returns null if the extension is not supported.
|
|
1060
|
+
*/
|
|
1061
|
+
static getLanguage(filename) {
|
|
1062
|
+
const ext = path.extname(filename).toLowerCase();
|
|
1063
|
+
return this.extensionMap[ext] || null;
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* Register or override a file extension mapping.
|
|
1067
|
+
*/
|
|
1068
|
+
static registerExtension(ext, lang) {
|
|
1069
|
+
const normalizedExt = ext.startsWith(".") ? ext : `.${ext}`;
|
|
1070
|
+
this.extensionMap[normalizedExt.toLowerCase()] = lang;
|
|
1071
|
+
}
|
|
1072
|
+
};
|
|
1073
|
+
|
|
1074
|
+
// src/utils/tree-sitter/loader.ts
|
|
1075
|
+
import { Parser, Language } from "web-tree-sitter";
|
|
1076
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1077
|
+
import path2 from "path";
|
|
1078
|
+
var getDirname = () => {
|
|
1079
|
+
try {
|
|
1080
|
+
return __dirname;
|
|
1081
|
+
} catch {
|
|
1082
|
+
return path2.dirname(fileURLToPath4(import.meta.url));
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
var _dirname = getDirname();
|
|
1086
|
+
var parserCache = /* @__PURE__ */ new Map();
|
|
1087
|
+
var isInitialized = false;
|
|
1088
|
+
var LANGUAGE_MAP = {
|
|
1089
|
+
bash: "tree-sitter-bash.wasm",
|
|
1090
|
+
typescript: "tree-sitter-typescript.wasm",
|
|
1091
|
+
tsx: "tree-sitter-tsx.wasm",
|
|
1092
|
+
python: "tree-sitter-python.wasm",
|
|
1093
|
+
rust: "tree-sitter-rust.wasm",
|
|
1094
|
+
go: "tree-sitter-go.wasm",
|
|
1095
|
+
java: "tree-sitter-java.wasm",
|
|
1096
|
+
cpp: "tree-sitter-cpp.wasm",
|
|
1097
|
+
c: "tree-sitter-c.wasm",
|
|
1098
|
+
c_sharp: "tree-sitter-c_sharp.wasm",
|
|
1099
|
+
scala: "tree-sitter-scala.wasm",
|
|
1100
|
+
haskell: "tree-sitter-haskell.wasm",
|
|
1101
|
+
julia: "tree-sitter-julia.wasm",
|
|
1102
|
+
ocaml: "tree-sitter-ocaml.wasm",
|
|
1103
|
+
swift: "tree-sitter-swift.wasm",
|
|
1104
|
+
nix: "tree-sitter-nix.wasm",
|
|
1105
|
+
ruby: "tree-sitter-ruby.wasm",
|
|
1106
|
+
html: "tree-sitter-html.wasm",
|
|
1107
|
+
css: "tree-sitter-css.wasm",
|
|
1108
|
+
json: "tree-sitter-json.wasm",
|
|
1109
|
+
yaml: "tree-sitter-yaml.wasm"
|
|
1110
|
+
};
|
|
1111
|
+
async function ensureInitialized() {
|
|
1112
|
+
if (isInitialized) return;
|
|
1113
|
+
let resourcesDir;
|
|
1114
|
+
if (_dirname.includes("dist") || _dirname.includes("node_modules")) {
|
|
1115
|
+
resourcesDir = path2.resolve(_dirname, "../resources/tree-sitter");
|
|
1116
|
+
} else {
|
|
1117
|
+
resourcesDir = path2.resolve(_dirname, "../../../resources/tree-sitter");
|
|
1118
|
+
}
|
|
1119
|
+
const treeWasmPath = path2.join(resourcesDir, "tree-sitter.wasm");
|
|
1120
|
+
await Parser.init({
|
|
1121
|
+
locateFile: () => treeWasmPath
|
|
1122
|
+
});
|
|
1123
|
+
isInitialized = true;
|
|
1124
|
+
}
|
|
1125
|
+
var loadLanguage = async (lang) => {
|
|
1126
|
+
if (parserCache.has(lang)) {
|
|
1127
|
+
return parserCache.get(lang);
|
|
1128
|
+
}
|
|
1129
|
+
await ensureInitialized();
|
|
1130
|
+
const fileName = LANGUAGE_MAP[lang];
|
|
1131
|
+
if (!fileName) {
|
|
1132
|
+
throw new Error(`Unsupported language: ${lang}`);
|
|
1133
|
+
}
|
|
1134
|
+
try {
|
|
1135
|
+
let resourcesDir;
|
|
1136
|
+
if (_dirname.includes("dist") || _dirname.includes("node_modules")) {
|
|
1137
|
+
resourcesDir = path2.resolve(_dirname, "../resources/tree-sitter");
|
|
1138
|
+
} else {
|
|
1139
|
+
resourcesDir = path2.resolve(_dirname, "../../../resources/tree-sitter");
|
|
1140
|
+
}
|
|
1141
|
+
const wasmPath = path2.join(resourcesDir, fileName);
|
|
1142
|
+
const language = await Language.load(wasmPath);
|
|
1143
|
+
const parser = new Parser();
|
|
1144
|
+
parser.setLanguage(language);
|
|
1145
|
+
parserCache.set(lang, parser);
|
|
1146
|
+
return parser;
|
|
1147
|
+
} catch (error) {
|
|
1148
|
+
console.error(`Error loading language ${lang}:`, error);
|
|
1149
|
+
throw new Error(`Failed to load language ${lang}: ${error}`);
|
|
1150
|
+
}
|
|
1151
|
+
};
|
|
1152
|
+
var legacyParserInstance = null;
|
|
1153
|
+
var initParser = async () => {
|
|
1154
|
+
if (legacyParserInstance) return legacyParserInstance;
|
|
1155
|
+
legacyParserInstance = await loadLanguage("bash");
|
|
1156
|
+
return legacyParserInstance;
|
|
1157
|
+
};
|
|
1158
|
+
|
|
1159
|
+
// src/utils/tree-sitter/lsp-adapter.ts
|
|
1160
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
1161
|
+
var SYMBOL_KIND_MAP2 = {
|
|
1162
|
+
// TypeScript / JavaScript
|
|
1163
|
+
function_declaration: "Function",
|
|
1164
|
+
method_definition: "Method",
|
|
1165
|
+
class_declaration: "Class",
|
|
1166
|
+
interface_declaration: "Interface",
|
|
1167
|
+
variable_declarator: "Variable",
|
|
1168
|
+
export_statement: "Module",
|
|
1169
|
+
// Python
|
|
1170
|
+
function_definition: "Function",
|
|
1171
|
+
class_definition: "Class",
|
|
1172
|
+
// Bash
|
|
1173
|
+
// function_definition is shared with Python/others
|
|
1174
|
+
// C#
|
|
1175
|
+
namespace_declaration: "Namespace",
|
|
1176
|
+
method_declaration: "Method",
|
|
1177
|
+
struct_declaration: "Struct",
|
|
1178
|
+
enum_declaration: "Enum",
|
|
1179
|
+
property_declaration: "Property",
|
|
1180
|
+
// class_declaration shared
|
|
1181
|
+
// interface_declaration shared
|
|
1182
|
+
// Go
|
|
1183
|
+
type_declaration: "Class",
|
|
1184
|
+
// often struct/interface
|
|
1185
|
+
field_declaration: "Field",
|
|
1186
|
+
// function_declaration shared
|
|
1187
|
+
// method_declaration shared
|
|
1188
|
+
// Rust
|
|
1189
|
+
function_item: "Function",
|
|
1190
|
+
struct_item: "Struct",
|
|
1191
|
+
enum_item: "Enum",
|
|
1192
|
+
impl_item: "Class",
|
|
1193
|
+
trait_item: "Interface",
|
|
1194
|
+
mod_item: "Module",
|
|
1195
|
+
// Java
|
|
1196
|
+
// class_declaration shared
|
|
1197
|
+
// method_declaration shared
|
|
1198
|
+
// interface_declaration shared
|
|
1199
|
+
// enum_declaration shared
|
|
1200
|
+
// Scala
|
|
1201
|
+
object_definition: "Class",
|
|
1202
|
+
trait_definition: "Interface",
|
|
1203
|
+
// class_definition shared with Python
|
|
1204
|
+
// function_definition shared with Python
|
|
1205
|
+
// Ruby
|
|
1206
|
+
module: "Module"
|
|
1207
|
+
// class shared
|
|
1208
|
+
// method shared
|
|
1209
|
+
// PHP
|
|
1210
|
+
// class_declaration shared
|
|
1211
|
+
// method_declaration shared
|
|
1212
|
+
// function_definition shared
|
|
1213
|
+
};
|
|
1214
|
+
var TreeSitterLspAdapter = class {
|
|
1215
|
+
static async getDocumentSymbols(filePath) {
|
|
1216
|
+
const langId = ParserRegistry.getLanguage(filePath);
|
|
1217
|
+
if (!langId) {
|
|
1218
|
+
console.warn(`No language found for file: ${filePath}`);
|
|
1219
|
+
return [];
|
|
1220
|
+
}
|
|
1221
|
+
try {
|
|
1222
|
+
const parser = await loadLanguage(langId);
|
|
1223
|
+
const content = await readFile2(filePath, "utf-8");
|
|
1224
|
+
const tree = parser.parse(content);
|
|
1225
|
+
return this.collectSymbols(tree.rootNode);
|
|
1226
|
+
} catch (error) {
|
|
1227
|
+
console.error(`Error parsing file ${filePath}:`, error);
|
|
1228
|
+
return [];
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
static collectSymbols(node) {
|
|
1232
|
+
const symbols = [];
|
|
1233
|
+
const kind = SYMBOL_KIND_MAP2[node.type];
|
|
1234
|
+
if (kind) {
|
|
1235
|
+
const name = this.getName(node);
|
|
1236
|
+
if (name) {
|
|
1237
|
+
const symbol = {
|
|
1238
|
+
name,
|
|
1239
|
+
kind,
|
|
1240
|
+
range: {
|
|
1241
|
+
start: { line: node.startPosition.row, character: node.startPosition.column },
|
|
1242
|
+
end: { line: node.endPosition.row, character: node.endPosition.column }
|
|
1243
|
+
},
|
|
1244
|
+
children: []
|
|
1245
|
+
};
|
|
1246
|
+
for (const child of node.namedChildren) {
|
|
1247
|
+
symbol.children?.push(...this.collectSymbols(child));
|
|
1248
|
+
}
|
|
1249
|
+
symbols.push(symbol);
|
|
1250
|
+
return symbols;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
for (const child of node.namedChildren) {
|
|
1254
|
+
symbols.push(...this.collectSymbols(child));
|
|
1255
|
+
}
|
|
1256
|
+
return symbols;
|
|
1257
|
+
}
|
|
1258
|
+
static getName(node) {
|
|
1259
|
+
const nameNode = node.childForFieldName("name");
|
|
1260
|
+
if (nameNode) return nameNode.text;
|
|
1261
|
+
for (const child of node.namedChildren) {
|
|
1262
|
+
if (child.type === "identifier" || child.type === "type_identifier") {
|
|
1263
|
+
return child.text;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
return null;
|
|
1267
|
+
}
|
|
1268
|
+
};
|
|
1269
|
+
|
|
1270
|
+
// src/utils/tree-sitter/scope-analyzer.ts
|
|
1271
|
+
import { Query } from "web-tree-sitter";
|
|
1272
|
+
var QUERIES = {
|
|
1273
|
+
python: `
|
|
1274
|
+
(function_definition name: (identifier) @name)
|
|
1275
|
+
(parameters (identifier) @param)
|
|
1276
|
+
(assignment left: (identifier) @var)
|
|
1277
|
+
`,
|
|
1278
|
+
typescript: `
|
|
1279
|
+
(function_declaration name: (identifier) @name)
|
|
1280
|
+
(variable_declarator name: (identifier) @var)
|
|
1281
|
+
(required_parameter pattern: (identifier) @param)
|
|
1282
|
+
(optional_parameter pattern: (identifier) @param)
|
|
1283
|
+
`
|
|
1284
|
+
};
|
|
1285
|
+
var ScopeAnalyzer = class {
|
|
1286
|
+
static async getScope(filename, code, position) {
|
|
1287
|
+
const langKey = ParserRegistry.getLanguage(filename);
|
|
1288
|
+
if (!langKey) {
|
|
1289
|
+
throw new Error(`Unsupported language for file: ${filename}`);
|
|
1290
|
+
}
|
|
1291
|
+
const parser = await loadLanguage(langKey);
|
|
1292
|
+
const tree = parser.parse(code);
|
|
1293
|
+
const node = tree.rootNode.descendantForPosition(position);
|
|
1294
|
+
const ancestors = [];
|
|
1295
|
+
let current = node;
|
|
1296
|
+
while (current) {
|
|
1297
|
+
ancestors.push(current);
|
|
1298
|
+
current = current.parent;
|
|
1299
|
+
}
|
|
1300
|
+
const scopeNodes = ancestors.filter(
|
|
1301
|
+
(n) => n.type === "function_definition" || n.type === "function_declaration" || n.type === "module" || // Python root
|
|
1302
|
+
n.type === "program"
|
|
1303
|
+
// TS root
|
|
1304
|
+
);
|
|
1305
|
+
const result = {
|
|
1306
|
+
locals: [],
|
|
1307
|
+
closure: []
|
|
1308
|
+
};
|
|
1309
|
+
if (scopeNodes.length === 0) return result;
|
|
1310
|
+
const queryStr = QUERIES[langKey];
|
|
1311
|
+
if (!queryStr) return result;
|
|
1312
|
+
const language = parser.language;
|
|
1313
|
+
if (!language) return result;
|
|
1314
|
+
const query = new Query(language, queryStr);
|
|
1315
|
+
for (let i = 0; i < scopeNodes.length; i++) {
|
|
1316
|
+
const scopeNode = scopeNodes[i];
|
|
1317
|
+
const captures = query.captures(scopeNode);
|
|
1318
|
+
const names = /* @__PURE__ */ new Set();
|
|
1319
|
+
for (const capture of captures) {
|
|
1320
|
+
const defNode = capture.node;
|
|
1321
|
+
let temp = defNode.parent;
|
|
1322
|
+
if (temp && (temp.type === "function_definition" || temp.type === "function_declaration")) {
|
|
1323
|
+
if (capture.name === "name") {
|
|
1324
|
+
temp = temp.parent;
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
let isDirect = true;
|
|
1328
|
+
while (temp && temp.id !== scopeNode.id) {
|
|
1329
|
+
if (temp.type === "function_definition" || temp.type === "function_declaration") {
|
|
1330
|
+
isDirect = false;
|
|
1331
|
+
break;
|
|
1332
|
+
}
|
|
1333
|
+
temp = temp.parent;
|
|
1334
|
+
}
|
|
1335
|
+
if (isDirect) {
|
|
1336
|
+
names.add(defNode.text);
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
if (i === 0) {
|
|
1340
|
+
result.locals = Array.from(names);
|
|
1341
|
+
} else {
|
|
1342
|
+
for (const name of names) {
|
|
1343
|
+
result.closure.push(name);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
result.closure = Array.from(new Set(result.closure));
|
|
1348
|
+
return result;
|
|
1349
|
+
}
|
|
1350
|
+
};
|
|
1351
|
+
|
|
1352
|
+
// src/lsp/index.ts
|
|
1353
|
+
function getLanguageId(ext) {
|
|
1354
|
+
return LspServerRegistry.getInstance().getLanguageIdForExtension(ext);
|
|
1355
|
+
}
|
|
1356
|
+
function normalizeUri(filePath) {
|
|
1357
|
+
let uri = pathToFileURL2(filePath).href;
|
|
1358
|
+
if (process.platform === "win32") {
|
|
1359
|
+
uri = uri.replace(/^file:\/\/\/([a-zA-Z]):\//, (_match, drive) => {
|
|
1360
|
+
return `file:///${drive.toLowerCase()}%3A/`;
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
return uri;
|
|
1364
|
+
}
|
|
1365
|
+
function formatTreeSitterSymbols(symbols, depth = 0) {
|
|
1366
|
+
let text = "";
|
|
1367
|
+
const indent = " ".repeat(depth);
|
|
1368
|
+
for (const s of symbols) {
|
|
1369
|
+
text += `${indent}- ${s.name} (${s.kind}) [Ln ${s.range.start.line + 1}]
|
|
1370
|
+
`;
|
|
1371
|
+
if (s.children && s.children.length > 0) {
|
|
1372
|
+
text += formatTreeSitterSymbols(s.children, depth + 1);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
return text;
|
|
1376
|
+
}
|
|
1377
|
+
function formatTreeSitterDocumentSymbolResult(symbols) {
|
|
1378
|
+
const formatted = symbols.length > 0 ? `Document symbols:
|
|
1379
|
+
${formatTreeSitterSymbols(symbols)}` : "No symbols found.";
|
|
1380
|
+
return {
|
|
1381
|
+
formatted,
|
|
1382
|
+
resultCount: symbols.length,
|
|
1383
|
+
fileCount: symbols.length > 0 ? 1 : 0
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1386
|
+
function normalizeWorkspaceSymbol(sym) {
|
|
1387
|
+
if (!sym || typeof sym !== "object") return null;
|
|
1388
|
+
const range = sym.range ?? sym.location?.range;
|
|
1389
|
+
if (!range || !sym.name) return null;
|
|
1390
|
+
const uri = sym.uri ?? sym.location?.uri;
|
|
1391
|
+
return { ...sym, range, uri };
|
|
1392
|
+
}
|
|
1393
|
+
function sortWorkspaceSymbols(symbols) {
|
|
1394
|
+
return [...symbols].sort((a, b) => {
|
|
1395
|
+
const nameA = String(a?.name ?? "").toLowerCase();
|
|
1396
|
+
const nameB = String(b?.name ?? "").toLowerCase();
|
|
1397
|
+
if (nameA !== nameB) return nameA.localeCompare(nameB);
|
|
1398
|
+
const uriA = String(a?.uri ?? a?.location?.uri ?? "");
|
|
1399
|
+
const uriB = String(b?.uri ?? b?.location?.uri ?? "");
|
|
1400
|
+
if (uriA !== uriB) return uriA.localeCompare(uriB);
|
|
1401
|
+
const lineA = a?.range?.start?.line ?? a?.location?.range?.start?.line ?? 0;
|
|
1402
|
+
const lineB = b?.range?.start?.line ?? b?.location?.range?.start?.line ?? 0;
|
|
1403
|
+
if (lineA !== lineB) return lineA - lineB;
|
|
1404
|
+
const charA = a?.range?.start?.character ?? a?.location?.range?.start?.character ?? 0;
|
|
1405
|
+
const charB = b?.range?.start?.character ?? b?.location?.range?.start?.character ?? 0;
|
|
1406
|
+
return charA - charB;
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
function serveStatusWithProvider(statusProvider, options) {
|
|
1410
|
+
const host = options?.host ?? "127.0.0.1";
|
|
1411
|
+
const port = typeof options?.port === "number" ? options.port : 7337;
|
|
1412
|
+
const path3 = options?.path ?? "/lsp/status";
|
|
1413
|
+
const allowCors = options?.allowCors ?? true;
|
|
1414
|
+
const corsHeaders = allowCors ? {
|
|
1415
|
+
"Access-Control-Allow-Origin": "*",
|
|
1416
|
+
"Access-Control-Allow-Methods": "GET,OPTIONS",
|
|
1417
|
+
"Access-Control-Allow-Headers": "Content-Type"
|
|
1418
|
+
} : {};
|
|
1419
|
+
const server = http.createServer((req, res) => {
|
|
1420
|
+
const method = req.method ?? "GET";
|
|
1421
|
+
const url = new URL(req.url ?? "/", `http://${host}`);
|
|
1422
|
+
if (url.pathname !== path3) {
|
|
1423
|
+
res.writeHead(404, corsHeaders);
|
|
1424
|
+
res.end("Not Found");
|
|
1425
|
+
return;
|
|
1426
|
+
}
|
|
1427
|
+
if (method === "OPTIONS") {
|
|
1428
|
+
res.writeHead(204, corsHeaders);
|
|
1429
|
+
res.end();
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
if (method !== "GET") {
|
|
1433
|
+
res.writeHead(405, corsHeaders);
|
|
1434
|
+
res.end("Method Not Allowed");
|
|
1435
|
+
return;
|
|
1436
|
+
}
|
|
1437
|
+
const status = statusProvider();
|
|
1438
|
+
res.writeHead(200, { "Content-Type": "application/json", ...corsHeaders });
|
|
1439
|
+
res.end(JSON.stringify(status, null, 2));
|
|
1440
|
+
});
|
|
1441
|
+
server.port = port;
|
|
1442
|
+
server.listen(port, host);
|
|
1443
|
+
return server;
|
|
1444
|
+
}
|
|
1445
|
+
var LspAPI = {
|
|
1446
|
+
async run(input) {
|
|
1447
|
+
const rootPath = input.rootPath ?? getCwd();
|
|
1448
|
+
const client = await LspClientManager.getInstance().getClient(
|
|
1449
|
+
input.filePath,
|
|
1450
|
+
rootPath
|
|
1451
|
+
);
|
|
1452
|
+
if (client) {
|
|
1453
|
+
try {
|
|
1454
|
+
switch (input.operation) {
|
|
1455
|
+
case "goToDefinition": {
|
|
1456
|
+
if (input.line === void 0 || input.character === void 0) {
|
|
1457
|
+
return {
|
|
1458
|
+
formatted: "Error performing goToDefinition: Missing line/character",
|
|
1459
|
+
resultCount: 0,
|
|
1460
|
+
fileCount: 0
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
const result = await client.goToDefinition(
|
|
1464
|
+
input.filePath,
|
|
1465
|
+
input.line,
|
|
1466
|
+
input.character
|
|
1467
|
+
);
|
|
1468
|
+
return formatGenericDefinitionResult(result);
|
|
1469
|
+
}
|
|
1470
|
+
case "findReferences": {
|
|
1471
|
+
if (input.line === void 0 || input.character === void 0) {
|
|
1472
|
+
return {
|
|
1473
|
+
formatted: "Error performing findReferences: Missing line/character",
|
|
1474
|
+
resultCount: 0,
|
|
1475
|
+
fileCount: 0
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
const result = await client.findReferences(
|
|
1479
|
+
input.filePath,
|
|
1480
|
+
input.line,
|
|
1481
|
+
input.character
|
|
1482
|
+
);
|
|
1483
|
+
return formatGenericReferencesResult(result);
|
|
1484
|
+
}
|
|
1485
|
+
case "hover": {
|
|
1486
|
+
if (input.line === void 0 || input.character === void 0) {
|
|
1487
|
+
return {
|
|
1488
|
+
formatted: "Error performing hover: Missing line/character",
|
|
1489
|
+
resultCount: 0,
|
|
1490
|
+
fileCount: 0
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
const result = await client.hover(
|
|
1494
|
+
input.filePath,
|
|
1495
|
+
input.line,
|
|
1496
|
+
input.character
|
|
1497
|
+
);
|
|
1498
|
+
return formatGenericHoverResult(result, input.line, input.character);
|
|
1499
|
+
}
|
|
1500
|
+
case "documentSymbol": {
|
|
1501
|
+
const result = await client.documentSymbol(input.filePath);
|
|
1502
|
+
return formatGenericDocumentSymbolResult(result);
|
|
1503
|
+
}
|
|
1504
|
+
case "workspaceSymbol": {
|
|
1505
|
+
const query = input.query ?? input.filePath;
|
|
1506
|
+
const results = await LspClientManager.getInstance().workspaceSymbol(
|
|
1507
|
+
query,
|
|
1508
|
+
rootPath
|
|
1509
|
+
);
|
|
1510
|
+
return formatGenericDocumentSymbolResult(results);
|
|
1511
|
+
}
|
|
1512
|
+
case "goToImplementation": {
|
|
1513
|
+
if (input.line === void 0 || input.character === void 0) {
|
|
1514
|
+
return {
|
|
1515
|
+
formatted: "Error performing goToImplementation: Missing line/character",
|
|
1516
|
+
resultCount: 0,
|
|
1517
|
+
fileCount: 0
|
|
1518
|
+
};
|
|
1519
|
+
}
|
|
1520
|
+
const result = await client.goToImplementation(
|
|
1521
|
+
input.filePath,
|
|
1522
|
+
input.line,
|
|
1523
|
+
input.character
|
|
1524
|
+
);
|
|
1525
|
+
return formatGenericDefinitionResult(result);
|
|
1526
|
+
}
|
|
1527
|
+
case "prepareCallHierarchy": {
|
|
1528
|
+
if (input.line === void 0 || input.character === void 0) {
|
|
1529
|
+
return {
|
|
1530
|
+
formatted: "Error performing prepareCallHierarchy: Missing line/character",
|
|
1531
|
+
resultCount: 0,
|
|
1532
|
+
fileCount: 0
|
|
1533
|
+
};
|
|
1534
|
+
}
|
|
1535
|
+
const result = await client.prepareCallHierarchy(
|
|
1536
|
+
input.filePath,
|
|
1537
|
+
input.line,
|
|
1538
|
+
input.character
|
|
1539
|
+
);
|
|
1540
|
+
return formatGenericCallHierarchyItemsResult(
|
|
1541
|
+
Array.isArray(result) ? result : []
|
|
1542
|
+
);
|
|
1543
|
+
}
|
|
1544
|
+
case "incomingCalls": {
|
|
1545
|
+
if (input.line === void 0 || input.character === void 0) {
|
|
1546
|
+
return {
|
|
1547
|
+
formatted: "Error performing incomingCalls: Missing line/character",
|
|
1548
|
+
resultCount: 0,
|
|
1549
|
+
fileCount: 0
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1552
|
+
const items = await client.prepareCallHierarchy(
|
|
1553
|
+
input.filePath,
|
|
1554
|
+
input.line,
|
|
1555
|
+
input.character
|
|
1556
|
+
);
|
|
1557
|
+
const callResults = await Promise.all(
|
|
1558
|
+
(Array.isArray(items) ? items : []).map(
|
|
1559
|
+
(item) => client.incomingCalls(item)
|
|
1560
|
+
)
|
|
1561
|
+
);
|
|
1562
|
+
const calls = callResults.flat().filter(Boolean);
|
|
1563
|
+
return formatGenericIncomingCallsResult(calls);
|
|
1564
|
+
}
|
|
1565
|
+
case "outgoingCalls": {
|
|
1566
|
+
if (input.line === void 0 || input.character === void 0) {
|
|
1567
|
+
return {
|
|
1568
|
+
formatted: "Error performing outgoingCalls: Missing line/character",
|
|
1569
|
+
resultCount: 0,
|
|
1570
|
+
fileCount: 0
|
|
1571
|
+
};
|
|
1572
|
+
}
|
|
1573
|
+
const items = await client.prepareCallHierarchy(
|
|
1574
|
+
input.filePath,
|
|
1575
|
+
input.line,
|
|
1576
|
+
input.character
|
|
1577
|
+
);
|
|
1578
|
+
const callResults = await Promise.all(
|
|
1579
|
+
(Array.isArray(items) ? items : []).map(
|
|
1580
|
+
(item) => client.outgoingCalls(item)
|
|
1581
|
+
)
|
|
1582
|
+
);
|
|
1583
|
+
const calls = callResults.flat().filter(Boolean);
|
|
1584
|
+
return formatGenericOutgoingCallsResult(calls);
|
|
1585
|
+
}
|
|
1586
|
+
case "diagnostics": {
|
|
1587
|
+
if (input.waitForDiagnostics) {
|
|
1588
|
+
await client.waitForReadiness(
|
|
1589
|
+
input.filePath,
|
|
1590
|
+
input.timeoutMs ?? 5e3
|
|
1591
|
+
);
|
|
1592
|
+
}
|
|
1593
|
+
const diagnostics = client.getDiagnostics(input.filePath);
|
|
1594
|
+
return formatGenericDiagnosticsResult(diagnostics);
|
|
1595
|
+
}
|
|
1596
|
+
default: {
|
|
1597
|
+
return {
|
|
1598
|
+
formatted: `Operation ${input.operation} not supported by generic client yet.`,
|
|
1599
|
+
resultCount: 0,
|
|
1600
|
+
fileCount: 0
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
} catch (error) {
|
|
1605
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1606
|
+
return {
|
|
1607
|
+
formatted: `Error performing ${input.operation} with generic client: ${message}`,
|
|
1608
|
+
resultCount: 0,
|
|
1609
|
+
fileCount: 0
|
|
1610
|
+
};
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
const ext = extname2(input.filePath);
|
|
1614
|
+
return {
|
|
1615
|
+
formatted: `No LSP server available for file type: ${ext}`,
|
|
1616
|
+
resultCount: 0,
|
|
1617
|
+
fileCount: 0
|
|
1618
|
+
};
|
|
1619
|
+
},
|
|
1620
|
+
async runAll(inputs) {
|
|
1621
|
+
return Promise.all(inputs.map((input) => this.run(input)));
|
|
1622
|
+
},
|
|
1623
|
+
async touchFile(filePath, options = {}) {
|
|
1624
|
+
const client = await LspClientManager.getInstance().getClientForFile(filePath);
|
|
1625
|
+
if (!client) return false;
|
|
1626
|
+
const content = await readFile3(filePath, "utf-8");
|
|
1627
|
+
const languageId = getLanguageId(extname2(filePath));
|
|
1628
|
+
await client.didOpen(filePath, content, languageId);
|
|
1629
|
+
if (options.wait) {
|
|
1630
|
+
await client.waitForReadiness(filePath, options.timeoutMs ?? 2e3);
|
|
1631
|
+
}
|
|
1632
|
+
return true;
|
|
1633
|
+
},
|
|
1634
|
+
async diagnostics(filePath, rootPath) {
|
|
1635
|
+
return LspClientManager.getInstance().getAllDiagnostics(filePath, rootPath);
|
|
1636
|
+
},
|
|
1637
|
+
async diagnosticsPretty(filePath, options) {
|
|
1638
|
+
const diagnostics = await LspClientManager.getInstance().getAllDiagnostics(
|
|
1639
|
+
filePath,
|
|
1640
|
+
options?.rootPath
|
|
1641
|
+
);
|
|
1642
|
+
return formatDiagnosticsPretty(diagnostics, { maxItems: options?.maxItems });
|
|
1643
|
+
},
|
|
1644
|
+
async documentSymbolRaw(filePath, rootPath) {
|
|
1645
|
+
const manager = LspClientManager.getInstance();
|
|
1646
|
+
const resolvedRoot = rootPath ?? getCwd();
|
|
1647
|
+
const managerDocumentSymbol = manager.documentSymbol;
|
|
1648
|
+
if (typeof managerDocumentSymbol === "function") {
|
|
1649
|
+
return managerDocumentSymbol.call(manager, filePath, resolvedRoot);
|
|
1650
|
+
}
|
|
1651
|
+
const client = await manager.getClient(filePath, resolvedRoot);
|
|
1652
|
+
if (!client) return null;
|
|
1653
|
+
const clientDocumentSymbol = client.documentSymbol;
|
|
1654
|
+
if (typeof clientDocumentSymbol !== "function") return [];
|
|
1655
|
+
try {
|
|
1656
|
+
const symbols = await clientDocumentSymbol.call(client, filePath);
|
|
1657
|
+
return Array.isArray(symbols) ? symbols : [];
|
|
1658
|
+
} catch {
|
|
1659
|
+
return [];
|
|
1660
|
+
}
|
|
1661
|
+
},
|
|
1662
|
+
async findReferencesRaw(filePath, line, character, rootPath) {
|
|
1663
|
+
const client = await LspClientManager.getInstance().getClient(
|
|
1664
|
+
filePath,
|
|
1665
|
+
rootPath ?? getCwd()
|
|
1666
|
+
);
|
|
1667
|
+
if (!client) return null;
|
|
1668
|
+
return await client.findReferences(filePath, line, character);
|
|
1669
|
+
},
|
|
1670
|
+
normalizeUri,
|
|
1671
|
+
async workspaceSymbol(query, options) {
|
|
1672
|
+
const { rootPath, ...rest } = options ?? {};
|
|
1673
|
+
return LspClientManager.getInstance().workspaceSymbol(
|
|
1674
|
+
query,
|
|
1675
|
+
rootPath ?? getCwd(),
|
|
1676
|
+
rest
|
|
1677
|
+
);
|
|
1678
|
+
},
|
|
1679
|
+
serveStatus(options) {
|
|
1680
|
+
return serveStatusWithProvider(() => LspAPI.status(), options);
|
|
1681
|
+
},
|
|
1682
|
+
status() {
|
|
1683
|
+
return LspClientManager.getInstance().status();
|
|
1684
|
+
}
|
|
1685
|
+
};
|
|
1686
|
+
var LspFacade = {
|
|
1687
|
+
async run(input) {
|
|
1688
|
+
const rootPath = input.rootPath ?? getCwd();
|
|
1689
|
+
if (input.operation === "documentSymbol") {
|
|
1690
|
+
const lang = ParserRegistry.getLanguage(input.filePath);
|
|
1691
|
+
if (lang) {
|
|
1692
|
+
try {
|
|
1693
|
+
const symbols = await TreeSitterLspAdapter.getDocumentSymbols(input.filePath);
|
|
1694
|
+
return formatTreeSitterDocumentSymbolResult(symbols);
|
|
1695
|
+
} catch {
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
if (input.operation === "getScope") {
|
|
1700
|
+
if (input.line === void 0 || input.character === void 0) {
|
|
1701
|
+
return {
|
|
1702
|
+
formatted: "Error performing getScope: Missing line/character",
|
|
1703
|
+
resultCount: 0,
|
|
1704
|
+
fileCount: 0
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
const lang = ParserRegistry.getLanguage(input.filePath);
|
|
1708
|
+
if (!lang) {
|
|
1709
|
+
return {
|
|
1710
|
+
formatted: "getScope not supported for this language",
|
|
1711
|
+
resultCount: 0,
|
|
1712
|
+
fileCount: 0
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
try {
|
|
1716
|
+
const code = await readFile3(input.filePath, "utf-8");
|
|
1717
|
+
const scope = await ScopeAnalyzer.getScope(input.filePath, code, {
|
|
1718
|
+
row: input.line - 1,
|
|
1719
|
+
column: input.character - 1
|
|
1720
|
+
});
|
|
1721
|
+
let resultText = "";
|
|
1722
|
+
if (scope.locals.length > 0) {
|
|
1723
|
+
resultText += `Locals: ${scope.locals.join(", ")}
|
|
1724
|
+
`;
|
|
1725
|
+
} else {
|
|
1726
|
+
resultText += "Locals: (none)\n";
|
|
1727
|
+
}
|
|
1728
|
+
if (scope.closure.length > 0) {
|
|
1729
|
+
resultText += `Closure: ${scope.closure.join(", ")}
|
|
1730
|
+
`;
|
|
1731
|
+
} else {
|
|
1732
|
+
resultText += "Closure: (none)\n";
|
|
1733
|
+
}
|
|
1734
|
+
return {
|
|
1735
|
+
formatted: resultText,
|
|
1736
|
+
resultCount: scope.locals.length + scope.closure.length,
|
|
1737
|
+
fileCount: 1
|
|
1738
|
+
};
|
|
1739
|
+
} catch (error) {
|
|
1740
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1741
|
+
return {
|
|
1742
|
+
formatted: `Error performing getScope: ${message}`,
|
|
1743
|
+
resultCount: 0,
|
|
1744
|
+
fileCount: 0
|
|
1745
|
+
};
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
if (input.operation !== "workspaceSymbol") {
|
|
1749
|
+
await LspAPI.touchFile(input.filePath, {
|
|
1750
|
+
wait: input.operation === "diagnostics" && input.waitForDiagnostics,
|
|
1751
|
+
timeoutMs: input.timeoutMs,
|
|
1752
|
+
rootPath
|
|
1753
|
+
});
|
|
1754
|
+
}
|
|
1755
|
+
if (input.operation === "workspaceSymbol") {
|
|
1756
|
+
const query = input.query ?? input.filePath;
|
|
1757
|
+
const symbols = await LspAPI.workspaceSymbol(query, { rootPath });
|
|
1758
|
+
const normalized = symbols.map(normalizeWorkspaceSymbol).filter(Boolean);
|
|
1759
|
+
const sorted = sortWorkspaceSymbols(normalized);
|
|
1760
|
+
return formatGenericDocumentSymbolResult(sorted);
|
|
1761
|
+
}
|
|
1762
|
+
const { operation, ...rest } = input;
|
|
1763
|
+
return LspAPI.run({
|
|
1764
|
+
...rest,
|
|
1765
|
+
operation,
|
|
1766
|
+
rootPath
|
|
1767
|
+
});
|
|
1768
|
+
},
|
|
1769
|
+
async runAll(inputs) {
|
|
1770
|
+
return Promise.all(inputs.map((input) => this.run(input)));
|
|
1771
|
+
},
|
|
1772
|
+
async touchFile(filePath, options = {}) {
|
|
1773
|
+
return LspAPI.touchFile(filePath, options);
|
|
1774
|
+
},
|
|
1775
|
+
async diagnosticsPretty(filePath, options) {
|
|
1776
|
+
return LspAPI.diagnosticsPretty(filePath, options);
|
|
1777
|
+
},
|
|
1778
|
+
async checkFileReferences(filePath) {
|
|
1779
|
+
const rootPath = getCwd();
|
|
1780
|
+
const fileUri = LspAPI.normalizeUri(filePath);
|
|
1781
|
+
const symbols = await LspAPI.documentSymbolRaw(filePath, rootPath);
|
|
1782
|
+
if (symbols === null) {
|
|
1783
|
+
throw new Error("LSP unavailable");
|
|
1784
|
+
}
|
|
1785
|
+
const checkSymbolRefs = async (sym) => {
|
|
1786
|
+
const range = sym.selectionRange || sym.range || (sym.location ? sym.location.range : null);
|
|
1787
|
+
if (!range) return null;
|
|
1788
|
+
const refs = await LspAPI.findReferencesRaw(
|
|
1789
|
+
filePath,
|
|
1790
|
+
range.start.line + 1,
|
|
1791
|
+
range.start.character + 1,
|
|
1792
|
+
rootPath
|
|
1793
|
+
);
|
|
1794
|
+
if (refs && Array.isArray(refs)) {
|
|
1795
|
+
const externalRefs = refs.filter((r) => r.uri !== fileUri);
|
|
1796
|
+
if (externalRefs.length > 0) {
|
|
1797
|
+
const refFiles = externalRefs.map((r) => r.uri).slice(0, 3).join(", ");
|
|
1798
|
+
return `Symbol '${sym.name}' referenced by ${externalRefs.length} locations (e.g. ${refFiles})`;
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
return null;
|
|
1802
|
+
};
|
|
1803
|
+
if (symbols && Array.isArray(symbols) && symbols.length > 0) {
|
|
1804
|
+
for (const sym of symbols) {
|
|
1805
|
+
const result = await checkSymbolRefs(sym);
|
|
1806
|
+
if (result) return result;
|
|
1807
|
+
}
|
|
1808
|
+
} else {
|
|
1809
|
+
const refs = await LspAPI.findReferencesRaw(filePath, 1, 1, rootPath);
|
|
1810
|
+
if (refs && Array.isArray(refs)) {
|
|
1811
|
+
const externalRefs = refs.filter((r) => r.uri !== fileUri);
|
|
1812
|
+
if (externalRefs.length > 0) {
|
|
1813
|
+
return `File referenced by ${externalRefs.length} locations`;
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
return null;
|
|
1818
|
+
},
|
|
1819
|
+
serveStatus(options) {
|
|
1820
|
+
return serveStatusWithProvider(() => LspFacade.status(), options);
|
|
1821
|
+
},
|
|
1822
|
+
status() {
|
|
1823
|
+
return LspAPI.status();
|
|
1824
|
+
}
|
|
1825
|
+
};
|
|
1826
|
+
|
|
1827
|
+
export {
|
|
1828
|
+
formatDiagnosticsPretty,
|
|
1829
|
+
ParserRegistry,
|
|
1830
|
+
loadLanguage,
|
|
1831
|
+
initParser,
|
|
1832
|
+
LspAPI,
|
|
1833
|
+
LspFacade
|
|
1834
|
+
};
|