diff --git a/crates/ide-sdk/src/discover.rs b/crates/ide-sdk/src/discover.rs index 0be9d78c..44633bff 100644 --- a/crates/ide-sdk/src/discover.rs +++ b/crates/ide-sdk/src/discover.rs @@ -545,7 +545,7 @@ fn jdk() -> Vec { /// A JDK as the SDK pickers list it (`Eclipse Adoptium 25.0.1`). pub fn jdk_sdk(j: &crate::java::Jdk) -> Sdk { - let java_home = std::env::var_os("JAVA_HOME").and_then(|h| dunce::canonicalize(h).ok()); + let java_home = std::env::var_os("JAVA_HOME").and_then(|h| crate::launch_path(Path::new(&h)).ok()); let release = std::fs::read_to_string(j.home.join("release")).unwrap_or_default(); let version = release_value(&release, "JAVA_VERSION").unwrap_or_else(|| j.major.to_string()); let name = match release_value(&release, "IMPLEMENTOR") { @@ -579,31 +579,6 @@ fn maven_version(home: &Path) -> Option { }) } -/// `path` with symlinks resolved (Homebrew's `bin/mvn` → its Cellar), except that a Windows -/// drive letter is kept: on a mapped network drive or a `subst` drive canonicalizing gives -/// `\\?\UNC\server\share\…` or another drive, and cmd.exe can't start `mvn.cmd` from a -/// `\\?\` path (nor is it the path the user knows). -fn launch_path(path: &Path) -> std::io::Result { - let real = dunce::canonicalize(path)?; - #[cfg(windows)] - { - use std::path::{Component, Prefix}; - let drive = |p: &Path| match p.components().next() { - Some(Component::Prefix(x)) => match x.kind() { - Prefix::Disk(d) | Prefix::VerbatimDisk(d) => Some(d.to_ascii_uppercase()), - _ => None, - }, - _ => None, - }; - if let Some(d) = drive(path) - && drive(&real) != Some(d) - { - return std::path::absolute(path); - } - } - Ok(real) -} - /// A Maven home (`bin/mvn` and `lib/`); Homebrew's formula keeps it in `libexec`. fn maven_home(dir: &Path) -> Option { let mvn = if cfg!(windows) { "mvn.cmd" } else { "mvn" }; @@ -620,7 +595,7 @@ fn maven() -> Vec { } else { which("mvn") }; - if let Some(mvn) = on_path.and_then(|p| launch_path(&p).ok()) + if let Some(mvn) = on_path.and_then(|p| crate::launch_path(&p).ok()) && let Some(home) = mvn.parent().and_then(Path::parent) { found.push((home.to_path_buf(), "path")); @@ -654,7 +629,7 @@ fn maven() -> Vec { let mut out = Vec::new(); for (dir, source) in found { let Some(home) = maven_home(&dir) else { continue }; - let home = launch_path(&home).unwrap_or(home); + let home = crate::launch_path(&home).unwrap_or(home); if !seen.insert(home.clone()) { continue; } diff --git a/crates/ide-sdk/src/java.rs b/crates/ide-sdk/src/java.rs index a2f60063..05f89fc9 100644 --- a/crates/ide-sdk/src/java.rs +++ b/crates/ide-sdk/src/java.rs @@ -38,10 +38,7 @@ pub fn jdk_at(home: &Path) -> Option { if !home.join("bin").join(crate::exe("java")).is_file() { return None; } - Some(Jdk { - major: parse_release(&release)?, - home: dunce::canonicalize(home).unwrap_or_else(|_| home.to_path_buf()), - }) + Some(Jdk { major: parse_release(&release)?, home: crate::launch_path(home).unwrap_or_else(|_| home.to_path_buf()) }) } fn children(dir: &Path) -> Vec { @@ -90,7 +87,7 @@ fn scan() -> Vec { let path = std::env::var_os("PATH").unwrap_or_default(); if let Some(java) = std::env::split_paths(&path).map(|d| d.join(crate::exe("java"))).find(|j| j.is_file()) { // Through symlinks (`/usr/bin/java`, Homebrew, SDKMAN `current`) to `/bin/java`. - if let Some(home) = dunce::canonicalize(java).ok().and_then(|j| j.parent()?.parent().map(Path::to_path_buf)) { + if let Some(home) = crate::launch_path(&java).ok().and_then(|j| j.parent()?.parent().map(Path::to_path_buf)) { homes.push(home); } } diff --git a/crates/ide-sdk/src/lib.rs b/crates/ide-sdk/src/lib.rs index ef4efaf6..722d8b2d 100644 --- a/crates/ide-sdk/src/lib.rs +++ b/crates/ide-sdk/src/lib.rs @@ -82,6 +82,31 @@ pub(crate) fn exe(name: &str) -> String { if cfg!(windows) { format!("{name}.exe") } else { name.to_owned() } } +/// `path` with symlinks resolved (Homebrew's `bin/mvn` → its Cellar), except that a Windows +/// drive letter is kept: on a mapped network drive or a `subst` drive canonicalizing gives +/// `\\?\UNC\server\share\…` or another drive, and cmd.exe can't start a script from a +/// `\\?\` path (`mvn.cmd`, or `mvn.cmd` with such a `JAVA_HOME`), nor is it the path the user knows. +pub(crate) fn launch_path(path: &Path) -> std::io::Result { + let real = dunce::canonicalize(path)?; + #[cfg(windows)] + { + use std::path::{Component, Prefix}; + let drive = |p: &Path| match p.components().next() { + Some(Component::Prefix(x)) => match x.kind() { + Prefix::Disk(d) | Prefix::VerbatimDisk(d) => Some(d.to_ascii_uppercase()), + _ => None, + }, + _ => None, + }; + if let Some(d) = drive(path) + && drive(&real) != Some(d) + { + return std::path::absolute(path); + } + } + Ok(real) +} + pub(crate) fn home_dir() -> Option { std::env::var_os("HOME").or_else(|| std::env::var_os("USERPROFILE")).map(PathBuf::from) }