mirror of
https://github.com/anomalyco/opencode.git
synced 2026-04-28 04:29:42 +00:00
Add Roslyn support for Razor and C# scripts (#24228)
This commit is contained in:
parent
5cd178ba70
commit
1e4b7b5451
3 changed files with 135 additions and 25 deletions
|
|
@ -14,6 +14,7 @@ export const LANGUAGE_EXTENSIONS: Record<string, string> = {
|
|||
".cc": "cpp",
|
||||
".c++": "cpp",
|
||||
".cs": "csharp",
|
||||
".csx": "csharp",
|
||||
".css": "css",
|
||||
".d": "d",
|
||||
".pas": "pascal",
|
||||
|
|
|
|||
|
|
@ -703,31 +703,10 @@ export const Zls: Info = {
|
|||
export const CSharp: Info = {
|
||||
id: "csharp",
|
||||
root: NearestRoot([".slnx", ".sln", ".csproj", "global.json"]),
|
||||
extensions: [".cs"],
|
||||
extensions: [".cs", ".csx"],
|
||||
async spawn(root) {
|
||||
let bin = which("roslyn-language-server")
|
||||
if (!bin) {
|
||||
if (!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 = Process.spawn(["dotnet", "tool", "install", "--global", "roslyn-language-server", "--prerelease"], {
|
||||
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 })
|
||||
}
|
||||
const bin = await getRoslynLanguageServer()
|
||||
if (!bin) return
|
||||
|
||||
return {
|
||||
process: spawn(bin, ["--stdio", "--autoLoadProjects"], {
|
||||
|
|
@ -737,6 +716,135 @@ export const CSharp: Info = {
|
|||
},
|
||||
}
|
||||
|
||||
export const Razor: Info = {
|
||||
id: "razor",
|
||||
root: NearestRoot([".slnx", ".sln", ".csproj", "global.json"]),
|
||||
extensions: [".razor", ".cshtml"],
|
||||
async spawn(root) {
|
||||
const bin = await getRoslynLanguageServer()
|
||||
if (!bin) return
|
||||
|
||||
const razor = await findVscodeRazorExtension()
|
||||
if (!razor) {
|
||||
log.info("VS Code C# extension with Razor support not found, skipping Razor LSP")
|
||||
return
|
||||
}
|
||||
|
||||
log.info("using VS Code Razor extension for roslyn-language-server", { extension: razor.extension })
|
||||
return {
|
||||
process: spawn(
|
||||
bin,
|
||||
[
|
||||
"--stdio",
|
||||
"--autoLoadProjects",
|
||||
`--razorSourceGenerator=${razor.compiler}`,
|
||||
`--razorDesignTimePath=${razor.targets}`,
|
||||
"--extension",
|
||||
razor.extension,
|
||||
],
|
||||
{
|
||||
cwd: root,
|
||||
},
|
||||
),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
let roslynLanguageServerInstall: Promise<string | undefined> | undefined
|
||||
|
||||
async function getRoslynLanguageServer() {
|
||||
const existing = which("roslyn-language-server")
|
||||
if (existing) return existing
|
||||
|
||||
const global = await roslynLanguageServerGlobalPath()
|
||||
if (global) return global
|
||||
|
||||
roslynLanguageServerInstall ||= installRoslynLanguageServer().finally(() => {
|
||||
roslynLanguageServerInstall = undefined
|
||||
})
|
||||
return roslynLanguageServerInstall
|
||||
}
|
||||
|
||||
async function installRoslynLanguageServer() {
|
||||
if (!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 = Process.spawn(["dotnet", "tool", "install", "--global", "roslyn-language-server", "--prerelease"], {
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
stdin: "pipe",
|
||||
})
|
||||
const exit = await proc.exited
|
||||
if (exit !== 0) {
|
||||
log.error("Failed to install roslyn-language-server")
|
||||
return
|
||||
}
|
||||
|
||||
const resolved = which("roslyn-language-server")
|
||||
if (resolved) {
|
||||
log.info(`installed roslyn-language-server`, { bin: resolved })
|
||||
return resolved
|
||||
}
|
||||
|
||||
const global = await roslynLanguageServerGlobalPath()
|
||||
if (global) {
|
||||
log.info(`installed roslyn-language-server`, { bin: global })
|
||||
return global
|
||||
}
|
||||
|
||||
log.error("Installed roslyn-language-server but could not resolve executable")
|
||||
}
|
||||
|
||||
async function roslynLanguageServerGlobalPath() {
|
||||
const bin = path.join(
|
||||
process.env.DOTNET_CLI_HOME ?? os.homedir(),
|
||||
".dotnet",
|
||||
"tools",
|
||||
"roslyn-language-server" + (process.platform === "win32" ? ".cmd" : ""),
|
||||
)
|
||||
return (await pathExists(bin)) ? bin : undefined
|
||||
}
|
||||
|
||||
async function findVscodeRazorExtension() {
|
||||
const roots = [
|
||||
process.env.VSCODE_EXTENSIONS,
|
||||
path.join(os.homedir(), ".vscode", "extensions"),
|
||||
path.join(os.homedir(), ".vscode-insiders", "extensions"),
|
||||
path.join(os.homedir(), ".vscode-server", "extensions"),
|
||||
path.join(os.homedir(), ".vscode-server-insiders", "extensions"),
|
||||
].filter((item) => item !== undefined)
|
||||
|
||||
for (const root of [...new Set(roots)]) {
|
||||
const entries = await fs.readdir(root, { withFileTypes: true }).catch(() => [])
|
||||
const candidates = await Promise.all(
|
||||
entries
|
||||
.filter((entry) => entry.isDirectory() && entry.name.startsWith("ms-dotnettools.csharp-"))
|
||||
.map(async (entry) => ({
|
||||
path: path.join(root, entry.name, ".razorExtension"),
|
||||
modified: (await fs.stat(path.join(root, entry.name)).catch(() => undefined))?.mtimeMs ?? 0,
|
||||
})),
|
||||
)
|
||||
for (const entry of candidates.sort((a, b) => b.modified - a.modified).map((candidate) => candidate.path)) {
|
||||
const result = {
|
||||
compiler: path.join(entry, "Microsoft.CodeAnalysis.Razor.Compiler.dll"),
|
||||
targets: path.join(entry, "Targets", "Microsoft.NET.Sdk.Razor.DesignTime.targets"),
|
||||
extension: path.join(entry, "Microsoft.VisualStudioCode.RazorExtension.dll"),
|
||||
}
|
||||
if (
|
||||
(await pathExists(result.compiler)) &&
|
||||
(await pathExists(result.targets)) &&
|
||||
(await pathExists(result.extension))
|
||||
) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const FSharp: Info = {
|
||||
id: "fsharp",
|
||||
root: NearestRoot([".slnx", ".sln", ".fsproj", "global.json"]),
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ OpenCode comes with several built-in LSP servers for popular languages:
|
|||
| astro | .astro | Auto-installs for Astro projects |
|
||||
| bash | .sh, .bash, .zsh, .ksh | Auto-installs bash-language-server |
|
||||
| clangd | .c, .cpp, .cc, .cxx, .c++, .h, .hpp, .hh, .hxx, .h++ | Auto-installs for C/C++ projects |
|
||||
| csharp | .cs | `.NET SDK` installed |
|
||||
| csharp | .cs, .csx | `.NET SDK` installed |
|
||||
| clojure-lsp | .clj, .cljs, .cljc, .edn | `clojure-lsp` command available |
|
||||
| dart | .dart | `dart` command available |
|
||||
| deno | .ts, .tsx, .js, .jsx, .mjs | `deno` command available (auto-detects deno.json/deno.jsonc) |
|
||||
|
|
@ -36,6 +36,7 @@ OpenCode comes with several built-in LSP servers for popular languages:
|
|||
| php intelephense | .php | Auto-installs for PHP projects |
|
||||
| prisma | .prisma | `prisma` command available |
|
||||
| pyright | .py, .pyi | `pyright` dependency installed |
|
||||
| razor | .razor, .cshtml | `.NET SDK` and VS Code C# extension installed |
|
||||
| ruby-lsp (rubocop) | .rb, .rake, .gemspec, .ru | `ruby` and `gem` commands available |
|
||||
| rust | .rs | `rust-analyzer` command available |
|
||||
| sourcekit-lsp | .swift, .objc, .objcpp | `swift` installed (`xcode` on macOS) |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue