From 1954b59167d0c743efa2fb3a0a47e57a025b4221 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Sat, 9 Aug 2025 11:04:58 -0500 Subject: [PATCH] feat: eslint lsp (#1744) --- packages/opencode/src/lsp/server.ts | 60 ++++++++++++++++++++++ packages/opencode/src/tool/edit.ts | 1 + packages/web/src/content/docs/docs/lsp.mdx | 7 +-- 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index f4648f0c..cf78fe7b 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -65,6 +65,66 @@ export namespace LSPServer { }, } + export const ESLint: Info = { + id: "eslint", + root: NearestRoot([ + "eslint.config.js", + "eslint.config.mjs", + "eslint.config.cjs", + "eslint.config.ts", + "eslint.config.mts", + "eslint.config.cts", + ".eslintrc.js", + ".eslintrc.cjs", + ".eslintrc.yaml", + ".eslintrc.yml", + ".eslintrc.json", + "package.json", + ]), + extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"], + async spawn(app, root) { + const eslint = await Bun.resolve("eslint", app.path.cwd).catch(() => {}) + if (!eslint) return + const serverPath = path.join(Global.Path.bin, "vscode-eslint", "server", "out", "eslintServer.js") + if (!(await Bun.file(serverPath).exists())) { + log.info("downloading and building VS Code ESLint server") + const response = await fetch("https://github.com/microsoft/vscode-eslint/archive/refs/heads/main.zip") + if (!response.ok) return + + const zipPath = path.join(Global.Path.bin, "vscode-eslint.zip") + await Bun.file(zipPath).write(response) + + await $`unzip -o -q ${zipPath}`.cwd(Global.Path.bin).nothrow() + await fs.rm(zipPath, { force: true }) + + const extractedPath = path.join(Global.Path.bin, "vscode-eslint-main") + const finalPath = path.join(Global.Path.bin, "vscode-eslint") + + if (await Bun.file(finalPath).exists()) { + await fs.rm(finalPath, { force: true, recursive: true }) + } + await fs.rename(extractedPath, finalPath) + + await $`npm install`.cwd(finalPath).quiet() + await $`npm run compile`.cwd(finalPath).quiet() + + log.info("installed VS Code ESLint server", { serverPath }) + } + + const proc = spawn(BunProc.which(), ["--max-old-space-size=8192", serverPath, "--stdio"], { + cwd: root, + env: { + ...process.env, + BUN_BE_BUN: "1", + }, + }) + + return { + process: proc, + } + }, + } + export const Gopls: Info = { id: "golang", root: async (file, app) => { diff --git a/packages/opencode/src/tool/edit.ts b/packages/opencode/src/tool/edit.ts index fbda9e4d..d3883b53 100644 --- a/packages/opencode/src/tool/edit.ts +++ b/packages/opencode/src/tool/edit.ts @@ -111,6 +111,7 @@ export const EditTool = Tool.define("edit", { continue } output += `\n\n${file}\n${issues + // TODO: may want to make more leniant for eslint .filter((item) => item.severity === 1) .map(LSP.Diagnostic.pretty) .join("\n")}\n\n` diff --git a/packages/web/src/content/docs/docs/lsp.mdx b/packages/web/src/content/docs/docs/lsp.mdx index 4783683b..76edad3e 100644 --- a/packages/web/src/content/docs/docs/lsp.mdx +++ b/packages/web/src/content/docs/docs/lsp.mdx @@ -11,9 +11,10 @@ opencode integrates with your Language Server Protocol (LSP) to help the LLM int opencode comes with several built-in LSP servers for popular languages: -| LSP Server | Extensions | Requirements | +| LSP Server | Extensions | Requirements | | ---------- | -------------------------------------------- | ----------------------------------- | | typescript | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts | `typescript` dependency in project | +| eslint | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts | `eslint` dependency in project | | gopls | .go | `go` command available | | ruby-lsp | .rb, .rake, .gemspec, .ru | `ruby` and `gem` commands available | | pyright | .py, .pyi | `pyright` dependency installed | @@ -41,7 +42,7 @@ You can customize LSP servers through the `lsp` section in your opencode config. ```json title="opencode.json" { "$schema": "https://opencode.ai/config.json", - "lsp": { } + "lsp": {} } ``` @@ -49,7 +50,7 @@ Each LSP server supports the following: | Property | Type | Description | | ---------------- | -------- | ------------------------------------------------- | -| `disabled` | boolean | Set this to `true` to disable the LSP server | +| `disabled` | boolean | Set this to `true` to disable the LSP server | | `command` | string[] | The command to start the LSP server | | `extensions` | string[] | File extensions this LSP server should handle | | `env` | object | Environment variables to set when starting server |