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(); 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 = {}; 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( input: (client: LSPClient.Info) => Promise, ): Promise { 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; }[] = [ { 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}`; } } }