From 7c3ba390a49d0898d4d8c42f99f3af433023e327 Mon Sep 17 00:00:00 2001 From: Mathews Date: Fri, 20 Feb 2026 10:42:31 -0500 Subject: [PATCH 1/4] feat: add roslyn-language-server as opt-in C# LSP alternative --- packages/opencode/src/flag/flag.ts | 1 + packages/opencode/src/lsp/index.ts | 13 ++++++++ packages/opencode/src/lsp/server.ts | 49 +++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/packages/opencode/src/flag/flag.ts b/packages/opencode/src/flag/flag.ts index 0049d716d095..9abec47989e4 100644 --- a/packages/opencode/src/flag/flag.ts +++ b/packages/opencode/src/flag/flag.ts @@ -48,6 +48,7 @@ export namespace Flag { export const OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX = number("OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX") export const OPENCODE_EXPERIMENTAL_OXFMT = OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_OXFMT") export const OPENCODE_EXPERIMENTAL_LSP_TY = truthy("OPENCODE_EXPERIMENTAL_LSP_TY") + export const OPENCODE_EXPERIMENTAL_LSP_ROSLYN = truthy("OPENCODE_EXPERIMENTAL_LSP_ROSLYN") export const OPENCODE_EXPERIMENTAL_LSP_TOOL = OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_LSP_TOOL") export const OPENCODE_DISABLE_FILETIME_CHECK = truthy("OPENCODE_DISABLE_FILETIME_CHECK") export const OPENCODE_EXPERIMENTAL_PLAN_MODE = OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_PLAN_MODE") diff --git a/packages/opencode/src/lsp/index.ts b/packages/opencode/src/lsp/index.ts index 9d7d30632ab3..5eb3b9e9e97c 100644 --- a/packages/opencode/src/lsp/index.ts +++ b/packages/opencode/src/lsp/index.ts @@ -74,6 +74,19 @@ export namespace LSP { delete servers["ty"] } } + + if (Flag.OPENCODE_EXPERIMENTAL_LSP_ROSLYN) { + // roslyn-language-server is enabled; disable the default csharp-ls + if (servers["csharp"]) { + log.info("LSP server csharp-ls is disabled because OPENCODE_EXPERIMENTAL_LSP_ROSLYN is enabled") + delete servers["csharp"] + } + } else { + // roslyn-language-server is not enabled; disable it so it doesn't conflict with csharp-ls + if (servers["csharp-roslyn"]) { + delete servers["csharp-roslyn"] + } + } } const state = Instance.state( diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index a4ebeb5a2567..37b657aede8d 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -770,6 +770,55 @@ export namespace LSPServer { }, } + export const RoslynLS: Info = { + id: "csharp-roslyn", + root: NearestRoot([".slnx", ".sln", ".csproj", "global.json"]), + extensions: [".cs"], + async spawn(root) { + let bin = Bun.which("roslyn-language-server", { + PATH: process.env["PATH"] + path.delimiter + Global.Path.bin, + }) + if (!bin) { + if (!Bun.which("dotnet")) { + log.error(".NET SDK is required to install roslyn-language-server") + return + } + + if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return + log.info("installing roslyn-language-server via dotnet tool") + const proc = Bun.spawn({ + cmd: [ + "dotnet", + "tool", + "install", + "--global", + "roslyn-language-server", + "--prerelease", + "--tool-path", + Global.Path.bin, + ], + stdout: "pipe", + stderr: "pipe", + stdin: "pipe", + }) + const exit = await proc.exited + if (exit !== 0) { + log.error("Failed to install roslyn-language-server") + return + } + + bin = path.join(Global.Path.bin, "roslyn-language-server" + (process.platform === "win32" ? ".exe" : "")) + log.info(`installed roslyn-language-server`, { bin }) + } + + return { + process: spawn(bin, ["--stdio", "--autoLoadProjects"], { + cwd: root, + }), + } + }, + } + export const FSharp: Info = { id: "fsharp", root: NearestRoot([".slnx", ".sln", ".fsproj", "global.json"]), From ff818db121a8aaff3dc138839e3ea9119a4dd461 Mon Sep 17 00:00:00 2001 From: Mathews Date: Fri, 20 Feb 2026 11:01:37 -0500 Subject: [PATCH 2/4] fix: pin roslyn-language-server to version 5.5.0-2.26103.6 instead of --prerelease --- packages/opencode/src/lsp/server.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index 37b657aede8d..505362444a83 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -793,7 +793,8 @@ export namespace LSPServer { "install", "--global", "roslyn-language-server", - "--prerelease", + "--version", + "5.5.0-2.26103.6", "--tool-path", Global.Path.bin, ], From cffefc77e2ef36a7cc414811babeaecd571f7efd Mon Sep 17 00:00:00 2001 From: Mathews Date: Fri, 20 Feb 2026 11:11:08 -0500 Subject: [PATCH 3/4] fix: use --prerelease instead of pinned version for roslyn-language-server install --- packages/opencode/src/lsp/server.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index 505362444a83..37b657aede8d 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -793,8 +793,7 @@ export namespace LSPServer { "install", "--global", "roslyn-language-server", - "--version", - "5.5.0-2.26103.6", + "--prerelease", "--tool-path", Global.Path.bin, ], From 3a927e0220f0904f3331c926131d6aee9905847b Mon Sep 17 00:00:00 2001 From: Mathews Bryan <37884221+jmbryan4@users.noreply.github.com> Date: Tue, 21 Apr 2026 17:24:43 -0400 Subject: [PATCH 4/4] use global tools location --- packages/opencode/src/lsp/server.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index dd76626fecfe..a07e3368643d 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -722,8 +722,6 @@ export const CSharp: Info = { "--global", "roslyn-language-server", "--prerelease", - "--tool-path", - Global.Path.bin, ], { stdout: "pipe",