From d04288e0f3d292ce47fc0246bcbdc50a9d57ad5e Mon Sep 17 00:00:00 2001 From: jazzfool Date: Fri, 31 Mar 2023 03:21:40 +1100 Subject: Canonicalize paths before stripping current dir as prefix (#6290) Co-authored-by: jazzfool --- helix-core/Cargo.toml | 1 + helix-core/src/path.rs | 29 +++++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) (limited to 'helix-core') diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index 9dfef9ae..e5c5f8f1 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -32,6 +32,7 @@ regex = "1" bitflags = "2.0" ahash = "0.8.3" hashbrown = { version = "0.13.2", features = ["raw"] } +dunce = "1.0" log = "0.4" serde = { version = "1.0", features = ["derive"] } diff --git a/helix-core/src/path.rs b/helix-core/src/path.rs index d59a6baa..efa46c46 100644 --- a/helix-core/src/path.rs +++ b/helix-core/src/path.rs @@ -40,6 +40,21 @@ pub fn expand_tilde(path: &Path) -> PathBuf { /// needs to improve on. /// Copied from cargo: pub fn get_normalized_path(path: &Path) -> PathBuf { + // normalization strategy is to canonicalize first ancestor path that exists (i.e., canonicalize as much as possible), + // then run handrolled normalization on the non-existent remainder + let (base, path) = path + .ancestors() + .find_map(|base| { + let canonicalized_base = dunce::canonicalize(base).ok()?; + let remainder = path.strip_prefix(base).ok()?.into(); + Some((canonicalized_base, remainder)) + }) + .unwrap_or_else(|| (PathBuf::new(), PathBuf::from(path))); + + if path.as_os_str().is_empty() { + return base; + } + let mut components = path.components().peekable(); let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { components.next(); @@ -63,7 +78,7 @@ pub fn get_normalized_path(path: &Path) -> PathBuf { } } } - ret + base.join(ret) } /// Returns the canonical, absolute form of a path with all intermediate components normalized. @@ -82,13 +97,19 @@ pub fn get_canonicalized_path(path: &Path) -> std::io::Result { } pub fn get_relative_path(path: &Path) -> PathBuf { + let path = PathBuf::from(path); let path = if path.is_absolute() { - let cwdir = std::env::current_dir().expect("couldn't determine current directory"); - path.strip_prefix(cwdir).unwrap_or(path) + let cwdir = std::env::current_dir() + .map(|path| get_normalized_path(&path)) + .expect("couldn't determine current directory"); + get_normalized_path(&path) + .strip_prefix(cwdir) + .map(PathBuf::from) + .unwrap_or(path) } else { path }; - fold_home_dir(path) + fold_home_dir(&path) } /// Returns a truncated filepath where the basepart of the path is reduced to the first -- cgit v1.2.3-70-g09d2