opencodekit 0.12.6 → 0.13.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/README.md +2 -2
- package/dist/index.js +2756 -523
- package/dist/template/.opencode/AGENTS.md +35 -128
- package/dist/template/.opencode/README.md +4 -3
- package/dist/template/.opencode/agent/build.md +32 -21
- package/dist/template/.opencode/agent/explore.md +27 -16
- package/dist/template/.opencode/agent/planner.md +103 -63
- package/dist/template/.opencode/agent/review.md +31 -23
- package/dist/template/.opencode/agent/rush.md +27 -19
- package/dist/template/.opencode/agent/scout.md +27 -19
- package/dist/template/.opencode/agent/vision.md +29 -19
- package/dist/template/.opencode/command/accessibility-check.md +1 -0
- package/dist/template/.opencode/command/analyze-mockup.md +1 -0
- package/dist/template/.opencode/command/analyze-project.md +2 -1
- package/dist/template/.opencode/command/brainstorm.md +2 -1
- package/dist/template/.opencode/command/design-audit.md +1 -0
- package/dist/template/.opencode/command/design.md +1 -0
- package/dist/template/.opencode/command/finish.md +39 -4
- package/dist/template/.opencode/command/fix.md +28 -1
- package/dist/template/.opencode/command/implement.md +26 -6
- package/dist/template/.opencode/command/init.md +1 -0
- package/dist/template/.opencode/command/pr.md +28 -1
- package/dist/template/.opencode/command/research-ui.md +1 -0
- package/dist/template/.opencode/command/research.md +1 -4
- package/dist/template/.opencode/command/review-codebase.md +1 -0
- package/dist/template/.opencode/command/start.md +106 -0
- package/dist/template/.opencode/command/status.md +3 -2
- package/dist/template/.opencode/command/summarize.md +2 -1
- package/dist/template/.opencode/command/triage.md +66 -12
- package/dist/template/.opencode/command/ui-review.md +1 -0
- package/dist/template/.opencode/memory/project/architecture.md +59 -6
- package/dist/template/.opencode/memory/project/beads-workflow.md +278 -0
- package/dist/template/.opencode/memory/project/commands.md +20 -164
- package/dist/template/.opencode/memory/session-context.md +40 -0
- package/dist/template/.opencode/memory/user.md +24 -7
- package/dist/template/.opencode/opencode.json +77 -16
- package/dist/template/.opencode/package.json +1 -1
- package/dist/template/.opencode/plugin/compaction.ts +62 -18
- package/dist/template/.opencode/plugin/lib/notify.ts +2 -3
- package/dist/template/.opencode/plugin/sessions.ts +1 -1
- package/dist/template/.opencode/plugin/skill-mcp.ts +11 -12
- package/dist/template/.opencode/skill/beads/SKILL.md +44 -0
- package/dist/template/.opencode/skill/condition-based-waiting/example.ts +71 -65
- package/dist/template/.opencode/tool/ast-grep.ts +3 -3
- package/dist/template/.opencode/tool/bd-inbox.ts +7 -6
- package/dist/template/.opencode/tool/bd-msg.ts +3 -3
- package/dist/template/.opencode/tool/bd-release.ts +2 -2
- package/dist/template/.opencode/tool/bd-reserve.ts +5 -4
- package/dist/template/.opencode/tool/memory-read.ts +58 -58
- package/dist/template/.opencode/tool/memory-search.ts +2 -2
- package/dist/template/.opencode/tool/memory-update.ts +53 -54
- package/dist/template/.opencode/tool/observation.ts +6 -6
- package/dist/template/.opencode/tsconfig.json +19 -19
- package/package.json +8 -17
- package/dist/template/.opencode/command.backup/analyze-project.md +0 -465
- package/dist/template/.opencode/command.backup/finish.md +0 -167
- package/dist/template/.opencode/command.backup/implement.md +0 -143
- package/dist/template/.opencode/command.backup/pr.md +0 -252
- package/dist/template/.opencode/command.backup/status.md +0 -376
- package/dist/template/.opencode/lib/lsp/client.ts +0 -614
- package/dist/template/.opencode/lib/lsp/config.ts +0 -199
- package/dist/template/.opencode/lib/lsp/constants.ts +0 -339
- package/dist/template/.opencode/lib/lsp/types.ts +0 -138
- package/dist/template/.opencode/lib/lsp/utils.ts +0 -190
- package/dist/template/.opencode/memory/project/SHELL_OUTPUT_MIGRATION_PLAN.md +0 -551
|
@@ -1,614 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { extname, resolve } from "node:path";
|
|
3
|
-
import { type Subprocess, spawn } from "bun";
|
|
4
|
-
import type { ResolvedServer } from "./config";
|
|
5
|
-
import { getLanguageId } from "./config";
|
|
6
|
-
import type { Diagnostic } from "./types";
|
|
7
|
-
|
|
8
|
-
interface ManagedClient {
|
|
9
|
-
client: LSPClient;
|
|
10
|
-
lastUsedAt: number;
|
|
11
|
-
refCount: number;
|
|
12
|
-
initPromise?: Promise<void>;
|
|
13
|
-
isInitializing: boolean;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
class LSPServerManager {
|
|
17
|
-
private static instance: LSPServerManager;
|
|
18
|
-
private clients = new Map<string, ManagedClient>();
|
|
19
|
-
private cleanupInterval: ReturnType<typeof setInterval> | null = null;
|
|
20
|
-
private readonly IDLE_TIMEOUT = 5 * 60 * 1000;
|
|
21
|
-
|
|
22
|
-
private constructor() {
|
|
23
|
-
this.startCleanupTimer();
|
|
24
|
-
this.registerProcessCleanup();
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
private registerProcessCleanup(): void {
|
|
28
|
-
const cleanup = () => {
|
|
29
|
-
for (const [, managed] of this.clients) {
|
|
30
|
-
try {
|
|
31
|
-
managed.client.stop();
|
|
32
|
-
} catch {}
|
|
33
|
-
}
|
|
34
|
-
this.clients.clear();
|
|
35
|
-
if (this.cleanupInterval) {
|
|
36
|
-
clearInterval(this.cleanupInterval);
|
|
37
|
-
this.cleanupInterval = null;
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
process.on("exit", cleanup);
|
|
42
|
-
process.on("SIGINT", () => {
|
|
43
|
-
cleanup();
|
|
44
|
-
process.exit(0);
|
|
45
|
-
});
|
|
46
|
-
process.on("SIGTERM", () => {
|
|
47
|
-
cleanup();
|
|
48
|
-
process.exit(0);
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
static getInstance(): LSPServerManager {
|
|
53
|
-
if (!LSPServerManager.instance) {
|
|
54
|
-
LSPServerManager.instance = new LSPServerManager();
|
|
55
|
-
}
|
|
56
|
-
return LSPServerManager.instance;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
private getKey(root: string, serverId: string): string {
|
|
60
|
-
return `${root}::${serverId}`;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
private startCleanupTimer(): void {
|
|
64
|
-
if (this.cleanupInterval) return;
|
|
65
|
-
this.cleanupInterval = setInterval(() => {
|
|
66
|
-
this.cleanupIdleClients();
|
|
67
|
-
}, 60000);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
private cleanupIdleClients(): void {
|
|
71
|
-
const now = Date.now();
|
|
72
|
-
for (const [key, managed] of this.clients) {
|
|
73
|
-
if (
|
|
74
|
-
managed.refCount === 0 &&
|
|
75
|
-
now - managed.lastUsedAt > this.IDLE_TIMEOUT
|
|
76
|
-
) {
|
|
77
|
-
managed.client.stop();
|
|
78
|
-
this.clients.delete(key);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async getClient(root: string, server: ResolvedServer): Promise<LSPClient> {
|
|
84
|
-
const key = this.getKey(root, server.id);
|
|
85
|
-
|
|
86
|
-
const managed = this.clients.get(key);
|
|
87
|
-
if (managed) {
|
|
88
|
-
if (managed.initPromise) {
|
|
89
|
-
await managed.initPromise;
|
|
90
|
-
}
|
|
91
|
-
if (managed.client.isAlive()) {
|
|
92
|
-
managed.refCount++;
|
|
93
|
-
managed.lastUsedAt = Date.now();
|
|
94
|
-
return managed.client;
|
|
95
|
-
}
|
|
96
|
-
await managed.client.stop();
|
|
97
|
-
this.clients.delete(key);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const client = new LSPClient(root, server);
|
|
101
|
-
const initPromise = (async () => {
|
|
102
|
-
await client.start();
|
|
103
|
-
await client.initialize();
|
|
104
|
-
})();
|
|
105
|
-
|
|
106
|
-
this.clients.set(key, {
|
|
107
|
-
client,
|
|
108
|
-
lastUsedAt: Date.now(),
|
|
109
|
-
refCount: 1,
|
|
110
|
-
initPromise,
|
|
111
|
-
isInitializing: true,
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
await initPromise;
|
|
115
|
-
const m = this.clients.get(key);
|
|
116
|
-
if (m) {
|
|
117
|
-
m.initPromise = undefined;
|
|
118
|
-
m.isInitializing = false;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return client;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
releaseClient(root: string, serverId: string): void {
|
|
125
|
-
const key = this.getKey(root, serverId);
|
|
126
|
-
const managed = this.clients.get(key);
|
|
127
|
-
if (managed && managed.refCount > 0) {
|
|
128
|
-
managed.refCount--;
|
|
129
|
-
managed.lastUsedAt = Date.now();
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
isServerInitializing(root: string, serverId: string): boolean {
|
|
134
|
-
const key = this.getKey(root, serverId);
|
|
135
|
-
const managed = this.clients.get(key);
|
|
136
|
-
return managed?.isInitializing ?? false;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export const lspManager = LSPServerManager.getInstance();
|
|
141
|
-
|
|
142
|
-
export class LSPClient {
|
|
143
|
-
private proc: Subprocess<"pipe", "pipe", "pipe"> | null = null;
|
|
144
|
-
private buffer: Uint8Array = new Uint8Array(0);
|
|
145
|
-
private pending = new Map<
|
|
146
|
-
number,
|
|
147
|
-
{ resolve: (value: unknown) => void; reject: (error: Error) => void }
|
|
148
|
-
>();
|
|
149
|
-
private requestIdCounter = 0;
|
|
150
|
-
private openedFiles = new Set<string>();
|
|
151
|
-
private stderrBuffer: string[] = [];
|
|
152
|
-
private processExited = false;
|
|
153
|
-
private diagnosticsStore = new Map<string, Diagnostic[]>();
|
|
154
|
-
|
|
155
|
-
constructor(
|
|
156
|
-
private root: string,
|
|
157
|
-
private server: ResolvedServer,
|
|
158
|
-
) {}
|
|
159
|
-
|
|
160
|
-
async start(): Promise<void> {
|
|
161
|
-
this.proc = spawn(this.server.command, {
|
|
162
|
-
stdin: "pipe",
|
|
163
|
-
stdout: "pipe",
|
|
164
|
-
stderr: "pipe",
|
|
165
|
-
cwd: this.root,
|
|
166
|
-
env: {
|
|
167
|
-
...process.env,
|
|
168
|
-
...this.server.env,
|
|
169
|
-
},
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
if (!this.proc) {
|
|
173
|
-
throw new Error(
|
|
174
|
-
`Failed to spawn LSP server: ${this.server.command.join(" ")}`,
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
this.startReading();
|
|
179
|
-
this.startStderrReading();
|
|
180
|
-
|
|
181
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
182
|
-
|
|
183
|
-
if (this.proc.exitCode !== null) {
|
|
184
|
-
const stderr = this.stderrBuffer.join("\n");
|
|
185
|
-
throw new Error(
|
|
186
|
-
`LSP server exited immediately with code ${this.proc.exitCode}${stderr ? `\nstderr: ${stderr}` : ""}`,
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
private startReading(): void {
|
|
192
|
-
if (!this.proc) return;
|
|
193
|
-
|
|
194
|
-
const reader = this.proc.stdout.getReader();
|
|
195
|
-
const read = async () => {
|
|
196
|
-
try {
|
|
197
|
-
while (true) {
|
|
198
|
-
const { done, value } = await reader.read();
|
|
199
|
-
if (done) {
|
|
200
|
-
this.processExited = true;
|
|
201
|
-
this.rejectAllPending("LSP server stdout closed");
|
|
202
|
-
break;
|
|
203
|
-
}
|
|
204
|
-
const newBuf = new Uint8Array(this.buffer.length + value.length);
|
|
205
|
-
newBuf.set(this.buffer);
|
|
206
|
-
newBuf.set(value, this.buffer.length);
|
|
207
|
-
this.buffer = newBuf;
|
|
208
|
-
this.processBuffer();
|
|
209
|
-
}
|
|
210
|
-
} catch (err) {
|
|
211
|
-
this.processExited = true;
|
|
212
|
-
this.rejectAllPending(`LSP stdout read error: ${err}`);
|
|
213
|
-
}
|
|
214
|
-
};
|
|
215
|
-
read();
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
private startStderrReading(): void {
|
|
219
|
-
if (!this.proc) return;
|
|
220
|
-
|
|
221
|
-
const reader = this.proc.stderr.getReader();
|
|
222
|
-
const read = async () => {
|
|
223
|
-
const decoder = new TextDecoder();
|
|
224
|
-
try {
|
|
225
|
-
while (true) {
|
|
226
|
-
const { done, value } = await reader.read();
|
|
227
|
-
if (done) break;
|
|
228
|
-
const text = decoder.decode(value);
|
|
229
|
-
this.stderrBuffer.push(text);
|
|
230
|
-
if (this.stderrBuffer.length > 100) {
|
|
231
|
-
this.stderrBuffer.shift();
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
} catch {}
|
|
235
|
-
};
|
|
236
|
-
read();
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
private rejectAllPending(reason: string): void {
|
|
240
|
-
for (const [id, handler] of this.pending) {
|
|
241
|
-
handler.reject(new Error(reason));
|
|
242
|
-
this.pending.delete(id);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
private findSequence(haystack: Uint8Array, needle: number[]): number {
|
|
247
|
-
outer: for (let i = 0; i <= haystack.length - needle.length; i++) {
|
|
248
|
-
for (let j = 0; j < needle.length; j++) {
|
|
249
|
-
if (haystack[i + j] !== needle[j]) continue outer;
|
|
250
|
-
}
|
|
251
|
-
return i;
|
|
252
|
-
}
|
|
253
|
-
return -1;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
private processBuffer(): void {
|
|
257
|
-
const decoder = new TextDecoder();
|
|
258
|
-
const CONTENT_LENGTH = [
|
|
259
|
-
67, 111, 110, 116, 101, 110, 116, 45, 76, 101, 110, 103, 116, 104, 58,
|
|
260
|
-
];
|
|
261
|
-
const CRLF_CRLF = [13, 10, 13, 10];
|
|
262
|
-
const LF_LF = [10, 10];
|
|
263
|
-
|
|
264
|
-
while (true) {
|
|
265
|
-
const headerStart = this.findSequence(this.buffer, CONTENT_LENGTH);
|
|
266
|
-
if (headerStart === -1) break;
|
|
267
|
-
if (headerStart > 0) this.buffer = this.buffer.slice(headerStart);
|
|
268
|
-
|
|
269
|
-
let headerEnd = this.findSequence(this.buffer, CRLF_CRLF);
|
|
270
|
-
let sepLen = 4;
|
|
271
|
-
if (headerEnd === -1) {
|
|
272
|
-
headerEnd = this.findSequence(this.buffer, LF_LF);
|
|
273
|
-
sepLen = 2;
|
|
274
|
-
}
|
|
275
|
-
if (headerEnd === -1) break;
|
|
276
|
-
|
|
277
|
-
const header = decoder.decode(this.buffer.slice(0, headerEnd));
|
|
278
|
-
const match = header.match(/Content-Length:\s*(\d+)/i);
|
|
279
|
-
if (!match) break;
|
|
280
|
-
|
|
281
|
-
const len = Number.parseInt(match[1], 10);
|
|
282
|
-
const start = headerEnd + sepLen;
|
|
283
|
-
const end = start + len;
|
|
284
|
-
if (this.buffer.length < end) break;
|
|
285
|
-
|
|
286
|
-
const content = decoder.decode(this.buffer.slice(start, end));
|
|
287
|
-
this.buffer = this.buffer.slice(end);
|
|
288
|
-
|
|
289
|
-
try {
|
|
290
|
-
const msg = JSON.parse(content);
|
|
291
|
-
|
|
292
|
-
if ("method" in msg && !("id" in msg)) {
|
|
293
|
-
if (
|
|
294
|
-
msg.method === "textDocument/publishDiagnostics" &&
|
|
295
|
-
msg.params?.uri
|
|
296
|
-
) {
|
|
297
|
-
this.diagnosticsStore.set(
|
|
298
|
-
msg.params.uri,
|
|
299
|
-
msg.params.diagnostics ?? [],
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
} else if ("id" in msg && "method" in msg) {
|
|
303
|
-
this.handleServerRequest(msg.id, msg.method, msg.params);
|
|
304
|
-
} else if ("id" in msg && this.pending.has(msg.id)) {
|
|
305
|
-
const handler = this.pending.get(msg.id);
|
|
306
|
-
this.pending.delete(msg.id);
|
|
307
|
-
if (handler) {
|
|
308
|
-
if ("error" in msg) {
|
|
309
|
-
handler.reject(new Error(msg.error.message));
|
|
310
|
-
} else {
|
|
311
|
-
handler.resolve(msg.result);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
} catch {}
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
private send(method: string, params?: unknown): Promise<unknown> {
|
|
320
|
-
if (!this.proc) throw new Error("LSP client not started");
|
|
321
|
-
|
|
322
|
-
if (this.processExited || this.proc.exitCode !== null) {
|
|
323
|
-
const stderr = this.stderrBuffer.slice(-10).join("\n");
|
|
324
|
-
throw new Error(
|
|
325
|
-
`LSP server already exited (code: ${this.proc.exitCode})${stderr ? `\nstderr: ${stderr}` : ""}`,
|
|
326
|
-
);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
const id = ++this.requestIdCounter;
|
|
330
|
-
const msg = JSON.stringify({ jsonrpc: "2.0", id, method, params });
|
|
331
|
-
const header = `Content-Length: ${Buffer.byteLength(msg)}\r\n\r\n`;
|
|
332
|
-
this.proc.stdin.write(header + msg);
|
|
333
|
-
|
|
334
|
-
return new Promise((resolve, reject) => {
|
|
335
|
-
this.pending.set(id, { resolve, reject });
|
|
336
|
-
setTimeout(() => {
|
|
337
|
-
if (this.pending.has(id)) {
|
|
338
|
-
this.pending.delete(id);
|
|
339
|
-
const stderr = this.stderrBuffer.slice(-5).join("\n");
|
|
340
|
-
reject(
|
|
341
|
-
new Error(
|
|
342
|
-
`LSP request timeout (method: ${method})${stderr ? `\nrecent stderr: ${stderr}` : ""}`,
|
|
343
|
-
),
|
|
344
|
-
);
|
|
345
|
-
}
|
|
346
|
-
}, 15000);
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
private notify(method: string, params?: unknown): void {
|
|
351
|
-
if (!this.proc) return;
|
|
352
|
-
if (this.processExited || this.proc.exitCode !== null) return;
|
|
353
|
-
|
|
354
|
-
const msg = JSON.stringify({ jsonrpc: "2.0", method, params });
|
|
355
|
-
this.proc.stdin.write(
|
|
356
|
-
`Content-Length: ${Buffer.byteLength(msg)}\r\n\r\n${msg}`,
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
private respond(id: number | string, result: unknown): void {
|
|
361
|
-
if (!this.proc) return;
|
|
362
|
-
if (this.processExited || this.proc.exitCode !== null) return;
|
|
363
|
-
|
|
364
|
-
const msg = JSON.stringify({ jsonrpc: "2.0", id, result });
|
|
365
|
-
this.proc.stdin.write(
|
|
366
|
-
`Content-Length: ${Buffer.byteLength(msg)}\r\n\r\n${msg}`,
|
|
367
|
-
);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
private handleServerRequest(
|
|
371
|
-
id: number | string,
|
|
372
|
-
method: string,
|
|
373
|
-
params?: unknown,
|
|
374
|
-
): void {
|
|
375
|
-
if (method === "workspace/configuration") {
|
|
376
|
-
const items =
|
|
377
|
-
(params as { items?: Array<{ section?: string }> })?.items ?? [];
|
|
378
|
-
const result = items.map((item) => {
|
|
379
|
-
if (item.section === "json") return { validate: { enable: true } };
|
|
380
|
-
return {};
|
|
381
|
-
});
|
|
382
|
-
this.respond(id, result);
|
|
383
|
-
} else if (method === "client/registerCapability") {
|
|
384
|
-
this.respond(id, null);
|
|
385
|
-
} else if (method === "window/workDoneProgress/create") {
|
|
386
|
-
this.respond(id, null);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
async initialize(): Promise<void> {
|
|
391
|
-
const rootUri = `file://${this.root}`;
|
|
392
|
-
await this.send("initialize", {
|
|
393
|
-
processId: process.pid,
|
|
394
|
-
rootUri,
|
|
395
|
-
rootPath: this.root,
|
|
396
|
-
workspaceFolders: [{ uri: rootUri, name: "workspace" }],
|
|
397
|
-
capabilities: {
|
|
398
|
-
textDocument: {
|
|
399
|
-
hover: { contentFormat: ["markdown", "plaintext"] },
|
|
400
|
-
definition: { linkSupport: true },
|
|
401
|
-
references: {},
|
|
402
|
-
documentSymbol: { hierarchicalDocumentSymbolSupport: true },
|
|
403
|
-
publishDiagnostics: {},
|
|
404
|
-
rename: {
|
|
405
|
-
prepareSupport: true,
|
|
406
|
-
prepareSupportDefaultBehavior: 1,
|
|
407
|
-
honorsChangeAnnotations: true,
|
|
408
|
-
},
|
|
409
|
-
codeAction: {
|
|
410
|
-
codeActionLiteralSupport: {
|
|
411
|
-
codeActionKind: {
|
|
412
|
-
valueSet: [
|
|
413
|
-
"quickfix",
|
|
414
|
-
"refactor",
|
|
415
|
-
"refactor.extract",
|
|
416
|
-
"refactor.inline",
|
|
417
|
-
"refactor.rewrite",
|
|
418
|
-
"source",
|
|
419
|
-
"source.organizeImports",
|
|
420
|
-
"source.fixAll",
|
|
421
|
-
],
|
|
422
|
-
},
|
|
423
|
-
},
|
|
424
|
-
isPreferredSupport: true,
|
|
425
|
-
disabledSupport: true,
|
|
426
|
-
dataSupport: true,
|
|
427
|
-
resolveSupport: {
|
|
428
|
-
properties: ["edit", "command"],
|
|
429
|
-
},
|
|
430
|
-
},
|
|
431
|
-
},
|
|
432
|
-
workspace: {
|
|
433
|
-
symbol: {},
|
|
434
|
-
workspaceFolders: true,
|
|
435
|
-
configuration: true,
|
|
436
|
-
applyEdit: true,
|
|
437
|
-
workspaceEdit: {
|
|
438
|
-
documentChanges: true,
|
|
439
|
-
},
|
|
440
|
-
},
|
|
441
|
-
},
|
|
442
|
-
...this.server.initialization,
|
|
443
|
-
});
|
|
444
|
-
this.notify("initialized");
|
|
445
|
-
this.notify("workspace/didChangeConfiguration", {
|
|
446
|
-
settings: { json: { validate: { enable: true } } },
|
|
447
|
-
});
|
|
448
|
-
await new Promise((r) => setTimeout(r, 300));
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
async openFile(filePath: string): Promise<void> {
|
|
452
|
-
const absPath = resolve(filePath);
|
|
453
|
-
if (this.openedFiles.has(absPath)) return;
|
|
454
|
-
|
|
455
|
-
const text = readFileSync(absPath, "utf-8");
|
|
456
|
-
const ext = extname(absPath);
|
|
457
|
-
const languageId = getLanguageId(ext);
|
|
458
|
-
|
|
459
|
-
this.notify("textDocument/didOpen", {
|
|
460
|
-
textDocument: {
|
|
461
|
-
uri: `file://${absPath}`,
|
|
462
|
-
languageId,
|
|
463
|
-
version: 1,
|
|
464
|
-
text,
|
|
465
|
-
},
|
|
466
|
-
});
|
|
467
|
-
this.openedFiles.add(absPath);
|
|
468
|
-
|
|
469
|
-
await new Promise((r) => setTimeout(r, 1000));
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
async hover(
|
|
473
|
-
filePath: string,
|
|
474
|
-
line: number,
|
|
475
|
-
character: number,
|
|
476
|
-
): Promise<unknown> {
|
|
477
|
-
const absPath = resolve(filePath);
|
|
478
|
-
await this.openFile(absPath);
|
|
479
|
-
return this.send("textDocument/hover", {
|
|
480
|
-
textDocument: { uri: `file://${absPath}` },
|
|
481
|
-
position: { line: line - 1, character },
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
async definition(
|
|
486
|
-
filePath: string,
|
|
487
|
-
line: number,
|
|
488
|
-
character: number,
|
|
489
|
-
): Promise<unknown> {
|
|
490
|
-
const absPath = resolve(filePath);
|
|
491
|
-
await this.openFile(absPath);
|
|
492
|
-
return this.send("textDocument/definition", {
|
|
493
|
-
textDocument: { uri: `file://${absPath}` },
|
|
494
|
-
position: { line: line - 1, character },
|
|
495
|
-
});
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
async references(
|
|
499
|
-
filePath: string,
|
|
500
|
-
line: number,
|
|
501
|
-
character: number,
|
|
502
|
-
includeDeclaration = true,
|
|
503
|
-
): Promise<unknown> {
|
|
504
|
-
const absPath = resolve(filePath);
|
|
505
|
-
await this.openFile(absPath);
|
|
506
|
-
return this.send("textDocument/references", {
|
|
507
|
-
textDocument: { uri: `file://${absPath}` },
|
|
508
|
-
position: { line: line - 1, character },
|
|
509
|
-
context: { includeDeclaration },
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
async documentSymbols(filePath: string): Promise<unknown> {
|
|
514
|
-
const absPath = resolve(filePath);
|
|
515
|
-
await this.openFile(absPath);
|
|
516
|
-
return this.send("textDocument/documentSymbol", {
|
|
517
|
-
textDocument: { uri: `file://${absPath}` },
|
|
518
|
-
});
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
async workspaceSymbols(query: string): Promise<unknown> {
|
|
522
|
-
return this.send("workspace/symbol", { query });
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
async diagnostics(filePath: string): Promise<{ items: Diagnostic[] }> {
|
|
526
|
-
const absPath = resolve(filePath);
|
|
527
|
-
const uri = `file://${absPath}`;
|
|
528
|
-
await this.openFile(absPath);
|
|
529
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
530
|
-
|
|
531
|
-
try {
|
|
532
|
-
const result = await this.send("textDocument/diagnostic", {
|
|
533
|
-
textDocument: { uri },
|
|
534
|
-
});
|
|
535
|
-
if (result && typeof result === "object" && "items" in result) {
|
|
536
|
-
return result as { items: Diagnostic[] };
|
|
537
|
-
}
|
|
538
|
-
} catch {}
|
|
539
|
-
|
|
540
|
-
return { items: this.diagnosticsStore.get(uri) ?? [] };
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
async prepareRename(
|
|
544
|
-
filePath: string,
|
|
545
|
-
line: number,
|
|
546
|
-
character: number,
|
|
547
|
-
): Promise<unknown> {
|
|
548
|
-
const absPath = resolve(filePath);
|
|
549
|
-
await this.openFile(absPath);
|
|
550
|
-
return this.send("textDocument/prepareRename", {
|
|
551
|
-
textDocument: { uri: `file://${absPath}` },
|
|
552
|
-
position: { line: line - 1, character },
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
async rename(
|
|
557
|
-
filePath: string,
|
|
558
|
-
line: number,
|
|
559
|
-
character: number,
|
|
560
|
-
newName: string,
|
|
561
|
-
): Promise<unknown> {
|
|
562
|
-
const absPath = resolve(filePath);
|
|
563
|
-
await this.openFile(absPath);
|
|
564
|
-
return this.send("textDocument/rename", {
|
|
565
|
-
textDocument: { uri: `file://${absPath}` },
|
|
566
|
-
position: { line: line - 1, character },
|
|
567
|
-
newName,
|
|
568
|
-
});
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
async codeAction(
|
|
572
|
-
filePath: string,
|
|
573
|
-
startLine: number,
|
|
574
|
-
startChar: number,
|
|
575
|
-
endLine: number,
|
|
576
|
-
endChar: number,
|
|
577
|
-
only?: string[],
|
|
578
|
-
): Promise<unknown> {
|
|
579
|
-
const absPath = resolve(filePath);
|
|
580
|
-
await this.openFile(absPath);
|
|
581
|
-
return this.send("textDocument/codeAction", {
|
|
582
|
-
textDocument: { uri: `file://${absPath}` },
|
|
583
|
-
range: {
|
|
584
|
-
start: { line: startLine - 1, character: startChar },
|
|
585
|
-
end: { line: endLine - 1, character: endChar },
|
|
586
|
-
},
|
|
587
|
-
context: {
|
|
588
|
-
diagnostics: [],
|
|
589
|
-
only,
|
|
590
|
-
},
|
|
591
|
-
});
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
async codeActionResolve(codeAction: unknown): Promise<unknown> {
|
|
595
|
-
return this.send("codeAction/resolve", codeAction);
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
isAlive(): boolean {
|
|
599
|
-
return (
|
|
600
|
-
this.proc !== null && !this.processExited && this.proc.exitCode === null
|
|
601
|
-
);
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
async stop(): Promise<void> {
|
|
605
|
-
try {
|
|
606
|
-
this.notify("shutdown", {});
|
|
607
|
-
this.notify("exit");
|
|
608
|
-
} catch {}
|
|
609
|
-
this.proc?.kill();
|
|
610
|
-
this.proc = null;
|
|
611
|
-
this.processExited = true;
|
|
612
|
-
this.diagnosticsStore.clear();
|
|
613
|
-
}
|
|
614
|
-
}
|