From 483fcdaddba6e6da677079e59367a1eb4a996d66 Mon Sep 17 00:00:00 2001 From: Hieu Nguyen Date: Fri, 24 Oct 2025 22:37:11 +0700 Subject: [PATCH] feat: support lua lsp (#3402) --- packages/opencode/src/lsp/server.ts | 122 ++++++++++++++++++++++++++ packages/web/src/content/docs/lsp.mdx | 1 + 2 files changed, 123 insertions(+) diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index 49b55aba..3969a64e 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -838,4 +838,126 @@ export namespace LSPServer { } }, } + + export const LuaLS: Info = { + id: "lua-ls", + root: NearestRoot([ + ".luarc.json", + ".luarc.jsonc", + ".luacheckrc", + ".stylua.toml", + "stylua.toml", + "selene.toml", + "selene.yml", + ]), + extensions: [".lua"], + async spawn(root) { + let bin = Bun.which("lua-language-server", { + PATH: process.env["PATH"] + ":" + Global.Path.bin, + }) + + if (!bin) { + if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return + log.info("downloading lua-language-server from GitHub releases") + + const releaseResponse = await fetch("https://api.github.com/repos/LuaLS/lua-language-server/releases/latest") + if (!releaseResponse.ok) { + log.error("Failed to fetch lua-language-server release info") + return + } + + const release = await releaseResponse.json() + + const platform = process.platform + const arch = process.arch + let assetName = "" + + let lualsArch: string = arch + if (arch === "arm64") lualsArch = "arm64" + else if (arch === "x64") lualsArch = "x64" + else if (arch === "ia32") lualsArch = "ia32" + + let lualsPlatform: string = platform + if (platform === "darwin") lualsPlatform = "darwin" + else if (platform === "linux") lualsPlatform = "linux" + else if (platform === "win32") lualsPlatform = "win32" + + const ext = platform === "win32" ? "zip" : "tar.gz" + + assetName = `lua-language-server-${release.tag_name}-${lualsPlatform}-${lualsArch}.${ext}` + + const supportedCombos = [ + "darwin-arm64.tar.gz", + "darwin-x64.tar.gz", + "linux-x64.tar.gz", + "linux-arm64.tar.gz", + "win32-x64.zip", + "win32-ia32.zip", + ] + + const assetSuffix = `${lualsPlatform}-${lualsArch}.${ext}` + if (!supportedCombos.includes(assetSuffix)) { + log.error(`Platform ${platform} and architecture ${arch} is not supported by lua-language-server`) + return + } + + const asset = release.assets.find((a: any) => a.name === assetName) + if (!asset) { + log.error(`Could not find asset ${assetName} in latest lua-language-server release`) + return + } + + const downloadUrl = asset.browser_download_url + const downloadResponse = await fetch(downloadUrl) + if (!downloadResponse.ok) { + log.error("Failed to download lua-language-server") + return + } + + const tempPath = path.join(Global.Path.bin, assetName) + await Bun.file(tempPath).write(downloadResponse) + + // Unlike zls which is a single self-contained binary, + // lua-language-server needs supporting files (meta/, locale/, etc.) + // Extract entire archive to dedicated directory to preserve all files + const installDir = path.join(Global.Path.bin, `lua-language-server-${lualsArch}-${lualsPlatform}`) + + // Remove old installation if exists + const stats = await fs.stat(installDir).catch(() => undefined) + if (stats) { + await fs.rm(installDir, { force: true, recursive: true }) + } + + await fs.mkdir(installDir, { recursive: true }) + + if (ext === "zip") { + await $`unzip -o -q ${tempPath} -d ${installDir}`.quiet().nothrow() + } else { + await $`tar -xzf ${tempPath} -C ${installDir}`.nothrow() + } + + await fs.rm(tempPath, { force: true }) + + // Binary is located in bin/ subdirectory within the extracted archive + bin = path.join(installDir, "bin", "lua-language-server" + (platform === "win32" ? ".exe" : "")) + + if (!(await Bun.file(bin).exists())) { + log.error("Failed to extract lua-language-server binary") + return + } + + if (platform !== "win32") { + await $`chmod +x ${bin}`.nothrow() + } + + log.info(`installed lua-language-server`, { bin }) + } + + return { + process: spawn(bin, { + cwd: root, + }), + } + }, + } } diff --git a/packages/web/src/content/docs/lsp.mdx b/packages/web/src/content/docs/lsp.mdx index da7d5dde..97aadab5 100644 --- a/packages/web/src/content/docs/lsp.mdx +++ b/packages/web/src/content/docs/lsp.mdx @@ -28,6 +28,7 @@ OpenCode comes with several built-in LSP servers for popular languages: | svelte | .svelte | Auto-installs for Svelte projects | | astro | .astro | Auto-installs for Astro projects | | jdtls | .java | `Java SDK (version 21+)` installed | +| lua-ls | .lua | Auto-installs for Lua projects | LSP servers are automatically enabled when one of the above file extensions are detected and the requirements are met.