diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f6ad008..a63d31b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Nushell: support for v0.86.0. +### Fixed + +- Normalize drive letters when resolving paths on Windows. + ## [0.9.2] - 2023-08-04 ### Added diff --git a/src/shell.rs b/src/shell.rs index a50c184b..1a3ad1e0 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -196,7 +196,7 @@ mod tests { } #[apply(opts)] - fn posix_shellcheck_(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) { + fn posix_shellcheck(cmd: Option<&str>, hook: InitHook, echo: bool, resolve_symlinks: bool) { let opts = Opts { cmd, hook, echo, resolve_symlinks }; let source = Posix(&opts).render().unwrap(); diff --git a/src/util.rs b/src/util.rs index 1f8fc95f..b0117887 100644 --- a/src/util.rs +++ b/src/util.rs @@ -291,44 +291,75 @@ pub fn resolve_path(path: impl AsRef) -> Result { } } - fn get_drive_path(drive_letter: u8) -> PathBuf { - format!(r"{}:\", drive_letter as char).into() + fn get_drive_prefix_path(drive_letter: u8) -> PathBuf { + format!(r"{}:\", patch_drive_letter(drive_letter)).into() } - fn get_drive_relative(drive_letter: u8) -> Result { + fn get_drive_relative_path(drive_letter: u8) -> Result { let path = current_dir()?; if Some(drive_letter) == get_drive_letter(&path) { - return Ok(path); + return Ok(patch_drive_prefix(path)); } - if let Some(path) = env::var_os(format!("={}:", drive_letter as char)) { - return Ok(path.into()); + if let Some(path) = env::var_os(format!("={}:", patch_drive_letter(drive_letter))) { + return Ok(patch_drive_prefix(path.into())); } - let path = get_drive_path(drive_letter); + let path = get_drive_prefix_path(drive_letter); Ok(path) } + fn patch_drive_letter(drive_letter: u8) -> char { + drive_letter.to_ascii_uppercase() as char + } + + // https://github.com/rust-lang/rust-analyzer/pull/14689 + fn patch_drive_prefix(path: PathBuf) -> PathBuf { + let mut components = path.components(); + + match components.next() { + Some(Component::Prefix(prefix)) => { + let prefix = match prefix.kind() { + Prefix::Disk(drive_letter) => { + format!(r"{}:", patch_drive_letter(drive_letter)) + } + Prefix::VerbatimDisk(drive_letter) => { + format!(r"\\?\{}:", patch_drive_letter(drive_letter)) + } + _ => return path, + }; + + let mut path = PathBuf::default(); + path.push(prefix); + path.extend(components); + path + } + _ => path, + } + } + match components.peek() { Some(Component::Prefix(prefix)) => match prefix.kind() { Prefix::Disk(drive_letter) => { - let disk = components.next().unwrap(); + components.next(); if components.peek() == Some(&Component::RootDir) { - let root = components.next().unwrap(); - stack.push(disk); - stack.push(root); + components.next(); + base_path = get_drive_prefix_path(drive_letter); } else { - base_path = get_drive_relative(drive_letter)?; - stack.extend(base_path.components()); + base_path = get_drive_relative_path(drive_letter)?; } + + stack.extend(base_path.components()); } Prefix::VerbatimDisk(drive_letter) => { components.next(); if components.peek() == Some(&Component::RootDir) { components.next(); + base_path = get_drive_prefix_path(drive_letter); + } else { + bail!("illegal path: {}", path.display()); } - base_path = get_drive_path(drive_letter); stack.extend(base_path.components()); } _ => bail!("invalid path: {}", path.display()), @@ -340,7 +371,7 @@ pub fn resolve_path(path: impl AsRef) -> Result { let drive_letter = get_drive_letter(¤t_dir).with_context(|| { format!("could not get drive letter: {}", current_dir.display()) })?; - base_path = get_drive_path(drive_letter); + base_path = get_drive_prefix_path(drive_letter); stack.extend(base_path.components()); } _ => {