дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 19:25:54 +02:00
fix(tools): install with the selected project SDK (#2334)
Цей коміт міститься в:
1 джерело
24d3173a55
коміт
e2f22de824
9 files changed
+279
-10
No files matched your search
@@ -24,8 +24,10 @@ fn tools(host: &Host, window: &str) -> IdeResult<Tools> {
|
||||
.collect();
|
||||
// Godot is usually an app bundle / Flatpak outside the search path.
|
||||
let found = ide_sdk::godot::find(None).map(|p| (ide_sdk::godot::COMMAND.to_owned(), p));
|
||||
let runtime = ide_sdk::apply::tool_install_environment(&root, &settings);
|
||||
let work_dir = (!root.as_os_str().is_empty()).then_some(root);
|
||||
Ok(Tools::new(&data)
|
||||
.with_runtime(runtime.commands, runtime.env)
|
||||
.with_custom_paths(custom)
|
||||
.with_found_paths(found.into_iter().collect())
|
||||
.with_work_dir(work_dir))
|
||||
|
||||
@@ -130,6 +130,34 @@ pub(crate) fn goroot(root: &Path, s: &Settings) -> Option<PathBuf> {
|
||||
.or_else(|| pinned::pinned_install(root, Tool::Go).and_then(|(dir, _)| pinned::goroot_in_install(&dir)))
|
||||
}
|
||||
|
||||
/// Runtime choices for package-manager based IDE tool installs.
|
||||
#[derive(Default)]
|
||||
pub struct ToolInstallEnvironment {
|
||||
pub commands: std::collections::BTreeMap<String, PathBuf>,
|
||||
pub env: std::collections::BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
/// Uses the same configured / project-pinned SDKs as Run and language servers.
|
||||
/// Build tags and target OS settings intentionally do not apply to host tools.
|
||||
pub fn tool_install_environment(root: &Path, s: &Settings) -> ToolInstallEnvironment {
|
||||
let mut runtime = ToolInstallEnvironment::default();
|
||||
if let Some(go) = goroot(root, s) {
|
||||
runtime.commands.insert("go".into(), go.join("bin").join(exe("go")));
|
||||
runtime.env.insert("GOROOT".into(), go.to_string_lossy().into_owned());
|
||||
}
|
||||
if let Some(node) = node_dir(root, s) {
|
||||
let node = setting_path(root, &s.languages.node.interpreter).unwrap_or_else(|| node.join(exe("node")));
|
||||
runtime.commands.insert("node".into(), node);
|
||||
}
|
||||
if let Some(ruby) = ruby_interpreter(root, s) {
|
||||
runtime.commands.insert("ruby".into(), ruby);
|
||||
}
|
||||
if let Some(python) = python_interpreter(root, s) {
|
||||
runtime.commands.insert("python".into(), python);
|
||||
}
|
||||
runtime
|
||||
}
|
||||
|
||||
/// A JDK setting: a JDK home, its `bin` or its `bin/java`.
|
||||
fn jdk_setting(root: &Path, value: &str) -> Option<Result<crate::java::Jdk, PathBuf>> {
|
||||
let p = setting_path(root, value)?;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::apply::{
|
||||
apply_to_run_configs, configure_server, jdtls_jdk, maven_command, maven_options, project_jdk, python_interpreter,
|
||||
ruff_configuration, terminal_activation, tsdk, without_derived,
|
||||
ruff_configuration, terminal_activation, tool_install_environment, tsdk, without_derived,
|
||||
};
|
||||
use crate::python::parse_pip_list;
|
||||
use crate::{bin_dir, exe, venv_of};
|
||||
@@ -2246,3 +2246,25 @@ fn plugin_interpreter_targets_route_all_php_node_command_families() {
|
||||
assert_eq!(config.extras.target.as_deref(), Some(""));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tool_installs_resolve_project_relative_sdks_and_leave_path_fallback_unconfigured() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let root = tmp.path();
|
||||
let mut s = Settings::default();
|
||||
let defaults = tool_install_environment(root, &s);
|
||||
assert!(!defaults.commands.contains_key("go"));
|
||||
assert!(!defaults.commands.contains_key("node"));
|
||||
assert!(!defaults.commands.contains_key("python"));
|
||||
assert!(!defaults.env.contains_key("GOROOT"));
|
||||
s.languages.go.goroot = "go-sdk".into();
|
||||
s.languages.node.interpreter = "node-sdk/bin/node".into();
|
||||
s.languages.python.interpreter = "python-sdk/bin/python3.14".into();
|
||||
s.languages.ruby.interpreter = "ruby-sdk/bin/ruby".into();
|
||||
let runtime = tool_install_environment(root, &s);
|
||||
assert_eq!(runtime.commands["go"], root.join("go-sdk/bin").join(if cfg!(windows) { "go.exe" } else { "go" }));
|
||||
assert_eq!(runtime.env["GOROOT"], root.join("go-sdk").to_string_lossy());
|
||||
assert_eq!(runtime.commands["node"], root.join("node-sdk/bin/node"));
|
||||
assert_eq!(runtime.commands["python"], root.join("python-sdk/bin/python3.14"));
|
||||
assert_eq!(runtime.commands["ruby"], root.join("ruby-sdk/bin/ruby"));
|
||||
}
|
||||
@@ -421,6 +421,7 @@ pub fn managed_bundle(app_data: &Path, name: &str) -> Option<PathBuf> {
|
||||
}
|
||||
|
||||
/// Installer and detector over one tools directory.
|
||||
#[derive(Clone)]
|
||||
pub struct Tools {
|
||||
root: PathBuf,
|
||||
/// Search path for system tools (excludes our own dirs).
|
||||
@@ -432,6 +433,8 @@ pub struct Tools {
|
||||
custom: BTreeMap<String, PathBuf>,
|
||||
/// Executables found outside the search path (app bundles), by command.
|
||||
found: BTreeMap<String, PathBuf>,
|
||||
runtime_commands: BTreeMap<String, PathBuf>,
|
||||
runtime_env: BTreeMap<String, String>,
|
||||
version_timeout: Duration,
|
||||
/// The project the tools are used for: tools that install into "the active runtime" (gems) run here,
|
||||
/// so a version manager picks the project's Ruby.
|
||||
@@ -456,11 +459,41 @@ impl Tools {
|
||||
platform,
|
||||
custom: BTreeMap::new(),
|
||||
found: BTreeMap::new(),
|
||||
runtime_commands: BTreeMap::new(),
|
||||
runtime_env: BTreeMap::new(),
|
||||
version_timeout: Duration::from_secs(4),
|
||||
work_dir: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Applies the project's SDKs to prerequisite lookup, probes and installer children.
|
||||
pub fn with_runtime(mut self, commands: BTreeMap<String, PathBuf>, env: BTreeMap<String, String>) -> Self {
|
||||
self.runtime_commands = commands;
|
||||
self.runtime_env = env;
|
||||
self
|
||||
}
|
||||
|
||||
fn for_method(&self, method: Method) -> Self {
|
||||
let commands: &[&str] = match method {
|
||||
Method::Go { .. } => &["go"],
|
||||
Method::Npm { .. } => &["node"],
|
||||
Method::Gem { .. } | Method::GemActive { .. } => &["ruby"],
|
||||
Method::PipVenv { .. } | Method::PipUser { .. } | Method::PipTarget { .. } => &["python"],
|
||||
_ => &[],
|
||||
};
|
||||
let mut runtime = self.clone();
|
||||
runtime.runtime_commands.retain(|name, _| commands.contains(&name.as_str()));
|
||||
runtime.runtime_env.retain(|name, _| name == "GOROOT" && commands.contains(&"go"));
|
||||
let dirs: Vec<PathBuf> =
|
||||
runtime.runtime_commands.values().filter_map(|p| p.parent().map(Path::to_path_buf)).collect();
|
||||
runtime.system_path =
|
||||
std::env::join_paths(dirs.iter().cloned().chain(std::env::split_paths(&self.system_path)))
|
||||
.unwrap_or_else(|_| self.system_path.clone());
|
||||
runtime.child_path = std::env::join_paths(dirs.iter().cloned().chain(std::env::split_paths(&self.child_path)))
|
||||
.unwrap_or_else(|_| self.child_path.clone());
|
||||
runtime
|
||||
}
|
||||
|
||||
/// Reports the executables chosen in settings (command → path) as the tools' location.
|
||||
pub fn with_custom_paths(mut self, custom: BTreeMap<String, PathBuf>) -> Self {
|
||||
self.custom = custom;
|
||||
@@ -493,6 +526,9 @@ impl Tools {
|
||||
}
|
||||
|
||||
fn which(&self, cmd: &str) -> Option<PathBuf> {
|
||||
if let Some(path) = self.runtime_commands.get(cmd) {
|
||||
return path.is_file().then(|| path.clone());
|
||||
}
|
||||
ide_core::tool_path::find_command(cmd, &self.system_path, &self.root)
|
||||
}
|
||||
|
||||
@@ -502,7 +538,7 @@ impl Tools {
|
||||
|
||||
fn command(&self, program: impl AsRef<std::ffi::OsStr>) -> Command {
|
||||
let mut cmd = Command::new(program);
|
||||
ide_core::process::prepare(&mut cmd).env("PATH", &self.child_path);
|
||||
ide_core::process::prepare(&mut cmd).envs(&self.runtime_env).env("PATH", &self.child_path);
|
||||
cmd
|
||||
}
|
||||
|
||||
@@ -524,6 +560,9 @@ impl Tools {
|
||||
/// Aliases that only open the Store unless the Store's Python is installed, so they count
|
||||
/// only when they run.
|
||||
fn python(&self) -> Option<PathBuf> {
|
||||
if self.runtime_commands.contains_key("python") {
|
||||
return self.which("python");
|
||||
}
|
||||
if self.platform.os != Os::Windows {
|
||||
return self.which_any(&["python3", "python"]);
|
||||
}
|
||||
@@ -664,6 +703,13 @@ impl Tools {
|
||||
}
|
||||
|
||||
fn info_for(&self, name: &str, usage: Option<&Usage>) -> Option<ToolInfo> {
|
||||
match registry::find(name) {
|
||||
Some(spec) => self.for_method(spec.method).info_for_runtime(name, usage),
|
||||
None => self.info_for_runtime(name, usage),
|
||||
}
|
||||
}
|
||||
|
||||
fn info_for_runtime(&self, name: &str, usage: Option<&Usage>) -> Option<ToolInfo> {
|
||||
let spec = registry::find(name);
|
||||
let languages = usage.map(|u| u.languages.clone()).unwrap_or_default();
|
||||
let install_hint = usage.and_then(|u| u.install_hint.clone());
|
||||
@@ -868,6 +914,19 @@ impl Tools {
|
||||
langs: &[LanguageConfig],
|
||||
cancel: &CancellationToken,
|
||||
emit: Emit,
|
||||
) -> IdeResult<ToolInfo> {
|
||||
match registry::find(name) {
|
||||
Some(spec) => self.for_method(spec.method).install_for_runtime(name, langs, cancel, emit),
|
||||
None => self.install_for_runtime(name, langs, cancel, emit),
|
||||
}
|
||||
}
|
||||
|
||||
fn install_for_runtime(
|
||||
&self,
|
||||
name: &str,
|
||||
langs: &[LanguageConfig],
|
||||
cancel: &CancellationToken,
|
||||
emit: Emit,
|
||||
) -> IdeResult<ToolInfo> {
|
||||
let spec = registry::find(name).ok_or_else(|| {
|
||||
let hint = usages(langs).get(name).and_then(|u| u.install_hint.clone());
|
||||
@@ -1130,6 +1189,9 @@ impl Tools {
|
||||
if let Some(uv) = self.which("uv") {
|
||||
let mut c = self.command(&uv);
|
||||
c.arg("venv").arg(&venv);
|
||||
if let Some(python) = self.runtime_commands.get("python") {
|
||||
c.arg("--python").arg(python);
|
||||
}
|
||||
run_streamed(c, cancel, emit)?;
|
||||
let mut c = self.command(&uv);
|
||||
c.args(["pip", "install", "--python"]).arg(&python).arg(package);
|
||||
@@ -1371,6 +1433,13 @@ impl Tools {
|
||||
|
||||
/// Removes a tool the IDE installed.
|
||||
pub fn uninstall(&self, name: &str, langs: &[LanguageConfig]) -> IdeResult<ToolInfo> {
|
||||
match registry::find(name) {
|
||||
Some(spec) => self.for_method(spec.method).uninstall_for_runtime(name, langs),
|
||||
None => self.uninstall_for_runtime(name, langs),
|
||||
}
|
||||
}
|
||||
|
||||
fn uninstall_for_runtime(&self, name: &str, langs: &[LanguageConfig]) -> IdeResult<ToolInfo> {
|
||||
let spec = registry::find(name).ok_or_else(|| IdeError::not_found(format!("unknown tool `{name}`")))?;
|
||||
// Uninstalling mid-install would tear down the directory under the running installer.
|
||||
if INSTALLING.lock().as_ref().is_some_and(|m| m.contains_key(name)) {
|
||||
|
||||
@@ -438,12 +438,15 @@ esac"#,
|
||||
#[test]
|
||||
fn npm_install_uses_tools_prefix() {
|
||||
let env = Env::new();
|
||||
let sdk = env._tmp.path().join("selected-sdk/bin");
|
||||
fs::create_dir_all(&sdk).unwrap();
|
||||
let log = env.root.parent().unwrap().join("npm-args.txt");
|
||||
env.script(
|
||||
&env.sys,
|
||||
&sdk,
|
||||
"npm",
|
||||
&format!(
|
||||
r#"echo "$@" > '{log}'
|
||||
r#"node --version >/dev/null || exit 98
|
||||
echo "$@" > '{log}'
|
||||
prefix=""; while [ $# -gt 0 ]; do [ "$1" = "--prefix" ] && prefix="$2"; shift; done
|
||||
/bin/mkdir -p "$prefix/node_modules/.bin" "$prefix/node_modules/bash-language-server"
|
||||
printf '#!/bin/sh\necho 5.4.0\n' > "$prefix/node_modules/.bin/bash-language-server"
|
||||
@@ -453,7 +456,14 @@ echo "added 1 package""#,
|
||||
log = log.display()
|
||||
),
|
||||
);
|
||||
let info = env.tools().install("bash-language-server", &langs(), &CancellationToken::new(), &|_| {}).unwrap();
|
||||
env.script(&sdk, "node", "exit 0");
|
||||
env.script(&env.sys, "node", "exit 99");
|
||||
env.script(&env.sys, "npm", "exit 99");
|
||||
let tools = env.tools().with_runtime(
|
||||
BTreeMap::from([("node".into(), sdk.join("node")), ("ruby".into(), env.sys.join("ruby"))]),
|
||||
BTreeMap::new(),
|
||||
);
|
||||
let info = tools.install("bash-language-server", &langs(), &CancellationToken::new(), &|_| {}).unwrap();
|
||||
assert_eq!(info.status, ToolStatus::Installed);
|
||||
assert_eq!(info.version.as_deref(), Some("5.4.0"));
|
||||
let args = fs::read_to_string(&log).unwrap();
|
||||
@@ -581,14 +591,19 @@ fn elixir_ls_needs_elixir_and_lists_its_debugger() {
|
||||
#[test]
|
||||
fn gem_install_wraps_stubs_with_gem_home() {
|
||||
let env = Env::new();
|
||||
let sdk = env._tmp.path().join("selected-sdk/bin");
|
||||
fs::create_dir_all(&sdk).unwrap();
|
||||
// `gem install --no-document --install-dir <home> --bindir <bin> solargraph`
|
||||
env.script(
|
||||
&env.sys,
|
||||
&sdk,
|
||||
"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();
|
||||
env.script(&sdk, "ruby", "exit 0");
|
||||
env.script(&env.sys, "ruby", "exit 99");
|
||||
env.script(&env.sys, "gem", "exit 99");
|
||||
let tools = env.tools().with_runtime(BTreeMap::from([("ruby".into(), sdk.join("ruby"))]), BTreeMap::new());
|
||||
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"));
|
||||
@@ -602,16 +617,18 @@ printf '#!/bin/sh\necho "$GEM_HOME"\n' > "$bin/solargraph"; /bin/chmod +x "$bin/
|
||||
#[test]
|
||||
fn ruby_lsp_installs_into_the_project_ruby_not_a_private_gem_home() {
|
||||
let env = Env::new();
|
||||
let sdk = env._tmp.path().join("selected-sdk/bin");
|
||||
fs::create_dir_all(&sdk).unwrap();
|
||||
let project = env._tmp.path().join("project");
|
||||
fs::create_dir_all(&project).unwrap();
|
||||
let marker = env._tmp.path().join("installed");
|
||||
// Homebrew's Ruby: the gem bindir is not on the search path; only the Ruby knows it.
|
||||
let bindir = env._tmp.path().join("gems/bin");
|
||||
fs::create_dir_all(&bindir).unwrap();
|
||||
env.script(&env.sys, "ruby", &format!("echo '{}'", bindir.display()));
|
||||
env.script(&sdk, "ruby", &format!("echo '{}'", bindir.display()));
|
||||
// `gem list --exact ruby-lsp` knows the gem once `gem install ruby-lsp` ran, in the cwd it was given.
|
||||
env.script(
|
||||
&env.sys,
|
||||
&sdk,
|
||||
"gem",
|
||||
&format!(
|
||||
r#"case "$1" in
|
||||
@@ -623,7 +640,10 @@ esac"#,
|
||||
b = bindir.display()
|
||||
),
|
||||
);
|
||||
let tools = env.tools().with_work_dir(Some(project.clone()));
|
||||
env.script(&env.sys, "ruby", "exit 99");
|
||||
env.script(&env.sys, "gem", "exit 99");
|
||||
let tools = env.tools().with_runtime(BTreeMap::from([("ruby".into(), sdk.join("ruby"))]), BTreeMap::new());
|
||||
let tools = tools.with_work_dir(Some(project.clone()));
|
||||
assert_eq!(find(&tools.list(&langs()), "ruby-lsp").status, ToolStatus::Missing);
|
||||
let info = tools.install("ruby-lsp", &langs(), &CancellationToken::new(), &|_| {}).unwrap();
|
||||
assert_eq!(info.status, ToolStatus::OnPath);
|
||||
@@ -800,3 +820,88 @@ fn python_probes_run_in_an_empty_private_directory() {
|
||||
let dir = env.root.join(".python-cwd");
|
||||
assert_eq!(out.trim(), dir.canonicalize().unwrap().to_string_lossy());
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn go_installers_use_selected_sdk_instead_of_broken_mise_shim() {
|
||||
for spec in registry::TOOLS.iter().filter(|s| matches!(s.method, registry::Method::Go { .. })) {
|
||||
let env = Env::new();
|
||||
env.script(&env.sys, "go", "echo 'mise ERROR No version is set for shim: go' >&2; exit 1");
|
||||
let sdk = env._tmp.path().join("selected go");
|
||||
let log = env._tmp.path().join("go.log");
|
||||
env.script(
|
||||
&sdk.join("bin"),
|
||||
"go",
|
||||
&format!(
|
||||
r#"
|
||||
[ "$GOROOT" = '{sdk}' ] || exit 99
|
||||
echo "$@" >> '{log}'
|
||||
case "$1" in
|
||||
install) printf '#!/bin/sh\necho 1.2.3\n' > "$GOBIN/{command}"; /bin/chmod +x "$GOBIN/{command}" ;;
|
||||
version) echo 'mod example.com/tool v1.2.3' ;;
|
||||
esac
|
||||
"#,
|
||||
sdk = sdk.display(),
|
||||
log = log.display(),
|
||||
command = spec.commands[0]
|
||||
),
|
||||
);
|
||||
let tools = env.tools().with_runtime(
|
||||
BTreeMap::from([("go".into(), sdk.join("bin/go"))]),
|
||||
BTreeMap::from([("GOROOT".into(), sdk.to_string_lossy().into_owned())]),
|
||||
);
|
||||
let info = tools.install(spec.name, &langs(), &CancellationToken::new(), &|_| {}).unwrap();
|
||||
assert_eq!(info.status, ToolStatus::Installed, "{}", spec.name);
|
||||
assert_eq!(info.version.as_deref(), Some("1.2.3"));
|
||||
let log = fs::read_to_string(log).unwrap();
|
||||
assert!(log.contains("install -v ") && log.contains("version -m"), "{log}");
|
||||
tools.uninstall(spec.name, &langs()).unwrap();
|
||||
assert!(
|
||||
env.tools()
|
||||
.install(spec.name, &langs(), &CancellationToken::new(), &|_| {})
|
||||
.unwrap_err()
|
||||
.message
|
||||
.contains("failed")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn selected_python_is_used_for_pip_and_uv_even_with_a_versioned_filename() {
|
||||
let env = Env::new();
|
||||
env.script(&env.sys, "python3", "exit 99");
|
||||
let sdk = env._tmp.path().join("python-sdk");
|
||||
let pip_log = env._tmp.path().join("pip.log");
|
||||
env.script(
|
||||
&sdk,
|
||||
"python3.14",
|
||||
&format!(
|
||||
r#"
|
||||
echo "$@" >> '{log}'
|
||||
[ "$1" = "-c" ] && exit 1
|
||||
while [ $# -gt 0 ]; do
|
||||
if [ "$1" = "--target" ]; then
|
||||
/bin/mkdir -p "$2/debugpy" "$2/debugpy-1.2.3.dist-info"
|
||||
/usr/bin/touch "$2/debugpy/__init__.py"
|
||||
exit 0
|
||||
fi
|
||||
shift
|
||||
done
|
||||
exit 1
|
||||
"#,
|
||||
log = pip_log.display()
|
||||
),
|
||||
);
|
||||
let tools = env.tools().with_runtime(BTreeMap::from([("python".into(), sdk.join("python3.14"))]), BTreeMap::new());
|
||||
assert_eq!(tools.python(), Some(sdk.join("python3.14")));
|
||||
let info = tools.install("debugpy", &langs(), &CancellationToken::new(), &|_| {}).unwrap();
|
||||
assert_eq!(info.status, ToolStatus::Installed);
|
||||
assert_eq!(info.version.as_deref(), Some("1.2.3"));
|
||||
assert!(fs::read_to_string(pip_log).unwrap().contains("-m pip install"));
|
||||
let log = env._tmp.path().join("uv.log");
|
||||
env.script(&env.sys, "uv", &format!("echo \"$@\" >> '{}'; exit 1", log.display()));
|
||||
let _ = tools.install("ruff", &langs(), &CancellationToken::new(), &|_| {});
|
||||
let args = fs::read_to_string(log).unwrap();
|
||||
assert!(args.contains(&format!("--python {}", sdk.join("python3.14").display())), "{args}");
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import { ConsoleView, type ConsoleInput } from "./ConsoleView";
|
||||
import { buildRows, mnemonics } from "./ConfigChooser";
|
||||
import { activate } from "./index";
|
||||
import {
|
||||
onRunExited,
|
||||
closeSessionTab,
|
||||
resetRuns,
|
||||
runtime,
|
||||
@@ -322,3 +323,24 @@ describe("plugin test selection launches", () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Go SDK recovery", () => {
|
||||
it.each([
|
||||
["mise ERROR No version is set for shim: go", "go", 1, true],
|
||||
["No version is set for shim: go", "shell", 1, false],
|
||||
["compile error", "go", 1, false],
|
||||
["No version is set for shim: go", "go", 0, false],
|
||||
] as const)("offers Go settings for %s (%s, %s)", async (text, kind, code, expected) => {
|
||||
const notify = vi.fn();
|
||||
provideNotificationService({ notify });
|
||||
const session = (await startRun(config("go", { kind })))!;
|
||||
runtime(session.id)!.console.append({ kind: "stderr", text: text.slice(0, 12) });
|
||||
runtime(session.id)!.console.append({ kind: "stderr", text: text.slice(12) + "\n" });
|
||||
const runId = 4321 as NonNullable<RunSession["runId"]>;
|
||||
useRun.setState((s) => ({ sessions: s.sessions.map((x) => ({ ...x, runId })) }));
|
||||
onRunExited(runId, code);
|
||||
await vi.waitFor(() => expect(useRun.getState().sessions[0]!.status).toBe("finished"));
|
||||
expect(notify).toHaveBeenCalledTimes(expected ? 1 : 0);
|
||||
if (expected) expect(notify.mock.calls[0]![0].actions[0].label).toBe("Go Settings…");
|
||||
});
|
||||
});
|
||||
@@ -1103,6 +1103,21 @@ export function testReportEvents(reports: readonly TestCaseReport[]): TestEvent[
|
||||
function notifyExit(sessionId: string, code: number | null) {
|
||||
const session = getSession(sessionId);
|
||||
if (!session || session.stopped || code === 0 || code === null) return;
|
||||
if (
|
||||
session.config.kind === "go" &&
|
||||
!useProject.getState().settings?.languages?.go?.goroot?.trim() &&
|
||||
/No version (?:is )?set for (?:shim: )?go/i.test(runtimes.get(sessionId)?.console.text() ?? "")
|
||||
) {
|
||||
notifications().notify({
|
||||
group: "run",
|
||||
level: "error",
|
||||
key: `run-exit-${sessionId}`,
|
||||
title: tx("Choose a Go SDK"),
|
||||
body: tx("The Go version manager has no SDK selected. Choose an installed SDK in Go settings and run again."),
|
||||
actions: [{ label: tx("Go Settings…"), run: () => void executeAction("file.settings", "languages.go") }],
|
||||
});
|
||||
return;
|
||||
}
|
||||
let visible = true;
|
||||
try {
|
||||
visible = toolWindowService().isVisible("run");
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
"Active frames keep running the old code until they return. New calls use the reloaded code.": "Active frames keep running the old code until they return. New calls use the reloaded code.",
|
||||
"The bottom frame cannot be reset. Re-enter the method or restart the debug session to run the new code.": "The bottom frame cannot be reset. Re-enter the method or restart the debug session to run the new code.",
|
||||
"Reset Frame ({0})": "Reset Frame ({0})",
|
||||
"Choose a Go SDK": "Choose a Go SDK",
|
||||
"The Go version manager has no SDK selected. Choose an installed SDK in Go settings and run again.": "The Go version manager has no SDK selected. Choose an installed SDK in Go settings and run again.",
|
||||
"Go Settings…": "Go Settings…",
|
||||
"Active Editor": "Active Editor",
|
||||
"Soft-Wrap": "Soft-Wrap",
|
||||
"The language server Java executable ({path}) cannot run on this machine. Choose a working JDK in {setting}, or set it to Auto.": "The language server Java executable ({path}) cannot run on this machine. Choose a working JDK in {setting}, or set it to Auto.",
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
"Active frames keep running the old code until they return. New calls use the reloaded code.": "活动栈帧将继续执行旧代码,直到方法返回。新的调用将使用重新加载的代码。",
|
||||
"The bottom frame cannot be reset. Re-enter the method or restart the debug session to run the new code.": "无法重置栈底帧。请重新进入该方法或重启调试会话,以执行新代码。",
|
||||
"Reset Frame ({0})": "重置栈帧({0})",
|
||||
"Choose a Go SDK": "选择 Go SDK",
|
||||
"The Go version manager has no SDK selected. Choose an installed SDK in Go settings and run again.": "Go 版本管理器未选择 SDK。请在 Go 设置中选择已安装的 SDK,然后重新运行。",
|
||||
"Go Settings…": "Go 设置…",
|
||||
"Active Editor": "活动编辑器",
|
||||
"The language server Java executable ({path}) cannot run on this machine. Choose a working JDK in {setting}, or set it to Auto.": "语言服务器的 Java 可执行文件({path})无法在此机器上运行。请在 {setting} 中选择可正常运行的 JDK,或设为自动。",
|
||||
"Input queue is full (4 MiB or 1024 writes); input was not sent.": "输入队列已满(4 MiB 或 1024 次写入);本次输入未发送。",
|
||||
|
||||
Reference in new issue
Заблокувати користувача