summaryrefslogtreecommitdiffhomepage
path: root/js/src/lsp
diff options
context:
space:
mode:
authorDax Raad <[email protected]>2025-05-30 20:47:56 -0400
committerDax Raad <[email protected]>2025-05-30 20:48:36 -0400
commitf3da73553c45f17e04b1e77cb13eb0fca714d1bd (patch)
treea24317a19e1ab2a89da50db669dc6894f15d00d1 /js/src/lsp
parent9a26b3058ffc1023e5c7e54b6d571c903d15888e (diff)
downloadopencode-f3da73553c45f17e04b1e77cb13eb0fca714d1bd.tar.gz
opencode-f3da73553c45f17e04b1e77cb13eb0fca714d1bd.zip
sync
Diffstat (limited to 'js/src/lsp')
-rw-r--r--js/src/lsp/client.ts208
-rw-r--r--js/src/lsp/index.ts131
-rw-r--r--js/src/lsp/language.ts89
3 files changed, 0 insertions, 428 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;
- }
-}
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<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;
- }
-
- 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<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}`;
- }
- }
-}
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<string, string> = {
- ".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;