diff options
| author | Dax Raad <[email protected]> | 2025-05-30 20:47:56 -0400 |
|---|---|---|
| committer | Dax Raad <[email protected]> | 2025-05-30 20:48:36 -0400 |
| commit | f3da73553c45f17e04b1e77cb13eb0fca714d1bd (patch) | |
| tree | a24317a19e1ab2a89da50db669dc6894f15d00d1 /js/src/lsp/client.ts | |
| parent | 9a26b3058ffc1023e5c7e54b6d571c903d15888e (diff) | |
| download | opencode-f3da73553c45f17e04b1e77cb13eb0fca714d1bd.tar.gz opencode-f3da73553c45f17e04b1e77cb13eb0fca714d1bd.zip | |
sync
Diffstat (limited to 'js/src/lsp/client.ts')
| -rw-r--r-- | js/src/lsp/client.ts | 208 |
1 files changed, 0 insertions, 208 deletions
diff --git a/js/src/lsp/client.ts b/js/src/lsp/client.ts deleted file mode 100644 index 82caa82a2..000000000 --- a/js/src/lsp/client.ts +++ /dev/null @@ -1,208 +0,0 @@ -import { spawn } from "child_process"; -import path from "path"; -import { - createMessageConnection, - StreamMessageReader, - StreamMessageWriter, -} from "vscode-jsonrpc/node"; -import type { Diagnostic as VSCodeDiagnostic } from "vscode-languageserver-types"; -import { App } from "../app/app"; -import { Log } from "../util/log"; -import { LANGUAGE_EXTENSIONS } from "./language"; -import { Bus } from "../bus"; -import z from "zod"; - -export namespace LSPClient { - const log = Log.create({ service: "lsp.client" }); - - export type Info = Awaited<ReturnType<typeof create>>; - - export type Diagnostic = VSCodeDiagnostic; - - export const Event = { - Diagnostics: Bus.event( - "lsp.client.diagnostics", - z.object({ - serverID: z.string(), - path: z.string(), - }), - ), - }; - - export async function create(input: { cmd: string[]; serverID: string }) { - log.info("starting client", input); - - const app = await App.use(); - const [command, ...args] = input.cmd; - const server = spawn(command, args, { - stdio: ["pipe", "pipe", "pipe"], - cwd: app.root, - }); - - const connection = createMessageConnection( - new StreamMessageReader(server.stdout), - new StreamMessageWriter(server.stdin), - ); - - const diagnostics = new Map<string, Diagnostic[]>(); - connection.onNotification("textDocument/publishDiagnostics", (params) => { - const path = new URL(params.uri).pathname; - log.info("textDocument/publishDiagnostics", { - path, - }); - const exists = diagnostics.has(path); - diagnostics.set(path, params.diagnostics); - // servers seem to send one blank publishDiagnostics event before the first real one - if (!exists && !params.diagnostics.length) return; - Bus.publish(Event.Diagnostics, { path, serverID: input.serverID }); - }); - connection.listen(); - - await connection.sendRequest("initialize", { - processId: server.pid, - initializationOptions: { - workspaceFolders: [ - { - name: "workspace", - uri: "file://" + app.root, - }, - ], - tsserver: { - path: require.resolve("typescript/lib/tsserver.js"), - }, - }, - capabilities: { - workspace: { - configuration: true, - didChangeConfiguration: { - dynamicRegistration: true, - }, - didChangeWatchedFiles: { - dynamicRegistration: true, - relativePatternSupport: true, - }, - }, - textDocument: { - synchronization: { - dynamicRegistration: true, - didSave: true, - }, - completion: { - completionItem: {}, - }, - codeLens: { - dynamicRegistration: true, - }, - documentSymbol: {}, - codeAction: { - codeActionLiteralSupport: { - codeActionKind: { - valueSet: [], - }, - }, - }, - publishDiagnostics: { - versionSupport: true, - }, - semanticTokens: { - requests: { - range: {}, - full: {}, - }, - tokenTypes: [], - tokenModifiers: [], - formats: [], - }, - }, - window: {}, - }, - }); - await connection.sendNotification("initialized", {}); - log.info("initialized"); - - const files = new Set<string>(); - - const result = { - get clientID() { - return input.serverID; - }, - get connection() { - return connection; - }, - notify: { - async open(input: { path: string }) { - const file = Bun.file(input.path); - const text = await file.text(); - const opened = files.has(input.path); - if (!opened) { - log.info("textDocument/didOpen", input); - diagnostics.delete(input.path); - const extension = path.extname(input.path); - const languageId = LANGUAGE_EXTENSIONS[extension] ?? "plaintext"; - await connection.sendNotification("textDocument/didOpen", { - textDocument: { - uri: `file://` + input.path, - languageId, - version: Date.now(), - text, - }, - }); - files.add(input.path); - return; - } - - log.info("textDocument/didChange", input); - diagnostics.delete(input.path); - await connection.sendNotification("textDocument/didChange", { - textDocument: { - uri: `file://` + input.path, - version: Date.now(), - }, - contentChanges: [ - { - text, - }, - ], - }); - }, - }, - get diagnostics() { - return diagnostics; - }, - async waitForDiagnostics(input: { path: string }) { - log.info("waiting for diagnostics", input); - let unsub: () => void; - let timeout: NodeJS.Timeout; - return await Promise.race([ - new Promise<void>(async (resolve) => { - unsub = Bus.subscribe(Event.Diagnostics, (event) => { - if ( - event.properties.path === input.path && - event.properties.serverID === result.clientID - ) { - log.info("got diagnostics", input); - clearTimeout(timeout); - unsub?.(); - resolve(); - } - }); - }), - new Promise<void>((resolve) => { - timeout = setTimeout(() => { - log.info("timed out refreshing diagnostics", input); - unsub?.(); - resolve(); - }, 5000); - }), - ]); - }, - async shutdown() { - log.info("shutting down"); - connection.end(); - connection.dispose(); - }, - }; - - return result; - } -} |
