opencode/js/src/lsp/index.ts
2025-05-26 21:44:55 -04:00

110 lines
2.7 KiB
TypeScript

import { App } from "../app";
import { Log } from "../util/log";
import { LSPClient } from "./client";
import path from "node:path";
export namespace LSP {
const log = Log.create({ service: "lsp" });
const state = App.state(
"lsp",
async () => {
const clients = new Map<string, LSPClient.Info>();
return {
clients,
};
},
async (state) => {
for (const client of state.clients.values()) {
await client.shutdown();
}
},
);
export async function file(input: string) {
const extension = path.parse(input).ext;
const s = await state();
const matches = AUTO.filter((x) => x.extensions.includes(extension));
for (const match of matches) {
const existing = s.clients.get(match.id);
if (existing) continue;
const client = await LSPClient.create({
cmd: match.command,
serverID: match.id,
});
s.clients.set(match.id, client);
}
await run(async (client) => {
const wait = client.waitForDiagnostics({ path: input });
await client.notify.open({ path: input });
return wait;
});
}
export async function diagnostics() {
const results: Record<string, LSPClient.Diagnostic[]> = {};
for (const result of await run(async (client) => client.diagnostics)) {
for (const [path, diagnostics] of result.entries()) {
const arr = results[path] || [];
arr.push(...diagnostics);
results[path] = arr;
}
}
return results;
}
async function run<T>(
input: (client: LSPClient.Info) => Promise<T>,
): Promise<T[]> {
const clients = await state().then((x) => [...x.clients.values()]);
const tasks = clients.map((x) => input(x));
return Promise.all(tasks);
}
const AUTO: {
id: string;
command: string[];
extensions: string[];
install?: () => Promise<void>;
}[] = [
{
id: "typescript",
command: ["bun", "x", "typescript-language-server", "--stdio"],
extensions: [
".ts",
".tsx",
".js",
".jsx",
".mjs",
".cjs",
".mts",
".cts",
".mtsx",
".ctsx",
],
},
{
id: "golang",
command: ["gopls"],
extensions: [".go"],
},
];
export namespace Diagnostic {
export function pretty(diagnostic: LSPClient.Diagnostic) {
const severityMap = {
1: "ERROR",
2: "WARN",
3: "INFO",
4: "HINT",
};
const severity = severityMap[diagnostic.severity || 1];
const line = diagnostic.range.start.line + 1;
const col = diagnostic.range.start.character + 1;
return `${severity} [${line}:${col}] ${diagnostic.message}`;
}
}
}