From f3da73553c45f17e04b1e77cb13eb0fca714d1bd Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 30 May 2025 20:47:56 -0400 Subject: sync --- js/src/lsp/client.ts | 208 ------------------------------------------------- js/src/lsp/index.ts | 131 ------------------------------- js/src/lsp/language.ts | 89 --------------------- 3 files changed, 428 deletions(-) delete mode 100644 js/src/lsp/client.ts delete mode 100644 js/src/lsp/index.ts delete mode 100644 js/src/lsp/language.ts (limited to 'js/src/lsp') 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>; - - 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(); - 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(); - - 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(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((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; - } -} diff --git a/js/src/lsp/index.ts b/js/src/lsp/index.ts deleted file mode 100644 index e3344a934..000000000 --- a/js/src/lsp/index.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { App } from "../app/app"; -import { Log } from "../util/log"; -import { LSPClient } from "./client"; -import path from "path"; - -export namespace LSP { - const log = Log.create({ service: "lsp" }); - - const state = App.state( - "lsp", - async () => { - log.info("initializing"); - 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; - } - - export async function hover(input: { - file: string; - line: number; - character: number; - }) { - return run((client) => { - return client.connection.sendRequest("textDocument/hover", { - textDocument: { - uri: `file://${input.file}`, - }, - position: { - line: input.line, - character: input.character, - }, - }); - }); - } - - 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}`; - } - } -} diff --git a/js/src/lsp/language.ts b/js/src/lsp/language.ts deleted file mode 100644 index e28d7a79c..000000000 --- a/js/src/lsp/language.ts +++ /dev/null @@ -1,89 +0,0 @@ -export const LANGUAGE_EXTENSIONS: Record = { - ".abap": "abap", - ".bat": "bat", - ".bib": "bibtex", - ".bibtex": "bibtex", - ".clj": "clojure", - ".coffee": "coffeescript", - ".c": "c", - ".cpp": "cpp", - ".cxx": "cpp", - ".cc": "cpp", - ".c++": "cpp", - ".cs": "csharp", - ".css": "css", - ".d": "d", - ".pas": "pascal", - ".pascal": "pascal", - ".diff": "diff", - ".patch": "diff", - ".dart": "dart", - ".dockerfile": "dockerfile", - ".ex": "elixir", - ".exs": "elixir", - ".erl": "erlang", - ".hrl": "erlang", - ".fs": "fsharp", - ".fsi": "fsharp", - ".fsx": "fsharp", - ".fsscript": "fsharp", - ".gitcommit": "git-commit", - ".gitrebase": "git-rebase", - ".go": "go", - ".groovy": "groovy", - ".hbs": "handlebars", - ".handlebars": "handlebars", - ".hs": "haskell", - ".html": "html", - ".htm": "html", - ".ini": "ini", - ".java": "java", - ".js": "javascript", - ".jsx": "javascriptreact", - ".json": "json", - ".tex": "latex", - ".latex": "latex", - ".less": "less", - ".lua": "lua", - ".makefile": "makefile", - makefile: "makefile", - ".md": "markdown", - ".markdown": "markdown", - ".m": "objective-c", - ".mm": "objective-cpp", - ".pl": "perl", - ".pm": "perl6", - ".php": "php", - ".ps1": "powershell", - ".psm1": "powershell", - ".pug": "jade", - ".jade": "jade", - ".py": "python", - ".r": "r", - ".cshtml": "razor", - ".razor": "razor", - ".rb": "ruby", - ".rs": "rust", - ".scss": "scss", - ".sass": "sass", - ".scala": "scala", - ".shader": "shaderlab", - ".sh": "shellscript", - ".bash": "shellscript", - ".zsh": "shellscript", - ".ksh": "shellscript", - ".sql": "sql", - ".swift": "swift", - ".ts": "typescript", - ".tsx": "typescriptreact", - ".mts": "typescript", - ".cts": "typescript", - ".mtsx": "typescriptreact", - ".ctsx": "typescriptreact", - ".xml": "xml", - ".xsl": "xsl", - ".yaml": "yaml", - ".yml": "yaml", - ".mjs": "javascript", - ".cjs": "javascript", -} as const; -- cgit v1.2.3