дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 11:15:40 +02:00
feat(tools): Install 15 more language servers and debug adapters
Release downloads: buf, clojure-lsp, texlab, neocmakelsp, LemMinX, Perl Navigator, kotlin-language-server, kotlin-debug-adapter and netcoredbg; Open VSX: bash-debug and php-debug (run through node). Two new methods: `gem` (ruby-lsp, solargraph), installed into the tool's folder and launched with GEM_HOME, and `dotnet tool` (csharp-ls, fsautocomplete), launched with DOTNET_ROOT so a Homebrew / user-local .NET is found. Tools declare the runtime they need (Java, Node.js, Perl). Each was installed from its real source and started on macOS arm64. Claude-Session: https://claude.ai/code/session_01B65qLEKnkeYTGdbwhLpjCu
Цей коміт міститься в:
1 джерело
fde075457b
коміт
c3ea376899
4 files changed
+429
-7
No files matched your search
@@ -177,6 +177,27 @@ pub fn locate(root: &Path, names: &[String]) -> Option<PathBuf> {
|
||||
|
||||
/// Creates `bin_dir/<command>` that runs `target`: a shell wrapper on Unix
|
||||
/// (keeps the target's own directory layout intact), a `.cmd` shim on Windows.
|
||||
/// Like [`link_executable`], with `env` set for `target`.
|
||||
pub fn env_wrapper(bin_dir: &Path, command: &str, target: &Path, env: &[(&str, &Path)]) -> IdeResult<PathBuf> {
|
||||
fs::create_dir_all(bin_dir)?;
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let quote = |p: &Path| format!("'{}'", p.to_string_lossy().replace('\'', r"'\''"));
|
||||
let vars: String = env.iter().map(|(k, v)| format!("{k}={} ", quote(v))).collect();
|
||||
let link = bin_dir.join(command);
|
||||
fs::write(&link, format!("#!/bin/sh\n{vars}exec {} \"$@\"\n", quote(target)))?;
|
||||
make_executable(&link)?;
|
||||
Ok(link)
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
let link = bin_dir.join(format!("{command}.cmd"));
|
||||
let vars: String = env.iter().map(|(k, v)| format!("set \"{k}={}\"\r\n", v.display())).collect();
|
||||
fs::write(&link, format!("@echo off\r\nsetlocal\r\n{vars}\"{}\" %*\r\n", target.display()))?;
|
||||
Ok(link)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn link_executable(bin_dir: &Path, command: &str, target: &Path) -> IdeResult<PathBuf> {
|
||||
fs::create_dir_all(bin_dir)?;
|
||||
#[cfg(unix)]
|
||||
|
||||
@@ -120,7 +120,9 @@ fn bin_dirs_in(root: &Path, platform: Platform) -> Vec<PathBuf> {
|
||||
fn managed_bin_dir(root: &Path, spec: &ToolSpec, platform: Platform) -> Option<PathBuf> {
|
||||
match spec.method {
|
||||
Method::Npm { .. } => Some(root.join("node_modules").join(".bin")),
|
||||
Method::Go { .. } | Method::Release { .. } => Some(root.join(spec.name).join("bin")),
|
||||
Method::Go { .. } | Method::Gem { .. } | Method::DotnetTool { .. } | Method::Release { .. } => {
|
||||
Some(root.join(spec.name).join("bin"))
|
||||
}
|
||||
Method::PipVenv { .. } => {
|
||||
Some(root.join(spec.name).join("venv").join(if platform.os == Os::Windows { "Scripts" } else { "bin" }))
|
||||
}
|
||||
@@ -376,6 +378,10 @@ impl Tools {
|
||||
"npm or pnpm is required: install Node.js (https://nodejs.org)",
|
||||
),
|
||||
Method::Go { .. } => need(self.which("go").is_some(), "Go is required (https://go.dev/dl)"),
|
||||
Method::Gem { .. } => need(self.which("gem").is_some(), "Ruby is required (https://www.ruby-lang.org)"),
|
||||
Method::DotnetTool { .. } => {
|
||||
need(self.which("dotnet").is_some(), ".NET SDK is required (https://dotnet.microsoft.com/download)")
|
||||
}
|
||||
Method::PipVenv { .. } => need(
|
||||
self.which("uv").is_some() || self.python().is_some(),
|
||||
"Python 3 or uv is required (https://www.python.org)",
|
||||
@@ -593,6 +599,8 @@ impl Tools {
|
||||
}
|
||||
Method::Npm { packages } => self.install_npm(packages, cancel, emit)?,
|
||||
Method::Go { module } => self.install_go(spec, module, cancel, emit)?,
|
||||
Method::Gem { gem } => self.install_gem(spec, gem, cancel, emit)?,
|
||||
Method::DotnetTool { package } => self.install_dotnet_tool(spec, package, cancel, emit)?,
|
||||
Method::PipVenv { package } => self.install_venv(spec, package, cancel, emit)?,
|
||||
Method::PipUser { package, .. } => self.install_pip_user(package, cancel, emit)?,
|
||||
Method::PipTarget { package, .. } => self.install_pip_target(spec, package, cancel, emit)?,
|
||||
@@ -672,6 +680,96 @@ impl Tools {
|
||||
self.write_manifest(spec.name, &Manifest { version, source: Some(module.to_string()) })
|
||||
}
|
||||
|
||||
fn install_gem(&self, spec: &ToolSpec, gem: &str, cancel: &CancellationToken, emit: Emit) -> IdeResult<()> {
|
||||
let dir = self.root.join(spec.name);
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
let home = dir.join("gems");
|
||||
std::fs::create_dir_all(&home)?;
|
||||
let mut c = self.command(self.which("gem").expect("checked"));
|
||||
c.args(["install", "--no-document", "--install-dir"]).arg(&home).arg("--bindir").arg(home.join("bin")).arg(gem);
|
||||
let result = run_streamed(c, cancel, emit).and_then(|_| {
|
||||
// RubyGems' stubs only find their gems through GEM_HOME / GEM_PATH.
|
||||
let env = [("GEM_HOME", home.as_path()), ("GEM_PATH", home.as_path())];
|
||||
for cmd in spec.commands {
|
||||
let stub = self
|
||||
.exe_in(&home.join("bin"), cmd)
|
||||
.ok_or_else(|| IdeError::new(ErrorKind::Io, format!("gem {gem} did not install `{cmd}`")))?;
|
||||
archive::env_wrapper(&dir.join("bin"), cmd, &stub, &env)?;
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
if let Err(e) = result {
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
return Err(e);
|
||||
}
|
||||
// `gems/gems/<gem>-<version>/`
|
||||
let version = std::fs::read_dir(home.join("gems")).ok().and_then(|entries| {
|
||||
entries.flatten().find_map(|e| {
|
||||
let name = e.file_name().to_string_lossy().into_owned();
|
||||
name.strip_prefix(&format!("{gem}-"))
|
||||
.filter(|v| v.starts_with(|c: char| c.is_ascii_digit()))
|
||||
.map(String::from)
|
||||
})
|
||||
});
|
||||
self.write_manifest(spec.name, &Manifest { version, source: Some(format!("rubygems:{gem}")) })
|
||||
}
|
||||
|
||||
fn install_dotnet_tool(
|
||||
&self,
|
||||
spec: &ToolSpec,
|
||||
package: &str,
|
||||
cancel: &CancellationToken,
|
||||
emit: Emit,
|
||||
) -> IdeResult<()> {
|
||||
let dir = self.root.join(spec.name);
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
let tool = dir.join("tool");
|
||||
std::fs::create_dir_all(&tool)?;
|
||||
let dotnet = self.which("dotnet").expect("checked");
|
||||
let mut c = self.command(&dotnet);
|
||||
c.args(["tool", "install", package, "--tool-path"]).arg(&tool);
|
||||
let result = run_streamed(c, cancel, emit).and_then(|_| {
|
||||
// The tool's apphost looks for .NET in the default location only; a
|
||||
// Homebrew / user-local SDK must be named through DOTNET_ROOT.
|
||||
let root = self.dotnet_root(&dotnet);
|
||||
for cmd in spec.commands {
|
||||
let exe = self
|
||||
.exe_in(&tool, cmd)
|
||||
.ok_or_else(|| IdeError::new(ErrorKind::Io, format!("{package} did not install `{cmd}`")))?;
|
||||
match &root {
|
||||
Some(root) => archive::env_wrapper(&dir.join("bin"), cmd, &exe, &[("DOTNET_ROOT", root)])?,
|
||||
None => archive::link_executable(&dir.join("bin"), cmd, &exe)?,
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
if let Err(e) = result {
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
return Err(e);
|
||||
}
|
||||
// `Package Id Version Commands` table.
|
||||
let mut c = self.command(&dotnet);
|
||||
c.args(["tool", "list", "--tool-path"]).arg(&tool);
|
||||
let version = run_quiet(c, Duration::from_secs(20)).and_then(|(_, out)| {
|
||||
out.lines().find_map(|l| {
|
||||
let mut cols = l.split_whitespace();
|
||||
cols.next().filter(|id| id.eq_ignore_ascii_case(package))?;
|
||||
cols.next().map(String::from)
|
||||
})
|
||||
});
|
||||
self.write_manifest(spec.name, &Manifest { version, source: Some(format!("nuget:{package}")) })
|
||||
}
|
||||
|
||||
/// The .NET install `dotnet` runs from: `Microsoft.NETCore.App 9.0.1 [<root>/shared/Microsoft.NETCore.App]`.
|
||||
fn dotnet_root(&self, dotnet: &Path) -> Option<PathBuf> {
|
||||
let mut c = self.command(dotnet);
|
||||
c.arg("--list-runtimes");
|
||||
let (_, out) = run_quiet(c, Duration::from_secs(20))?;
|
||||
let line = out.lines().find(|l| l.starts_with("Microsoft.NETCore.App "))?;
|
||||
let shared = line.rsplit_once('[')?.1.trim_end().strip_suffix(']')?;
|
||||
Path::new(shared).parent()?.parent().map(Path::to_path_buf)
|
||||
}
|
||||
|
||||
fn install_venv(&self, spec: &ToolSpec, package: &str, cancel: &CancellationToken, emit: Emit) -> IdeResult<()> {
|
||||
let dir = self.root.join(spec.name);
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
@@ -951,7 +1049,12 @@ impl Tools {
|
||||
run_streamed(c, &cancel, &quiet)?;
|
||||
}
|
||||
}
|
||||
Method::Go { .. } | Method::PipVenv { .. } | Method::PipTarget { .. } | Method::Release { .. } => {
|
||||
Method::Go { .. }
|
||||
| Method::Gem { .. }
|
||||
| Method::DotnetTool { .. }
|
||||
| Method::PipVenv { .. }
|
||||
| Method::PipTarget { .. }
|
||||
| Method::Release { .. } => {
|
||||
let dir = self.root.join(spec.name);
|
||||
if dir.exists() {
|
||||
std::fs::remove_dir_all(&dir)?;
|
||||
|
||||
@@ -44,6 +44,11 @@ pub enum Method {
|
||||
/// A pure-Python package unpacked into `<tools>/<name>/lib` (`pip install --target`,
|
||||
/// universal wheel), put on `PYTHONPATH` of whichever interpreter runs it.
|
||||
PipTarget { package: &'static str, module: &'static str },
|
||||
/// `gem install` into `<tools>/<name>/gems`, launched through `GEM_HOME` wrappers in `bin/`.
|
||||
Gem { gem: &'static str },
|
||||
/// `dotnet tool install --tool-path <tools>/<name>/tool <package>`, launched
|
||||
/// through `DOTNET_ROOT` wrappers in `bin/`.
|
||||
DotnetTool { package: &'static str },
|
||||
/// Platform archive download into `<tools>/<name>/`.
|
||||
Release { source: ReleaseSource, assets: &'static [AssetRule], locate: &'static [&'static str] },
|
||||
}
|
||||
@@ -54,6 +59,8 @@ impl Method {
|
||||
Method::Rustup { .. } => "rustup",
|
||||
Method::Npm { .. } => "npm",
|
||||
Method::Go { .. } => "go install",
|
||||
Method::Gem { .. } => "gem",
|
||||
Method::DotnetTool { .. } => "dotnet tool",
|
||||
Method::PipVenv { .. } => "pip (virtualenv)",
|
||||
Method::PipUser { .. } => "pip --user",
|
||||
Method::PipTarget { .. } => "pip (private copy)",
|
||||
@@ -96,6 +103,8 @@ use Os::{Linux, Mac, Windows};
|
||||
use ToolKind::{Debugger, LanguageServer, Plugin};
|
||||
|
||||
const V: Option<(&str, &[&str])> = None;
|
||||
const JAVA: &[(&str, &str)] = &[("java", "Java is required (https://adoptium.net)")];
|
||||
const NODE: &[(&str, &str)] = &[("node", "Node.js is required (https://nodejs.org)")];
|
||||
const ELIXIR: &[(&str, &str)] = &[("elixir", "Elixir is required (https://elixir-lang.org/install.html)")];
|
||||
const ELIXIR_LS_ASSETS: &[AssetRule] =
|
||||
&[any_arch(Mac, "elixir-ls-v*.zip"), any_arch(Linux, "elixir-ls-v*.zip"), any_arch(Windows, "elixir-ls-v*.zip")];
|
||||
@@ -565,6 +574,249 @@ pub static TOOLS: &[ToolSpec] = &[
|
||||
homepage: "https://github.com/elixir-lsp/elixir-ls",
|
||||
needs: ELIXIR,
|
||||
},
|
||||
ToolSpec {
|
||||
name: "buf",
|
||||
display: "Buf (Protocol Buffers)",
|
||||
kind: LanguageServer,
|
||||
commands: &["buf"],
|
||||
method: Method::Release {
|
||||
source: ReleaseSource::GitHub { repo: "bufbuild/buf" },
|
||||
assets: &[
|
||||
rule(Mac, Arm64, "buf-Darwin-arm64.tar.gz"),
|
||||
rule(Mac, X64, "buf-Darwin-x86_64.tar.gz"),
|
||||
rule(Linux, X64, "buf-Linux-x86_64.tar.gz"),
|
||||
rule(Linux, Arm64, "buf-Linux-aarch64.tar.gz"),
|
||||
rule(Windows, X64, "buf-Windows-x86_64.zip"),
|
||||
rule(Windows, Arm64, "buf-Windows-arm64.zip"),
|
||||
],
|
||||
locate: &["buf"],
|
||||
},
|
||||
version: ver("buf"),
|
||||
homepage: "https://buf.build",
|
||||
needs: &[],
|
||||
},
|
||||
ToolSpec {
|
||||
name: "clojure-lsp",
|
||||
display: "clojure-lsp",
|
||||
kind: LanguageServer,
|
||||
commands: &["clojure-lsp"],
|
||||
method: Method::Release {
|
||||
source: ReleaseSource::GitHub { repo: "clojure-lsp/clojure-lsp" },
|
||||
assets: &[
|
||||
rule(Mac, Arm64, "clojure-lsp-native-macos-aarch64.zip"),
|
||||
rule(Mac, X64, "clojure-lsp-native-macos-amd64.zip"),
|
||||
rule(Linux, X64, "clojure-lsp-native-linux-amd64.zip"),
|
||||
rule(Linux, Arm64, "clojure-lsp-native-linux-aarch64.zip"),
|
||||
rule(Windows, X64, "clojure-lsp-native-windows-amd64.zip"),
|
||||
],
|
||||
locate: &["clojure-lsp"],
|
||||
},
|
||||
version: V,
|
||||
homepage: "https://clojure-lsp.io",
|
||||
needs: &[],
|
||||
},
|
||||
ToolSpec {
|
||||
name: "texlab",
|
||||
display: "TexLab (LaTeX)",
|
||||
kind: LanguageServer,
|
||||
commands: &["texlab"],
|
||||
method: Method::Release {
|
||||
source: ReleaseSource::GitHub { repo: "latex-lsp/texlab" },
|
||||
assets: &[
|
||||
rule(Mac, Arm64, "texlab-aarch64-macos.tar.gz"),
|
||||
rule(Mac, X64, "texlab-x86_64-macos.tar.gz"),
|
||||
rule(Linux, X64, "texlab-x86_64-linux.tar.gz"),
|
||||
rule(Linux, Arm64, "texlab-aarch64-linux.tar.gz"),
|
||||
rule(Windows, X64, "texlab-x86_64-windows.zip"),
|
||||
rule(Windows, Arm64, "texlab-aarch64-windows.zip"),
|
||||
],
|
||||
locate: &["texlab"],
|
||||
},
|
||||
version: ver("texlab"),
|
||||
homepage: "https://github.com/latex-lsp/texlab",
|
||||
needs: &[],
|
||||
},
|
||||
ToolSpec {
|
||||
name: "neocmakelsp",
|
||||
display: "neocmakelsp (CMake)",
|
||||
kind: LanguageServer,
|
||||
commands: &["neocmakelsp"],
|
||||
method: Method::Release {
|
||||
source: ReleaseSource::GitHub { repo: "Decodetalkers/neocmakelsp" },
|
||||
assets: &[
|
||||
any_arch(Mac, "neocmakelsp-universal-apple-darwin.tar.gz"),
|
||||
rule(Linux, X64, "neocmakelsp-x86_64-unknown-linux-gnu.tar.gz"),
|
||||
rule(Linux, Arm64, "neocmakelsp-aarch64-unknown-linux-gnu.tar.gz"),
|
||||
rule(Windows, X64, "neocmakelsp-x86_64-pc-windows-msvc.zip"),
|
||||
rule(Windows, Arm64, "neocmakelsp-aarch64-pc-windows-msvc.zip"),
|
||||
],
|
||||
locate: &["neocmakelsp"],
|
||||
},
|
||||
version: ver("neocmakelsp"),
|
||||
homepage: "https://github.com/Decodetalkers/neocmakelsp",
|
||||
needs: &[],
|
||||
},
|
||||
ToolSpec {
|
||||
name: "lemminx",
|
||||
display: "LemMinX (XML)",
|
||||
kind: LanguageServer,
|
||||
commands: &["lemminx"],
|
||||
method: Method::Release {
|
||||
source: ReleaseSource::GitHub { repo: "redhat-developer/vscode-xml" },
|
||||
assets: &[
|
||||
rule(Mac, Arm64, "lemminx-osx-aarch_64.zip"),
|
||||
rule(Mac, X64, "lemminx-osx-x86_64.zip"),
|
||||
rule(Linux, X64, "lemminx-linux-x86_64.zip"),
|
||||
rule(Linux, Arm64, "lemminx-linux-aarch_64.zip"),
|
||||
rule(Windows, X64, "lemminx-win32.zip"),
|
||||
],
|
||||
locate: &["lemminx-*"],
|
||||
},
|
||||
version: V,
|
||||
homepage: "https://github.com/eclipse/lemminx",
|
||||
needs: &[],
|
||||
},
|
||||
ToolSpec {
|
||||
name: "perlnavigator",
|
||||
display: "Perl Navigator",
|
||||
kind: LanguageServer,
|
||||
commands: &["perlnavigator"],
|
||||
method: Method::Release {
|
||||
source: ReleaseSource::GitHub { repo: "bscan/PerlNavigator" },
|
||||
assets: &[
|
||||
any_arch(Mac, "perlnavigator-macos-x86_64.zip"),
|
||||
rule(Linux, X64, "perlnavigator-linux-x86_64.zip"),
|
||||
rule(Windows, X64, "perlnavigator-win-x86_64.zip"),
|
||||
],
|
||||
locate: &["perlnavigator"],
|
||||
},
|
||||
version: V,
|
||||
homepage: "https://github.com/bscan/PerlNavigator",
|
||||
needs: &[("perl", "Perl is required (https://www.perl.org/get.html)")],
|
||||
},
|
||||
ToolSpec {
|
||||
name: "kotlin-language-server",
|
||||
display: "Kotlin Language Server (fwcd)",
|
||||
kind: LanguageServer,
|
||||
commands: &["kotlin-language-server"],
|
||||
method: Method::Release {
|
||||
source: ReleaseSource::GitHub { repo: "fwcd/kotlin-language-server" },
|
||||
assets: &[any_arch(Mac, "server.zip"), any_arch(Linux, "server.zip"), any_arch(Windows, "server.zip")],
|
||||
locate: &["kotlin-language-server"],
|
||||
},
|
||||
version: V,
|
||||
homepage: "https://github.com/fwcd/kotlin-language-server",
|
||||
needs: JAVA,
|
||||
},
|
||||
ToolSpec {
|
||||
name: "kotlin-debug-adapter",
|
||||
display: "Kotlin Debug Adapter",
|
||||
kind: Debugger,
|
||||
commands: &["kotlin-debug-adapter"],
|
||||
method: Method::Release {
|
||||
source: ReleaseSource::GitHub { repo: "fwcd/kotlin-debug-adapter" },
|
||||
assets: &[any_arch(Mac, "adapter.zip"), any_arch(Linux, "adapter.zip"), any_arch(Windows, "adapter.zip")],
|
||||
locate: &["kotlin-debug-adapter"],
|
||||
},
|
||||
version: V,
|
||||
homepage: "https://github.com/fwcd/kotlin-debug-adapter",
|
||||
needs: JAVA,
|
||||
},
|
||||
ToolSpec {
|
||||
name: "netcoredbg",
|
||||
display: "netcoredbg (.NET debugger)",
|
||||
kind: Debugger,
|
||||
commands: &["netcoredbg"],
|
||||
method: Method::Release {
|
||||
source: ReleaseSource::GitHub { repo: "Samsung/netcoredbg" },
|
||||
assets: &[
|
||||
rule(Mac, Arm64, "netcoredbg-osx-arm64.zip"),
|
||||
rule(Linux, X64, "netcoredbg-linux-amd64.tar.gz"),
|
||||
rule(Linux, Arm64, "netcoredbg-linux-arm64.tar.gz"),
|
||||
rule(Windows, X64, "netcoredbg-win64.zip"),
|
||||
],
|
||||
locate: &["netcoredbg"],
|
||||
},
|
||||
version: V,
|
||||
homepage: "https://github.com/Samsung/netcoredbg",
|
||||
needs: &[],
|
||||
},
|
||||
ToolSpec {
|
||||
name: "bash-debug",
|
||||
display: "Bash Debug",
|
||||
kind: Debugger,
|
||||
commands: &["bash-debug-adapter"],
|
||||
method: Method::Release {
|
||||
source: ReleaseSource::OpenVsx { namespace: "rogalmic", extension: "bash-debug" },
|
||||
assets: &[
|
||||
any_arch(Mac, "rogalmic.bash-debug-*.vsix"),
|
||||
any_arch(Linux, "rogalmic.bash-debug-*.vsix"),
|
||||
any_arch(Windows, "rogalmic.bash-debug-*.vsix"),
|
||||
],
|
||||
locate: &["bashDebug.js"],
|
||||
},
|
||||
version: V,
|
||||
homepage: "https://github.com/rogalmic/vscode-bash-debug",
|
||||
needs: NODE,
|
||||
},
|
||||
ToolSpec {
|
||||
name: "php-debug",
|
||||
display: "PHP Debug (Xdebug)",
|
||||
kind: Debugger,
|
||||
commands: &["php-debug-adapter"],
|
||||
method: Method::Release {
|
||||
source: ReleaseSource::OpenVsx { namespace: "xdebug", extension: "php-debug" },
|
||||
assets: &[
|
||||
any_arch(Mac, "xdebug.php-debug-*.vsix"),
|
||||
any_arch(Linux, "xdebug.php-debug-*.vsix"),
|
||||
any_arch(Windows, "xdebug.php-debug-*.vsix"),
|
||||
],
|
||||
locate: &["phpDebug.js"],
|
||||
},
|
||||
version: V,
|
||||
homepage: "https://github.com/xdebug/vscode-php-debug",
|
||||
needs: NODE,
|
||||
},
|
||||
ToolSpec {
|
||||
name: "ruby-lsp",
|
||||
display: "Ruby LSP (Shopify)",
|
||||
kind: LanguageServer,
|
||||
commands: &["ruby-lsp"],
|
||||
method: Method::Gem { gem: "ruby-lsp" },
|
||||
version: V,
|
||||
homepage: "https://shopify.github.io/ruby-lsp",
|
||||
needs: &[],
|
||||
},
|
||||
ToolSpec {
|
||||
name: "solargraph",
|
||||
display: "Solargraph (Ruby)",
|
||||
kind: LanguageServer,
|
||||
commands: &["solargraph"],
|
||||
method: Method::Gem { gem: "solargraph" },
|
||||
version: ver("solargraph"),
|
||||
homepage: "https://solargraph.org",
|
||||
needs: &[],
|
||||
},
|
||||
ToolSpec {
|
||||
name: "csharp-ls",
|
||||
display: "csharp-ls",
|
||||
kind: LanguageServer,
|
||||
commands: &["csharp-ls"],
|
||||
method: Method::DotnetTool { package: "csharp-ls" },
|
||||
version: V,
|
||||
homepage: "https://github.com/razzmatazz/csharp-language-server",
|
||||
needs: &[],
|
||||
},
|
||||
ToolSpec {
|
||||
name: "fsautocomplete",
|
||||
display: "FsAutoComplete (F#)",
|
||||
kind: LanguageServer,
|
||||
commands: &["fsautocomplete"],
|
||||
method: Method::DotnetTool { package: "fsautocomplete" },
|
||||
version: V,
|
||||
homepage: "https://github.com/ionide/FsAutoComplete",
|
||||
needs: &[],
|
||||
},
|
||||
];
|
||||
|
||||
/// Plugin tools and the tool that loads them.
|
||||
|
||||
@@ -68,10 +68,12 @@ fn everything_missing_on_an_empty_system() {
|
||||
assert!(vtsls.languages.iter().any(|l| l == "TypeScript"));
|
||||
assert!(vtsls.unavailable_reason.as_deref().unwrap().contains("npm"));
|
||||
// Servers without an installer are listed with their manual hint.
|
||||
let ruby = find(&list, "solargraph");
|
||||
assert_eq!(ruby.install_method, None);
|
||||
assert!(!ruby.can_install);
|
||||
assert_eq!(ruby.status, ToolStatus::Missing);
|
||||
let hls = find(&list, "haskell-language-server");
|
||||
assert_eq!(hls.install_method, None);
|
||||
assert!(!hls.can_install);
|
||||
assert_eq!(hls.status, ToolStatus::Missing);
|
||||
// Gem tools need Ruby.
|
||||
assert!(find(&list, "solargraph").unavailable_reason.as_deref().unwrap().contains("Ruby"));
|
||||
// Shared npm package: each server is its own row.
|
||||
assert_eq!(find(&list, "vscode-css-language-server").install_method.as_deref(), Some("npm"));
|
||||
assert!(find(&list, "delve").kind == ToolKind::Debugger);
|
||||
@@ -155,7 +157,7 @@ fn bin_dirs_cover_every_managed_layout() {
|
||||
fn unknown_or_manual_tools_cannot_be_installed() {
|
||||
let env = Env::new();
|
||||
let tools = env.tools();
|
||||
let err = tools.install("solargraph", &langs(), &CancellationToken::new(), &|_| {}).unwrap_err();
|
||||
let err = tools.install("haskell-language-server", &langs(), &CancellationToken::new(), &|_| {}).unwrap_err();
|
||||
assert_eq!(err.kind, ErrorKind::Unavailable);
|
||||
assert!(err.message.contains("cannot be installed automatically"));
|
||||
assert_eq!(tools.info("nope", &langs()).unwrap_err().kind, ErrorKind::NotFound);
|
||||
@@ -336,3 +338,47 @@ fn elixir_ls_needs_elixir_and_lists_its_debugger() {
|
||||
assert!(find(&list, "elixir-ls").can_install);
|
||||
assert!(find(&list, "elixir-ls-debugger").can_install);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn gem_install_wraps_stubs_with_gem_home() {
|
||||
let env = Env::new();
|
||||
// `gem install --no-document --install-dir <home> --bindir <bin> solargraph`
|
||||
env.script(
|
||||
&env.sys,
|
||||
"gem",
|
||||
r#"home="$4"; bin="$6"; /bin/mkdir -p "$home/gems/solargraph-0.50.0" "$bin"
|
||||
printf '#!/bin/sh\necho "$GEM_HOME"\n' > "$bin/solargraph"; /bin/chmod +x "$bin/solargraph""#,
|
||||
);
|
||||
let tools = env.tools();
|
||||
let info = tools.install("solargraph", &langs(), &CancellationToken::new(), &|_| {}).unwrap();
|
||||
assert_eq!(info.status, ToolStatus::Installed);
|
||||
assert_eq!(info.version.as_deref(), Some("0.50.0"));
|
||||
let wrapper = env.root.join("solargraph/bin/solargraph");
|
||||
assert_eq!(info.path.as_deref(), Some(wrapper.as_path()));
|
||||
let out = std::process::Command::new(&wrapper).output().unwrap();
|
||||
assert_eq!(String::from_utf8_lossy(&out.stdout).trim(), env.root.join("solargraph/gems").to_string_lossy());
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn dotnet_tool_install_into_tool_path() {
|
||||
let env = Env::new();
|
||||
env.script(
|
||||
&env.sys,
|
||||
"dotnet",
|
||||
r#"case "$2" in
|
||||
install) /bin/mkdir -p "$5"; printf '#!/bin/sh\necho "$DOTNET_ROOT"\n' > "$5/csharp-ls"; /bin/chmod +x "$5/csharp-ls" ;;
|
||||
list) printf 'Package Id Version Commands\n----\ncsharp-ls 0.15.0 csharp-ls\n' ;;
|
||||
esac
|
||||
[ "$1" = --list-runtimes ] && echo 'Microsoft.NETCore.App 9.0.1 [/opt/dotnet/shared/Microsoft.NETCore.App]'
|
||||
exit 0"#,
|
||||
);
|
||||
let info = env.tools().install("csharp-ls", &langs(), &CancellationToken::new(), &|_| {}).unwrap();
|
||||
assert_eq!(info.status, ToolStatus::Installed);
|
||||
assert_eq!(info.version.as_deref(), Some("0.15.0"));
|
||||
let wrapper = env.root.join("csharp-ls/bin/csharp-ls");
|
||||
assert_eq!(info.path.as_deref(), Some(wrapper.as_path()));
|
||||
let out = std::process::Command::new(&wrapper).output().unwrap();
|
||||
assert_eq!(String::from_utf8_lossy(&out.stdout).trim(), "/opt/dotnet");
|
||||
}
|
||||
Reference in new issue
Заблокувати користувача