diff --git a/crates/assistant_slash_commands/src/file_command.rs b/crates/assistant_slash_commands/src/file_command.rs index a973d653e4527be808618f76d60af59e4a891947..261e15bc0ae8b9e886d4d146696db78e5c0c831d 100644 --- a/crates/assistant_slash_commands/src/file_command.rs +++ b/crates/assistant_slash_commands/src/file_command.rs @@ -492,7 +492,7 @@ mod custom_path_matcher { pub fn new(globs: &[String]) -> Result { let globs = globs .iter() - .map(|glob| Glob::new(&SanitizedPath::from(glob).to_glob_string())) + .map(|glob| Glob::new(&SanitizedPath::new(glob).to_glob_string())) .collect::, _>>()?; let sources = globs.iter().map(|glob| glob.glob().to_owned()).collect(); let sources_with_trailing_slash = globs diff --git a/crates/fs/src/fs.rs b/crates/fs/src/fs.rs index 75312c5c0cec4a4b285ff0320d90eeda1c0a4c6a..a5cf9b88254deff5b9a07402207f19875827d7f0 100644 --- a/crates/fs/src/fs.rs +++ b/crates/fs/src/fs.rs @@ -495,7 +495,8 @@ impl Fs for RealFs { }; // todo(windows) // When new version of `windows-rs` release, make this operation `async` - let path = SanitizedPath::from(path.canonicalize()?); + let path = path.canonicalize()?; + let path = SanitizedPath::new(&path); let path_string = path.to_string(); let file = StorageFile::GetFileFromPathAsync(&HSTRING::from(path_string))?.get()?; file.DeleteAsync(StorageDeleteOption::Default)?.get()?; @@ -522,7 +523,8 @@ impl Fs for RealFs { // todo(windows) // When new version of `windows-rs` release, make this operation `async` - let path = SanitizedPath::from(path.canonicalize()?); + let path = path.canonicalize()?; + let path = SanitizedPath::new(&path); let path_string = path.to_string(); let folder = StorageFolder::GetFolderFromPathAsync(&HSTRING::from(path_string))?.get()?; folder.DeleteAsync(StorageDeleteOption::Default)?.get()?; @@ -783,7 +785,7 @@ impl Fs for RealFs { { target = parent.join(target); if let Ok(canonical) = self.canonicalize(&target).await { - target = SanitizedPath::from(canonical).as_path().to_path_buf(); + target = SanitizedPath::new(&canonical).as_path().to_path_buf(); } } watcher.add(&target).ok(); diff --git a/crates/fs/src/fs_watcher.rs b/crates/fs/src/fs_watcher.rs index 6ad03ba6dfa2003b1642cbb542e3a9cf0bf13ec9..07374b7f40455f09cf52d31ddd1a1f64ab6abcd3 100644 --- a/crates/fs/src/fs_watcher.rs +++ b/crates/fs/src/fs_watcher.rs @@ -42,7 +42,7 @@ impl Drop for FsWatcher { impl Watcher for FsWatcher { fn add(&self, path: &std::path::Path) -> anyhow::Result<()> { - let root_path = SanitizedPath::from(path); + let root_path = SanitizedPath::new_arc(path); let tx = self.tx.clone(); let pending_paths = self.pending_path_events.clone(); @@ -70,7 +70,7 @@ impl Watcher for FsWatcher { .paths .iter() .filter_map(|event_path| { - let event_path = SanitizedPath::from(event_path); + let event_path = SanitizedPath::new(event_path); event_path.starts_with(&root_path).then(|| PathEvent { path: event_path.as_path().to_path_buf(), kind, diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index 5ac2be2f238e9c6986a5496af5e69a6ef658c0f7..3a6ccff90f06156345a71482fe723c76d4c2ca39 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -851,7 +851,7 @@ fn file_save_dialog( if !directory.to_string_lossy().is_empty() && let Some(full_path) = directory.canonicalize().log_err() { - let full_path = SanitizedPath::from(full_path); + let full_path = SanitizedPath::new(&full_path); let full_path_string = full_path.to_string(); let path_item: IShellItem = unsafe { SHCreateItemFromParsingName(&HSTRING::from(full_path_string), None)? }; diff --git a/crates/paths/src/paths.rs b/crates/paths/src/paths.rs index aab0354c9696f8bcdde5fd4bb00bd3651ac4b888..c2c3c89305939bc32c635549c23d64d565f8fbb0 100644 --- a/crates/paths/src/paths.rs +++ b/crates/paths/src/paths.rs @@ -63,7 +63,7 @@ pub fn set_custom_data_dir(dir: &str) -> &'static PathBuf { let abs_path = path .canonicalize() .expect("failed to canonicalize custom data directory's path to an absolute path"); - path = PathBuf::from(util::paths::SanitizedPath::from(abs_path)) + path = util::paths::SanitizedPath::new(&abs_path).into() } std::fs::create_dir_all(&path).expect("failed to create custom data directory"); path diff --git a/crates/project/src/git_store.rs b/crates/project/src/git_store.rs index a1c0508c3ed5355685828487229967c83b59cbd3..b7ff3e7fefc9b2e8aede04d3cd0fca88c16c2a62 100644 --- a/crates/project/src/git_store.rs +++ b/crates/project/src/git_store.rs @@ -62,7 +62,7 @@ use std::{ }; use sum_tree::{Edit, SumTree, TreeSet}; use text::{Bias, BufferId}; -use util::{ResultExt, debug_panic, post_inc}; +use util::{ResultExt, debug_panic, paths::SanitizedPath, post_inc}; use worktree::{ File, PathChange, PathKey, PathProgress, PathSummary, PathTarget, ProjectEntryId, UpdatedGitRepositoriesSet, UpdatedGitRepository, Worktree, @@ -3234,6 +3234,7 @@ impl Repository { let git_store = self.git_store.upgrade()?; let worktree_store = git_store.read(cx).worktree_store.read(cx); let abs_path = self.snapshot.work_directory_abs_path.join(&path.0); + let abs_path = SanitizedPath::new(&abs_path); let (worktree, relative_path) = worktree_store.find_worktree(abs_path, cx)?; Some(ProjectPath { worktree_id: worktree.read(cx).id(), diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index d11e5679968095d0a6541ab320ac3476dc7f794a..b4c7c0bc37fc0409570ece3c5e3df00b1b1cd89f 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -3186,7 +3186,7 @@ impl LocalLspStore { } else { let (path, pattern) = match &watcher.glob_pattern { lsp::GlobPattern::String(s) => { - let watcher_path = SanitizedPath::from(s); + let watcher_path = SanitizedPath::new(s); let path = glob_literal_prefix(watcher_path.as_path()); let pattern = watcher_path .as_path() @@ -3278,7 +3278,7 @@ impl LocalLspStore { let worktree_root_path = tree.abs_path(); match &watcher.glob_pattern { lsp::GlobPattern::String(s) => { - let watcher_path = SanitizedPath::from(s); + let watcher_path = SanitizedPath::new(s); let relative = watcher_path .as_path() .strip_prefix(&worktree_root_path) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 86b2e08d629c68d60e241b677288750d32a4bcbd..68e04cfd3bec25638964e8fccd675279c450795d 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -2061,13 +2061,12 @@ impl Project { exclude_sub_dirs: bool, cx: &App, ) -> Option { - let sanitized_path = SanitizedPath::from(path); - let path = sanitized_path.as_path(); + let path = SanitizedPath::new(path).as_path(); self.worktrees(cx) .filter_map(|worktree| { let worktree = worktree.read(cx); let abs_path = worktree.as_local()?.abs_path(); - let contains = path == abs_path + let contains = path == abs_path.as_ref() || (path.starts_with(abs_path) && (!exclude_sub_dirs || !metadata.is_dir)); contains.then(|| worktree.is_visible()) }) diff --git a/crates/project/src/worktree_store.rs b/crates/project/src/worktree_store.rs index 9033415ca40b4550e5f98bae2de8314be2e1a5fe..b814e46bd1584cb076a057623b8b66800365876d 100644 --- a/crates/project/src/worktree_store.rs +++ b/crates/project/src/worktree_store.rs @@ -61,7 +61,7 @@ pub struct WorktreeStore { worktrees_reordered: bool, #[allow(clippy::type_complexity)] loading_worktrees: - HashMap, Arc>>>>, + HashMap, Shared, Arc>>>>, state: WorktreeStoreState, } @@ -153,10 +153,10 @@ impl WorktreeStore { pub fn find_worktree( &self, - abs_path: impl Into, + abs_path: impl AsRef, cx: &App, ) -> Option<(Entity, PathBuf)> { - let abs_path: SanitizedPath = abs_path.into(); + let abs_path = SanitizedPath::new(&abs_path); for tree in self.worktrees() { if let Ok(relative_path) = abs_path.as_path().strip_prefix(tree.read(cx).abs_path()) { return Some((tree.clone(), relative_path.into())); @@ -212,11 +212,11 @@ impl WorktreeStore { pub fn create_worktree( &mut self, - abs_path: impl Into, + abs_path: impl AsRef, visible: bool, cx: &mut Context, ) -> Task>> { - let abs_path: SanitizedPath = abs_path.into(); + let abs_path: Arc = SanitizedPath::new_arc(&abs_path); if !self.loading_worktrees.contains_key(&abs_path) { let task = match &self.state { WorktreeStoreState::Remote { @@ -227,8 +227,7 @@ impl WorktreeStore { if upstream_client.is_via_collab() { Task::ready(Err(Arc::new(anyhow!("cannot create worktrees via collab")))) } else { - let abs_path = - RemotePathBuf::new(abs_path.as_path().to_path_buf(), *path_style); + let abs_path = RemotePathBuf::new(abs_path.to_path_buf(), *path_style); self.create_ssh_worktree(upstream_client.clone(), abs_path, visible, cx) } } @@ -321,15 +320,21 @@ impl WorktreeStore { fn create_local_worktree( &mut self, fs: Arc, - abs_path: impl Into, + abs_path: Arc, visible: bool, cx: &mut Context, ) -> Task, Arc>> { let next_entry_id = self.next_entry_id.clone(); - let path: SanitizedPath = abs_path.into(); cx.spawn(async move |this, cx| { - let worktree = Worktree::local(path.clone(), visible, fs, next_entry_id, cx).await; + let worktree = Worktree::local( + SanitizedPath::cast_arc(abs_path.clone()), + visible, + fs, + next_entry_id, + cx, + ) + .await; let worktree = worktree?; @@ -337,7 +342,7 @@ impl WorktreeStore { if visible { cx.update(|cx| { - cx.add_recent_document(path.as_path()); + cx.add_recent_document(abs_path.as_path()); }) .log_err(); } diff --git a/crates/remote/src/transport/ssh.rs b/crates/remote/src/transport/ssh.rs index 750fc6dc586c227c6461ab4650c1b46b6bb01dc6..0036a687a6f73b57723e8c3c9fcffc56cab626c2 100644 --- a/crates/remote/src/transport/ssh.rs +++ b/crates/remote/src/transport/ssh.rs @@ -902,7 +902,7 @@ impl SshRemoteConnection { // On Windows, the binding needs to be set to the canonical path #[cfg(target_os = "windows")] let src = - SanitizedPath::from(smol::fs::canonicalize("./target").await?).to_glob_string(); + SanitizedPath::new(&smol::fs::canonicalize("./target").await?).to_glob_string(); #[cfg(not(target_os = "windows"))] let src = "./target"; run_cmd( diff --git a/crates/util/src/paths.rs b/crates/util/src/paths.rs index 1192b14812580bf21e262620a3ccefc90c5acd54..318900d540172035b29ae25ad5f42dbbac87bf60 100644 --- a/crates/util/src/paths.rs +++ b/crates/util/src/paths.rs @@ -3,6 +3,7 @@ use regex::Regex; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::fmt::{Display, Formatter}; +use std::mem; use std::path::StripPrefixError; use std::sync::{Arc, OnceLock}; use std::{ @@ -99,21 +100,86 @@ impl> PathExt for T { } } -/// Due to the issue of UNC paths on Windows, which can cause bugs in various parts of Zed, introducing this `SanitizedPath` -/// leverages Rust's type system to ensure that all paths entering Zed are always "sanitized" by removing the `\\\\?\\` prefix. -/// On non-Windows operating systems, this struct is effectively a no-op. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct SanitizedPath(pub Arc); +/// In memory, this is identical to `Path`. On non-Windows conversions to this type are no-ops. On +/// windows, these conversions sanitize UNC paths by removing the `\\\\?\\` prefix. +#[derive(Eq, PartialEq, Hash, Ord, PartialOrd)] +#[repr(transparent)] +pub struct SanitizedPath(Path); impl SanitizedPath { - pub fn starts_with(&self, prefix: &SanitizedPath) -> bool { + pub fn new + ?Sized>(path: &T) -> &Self { + #[cfg(not(target_os = "windows"))] + return Self::unchecked_new(path.as_ref()); + + #[cfg(target_os = "windows")] + return Self::unchecked_new(dunce::simplified(path.as_ref())); + } + + pub fn unchecked_new + ?Sized>(path: &T) -> &Self { + // safe because `Path` and `SanitizedPath` have the same repr and Drop impl + unsafe { mem::transmute::<&Path, &Self>(path.as_ref()) } + } + + pub fn from_arc(path: Arc) -> Arc { + // safe because `Path` and `SanitizedPath` have the same repr and Drop impl + #[cfg(not(target_os = "windows"))] + return unsafe { mem::transmute::, Arc>(path) }; + + // TODO: could avoid allocating here if dunce::simplified results in the same path + #[cfg(target_os = "windows")] + return Self::new(&path).into(); + } + + pub fn new_arc + ?Sized>(path: &T) -> Arc { + Self::new(path).into() + } + + pub fn cast_arc(path: Arc) -> Arc { + // safe because `Path` and `SanitizedPath` have the same repr and Drop impl + unsafe { mem::transmute::, Arc>(path) } + } + + pub fn cast_arc_ref(path: &Arc) -> &Arc { + // safe because `Path` and `SanitizedPath` have the same repr and Drop impl + unsafe { mem::transmute::<&Arc, &Arc>(path) } + } + + pub fn starts_with(&self, prefix: &Self) -> bool { self.0.starts_with(&prefix.0) } - pub fn as_path(&self) -> &Arc { + pub fn as_path(&self) -> &Path { &self.0 } + pub fn file_name(&self) -> Option<&std::ffi::OsStr> { + self.0.file_name() + } + + pub fn extension(&self) -> Option<&std::ffi::OsStr> { + self.0.extension() + } + + pub fn join>(&self, path: P) -> PathBuf { + self.0.join(path) + } + + pub fn parent(&self) -> Option<&Self> { + self.0.parent().map(Self::unchecked_new) + } + + pub fn strip_prefix(&self, base: &Self) -> Result<&Path, StripPrefixError> { + self.0.strip_prefix(base.as_path()) + } + + pub fn to_str(&self) -> Option<&str> { + self.0.to_str() + } + + pub fn to_path_buf(&self) -> PathBuf { + self.0.to_path_buf() + } + pub fn to_glob_string(&self) -> String { #[cfg(target_os = "windows")] { @@ -124,13 +190,11 @@ impl SanitizedPath { self.0.to_string_lossy().to_string() } } +} - pub fn join(&self, path: &Self) -> Self { - self.0.join(&path.0).into() - } - - pub fn strip_prefix(&self, base: &Self) -> Result<&Path, StripPrefixError> { - self.0.strip_prefix(base.as_path()) +impl std::fmt::Debug for SanitizedPath { + fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(&self.0, formatter) } } @@ -140,29 +204,23 @@ impl Display for SanitizedPath { } } -impl From for Arc { - fn from(sanitized_path: SanitizedPath) -> Self { - sanitized_path.0 +impl From<&SanitizedPath> for Arc { + fn from(sanitized_path: &SanitizedPath) -> Self { + let path: Arc = sanitized_path.0.into(); + // safe because `Path` and `SanitizedPath` have the same repr and Drop impl + unsafe { mem::transmute(path) } } } -impl From for PathBuf { - fn from(sanitized_path: SanitizedPath) -> Self { - sanitized_path.0.as_ref().into() +impl From<&SanitizedPath> for PathBuf { + fn from(sanitized_path: &SanitizedPath) -> Self { + sanitized_path.as_path().into() } } -impl> From for SanitizedPath { - #[cfg(not(target_os = "windows"))] - fn from(path: T) -> Self { - let path = path.as_ref(); - SanitizedPath(path.into()) - } - - #[cfg(target_os = "windows")] - fn from(path: T) -> Self { - let path = path.as_ref(); - SanitizedPath(dunce::simplified(path).into()) +impl AsRef for SanitizedPath { + fn as_ref(&self) -> &Path { + &self.0 } } @@ -1195,14 +1253,14 @@ mod tests { #[cfg(target_os = "windows")] fn test_sanitized_path() { let path = Path::new("C:\\Users\\someone\\test_file.rs"); - let sanitized_path = SanitizedPath::from(path); + let sanitized_path = SanitizedPath::new(path); assert_eq!( sanitized_path.to_string(), "C:\\Users\\someone\\test_file.rs" ); let path = Path::new("\\\\?\\C:\\Users\\someone\\test_file.rs"); - let sanitized_path = SanitizedPath::from(path); + let sanitized_path = SanitizedPath::new(path); assert_eq!( sanitized_path.to_string(), "C:\\Users\\someone\\test_file.rs" diff --git a/crates/workspace/src/path_list.rs b/crates/workspace/src/path_list.rs index cf463e6b2295f78a8492c0f4aafd6ca6b8ed1788..01e2ffda949faf502de087fb0077cdbc758001ab 100644 --- a/crates/workspace/src/path_list.rs +++ b/crates/workspace/src/path_list.rs @@ -26,7 +26,7 @@ impl PathList { let mut indexed_paths: Vec<(usize, PathBuf)> = paths .iter() .enumerate() - .map(|(ix, path)| (ix, SanitizedPath::from(path).into())) + .map(|(ix, path)| (ix, SanitizedPath::new(path).into())) .collect(); indexed_paths.sort_by(|(_, a), (_, b)| a.cmp(b)); let order = indexed_paths.iter().map(|e| e.0).collect::>().into(); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 76693e716e3cafe31e60eeecfcaeeb5bb267fb77..0f119c14003d0f54f2f3a5323cb5e9106716a24d 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -2576,7 +2576,7 @@ impl Workspace { }; let this = this.clone(); - let abs_path: Arc = SanitizedPath::from(abs_path.clone()).into(); + let abs_path: Arc = SanitizedPath::new(&abs_path).as_path().into(); let fs = fs.clone(); let pane = pane.clone(); let task = cx.spawn(async move |cx| { diff --git a/crates/worktree/src/worktree.rs b/crates/worktree/src/worktree.rs index 845af538021326a0e609c9f6098ebf20ed1dc704..711c99ce28bbbc557a293d8b644ac6594f31ad7f 100644 --- a/crates/worktree/src/worktree.rs +++ b/crates/worktree/src/worktree.rs @@ -158,7 +158,7 @@ pub struct RemoteWorktree { #[derive(Clone)] pub struct Snapshot { id: WorktreeId, - abs_path: SanitizedPath, + abs_path: Arc, root_name: String, root_char_bag: CharBag, entries_by_path: SumTree, @@ -457,7 +457,7 @@ enum ScanState { scanning: bool, }, RootUpdated { - new_path: Option, + new_path: Option>, }, } @@ -763,8 +763,8 @@ impl Worktree { pub fn abs_path(&self) -> Arc { match self { - Worktree::Local(worktree) => worktree.abs_path.clone().into(), - Worktree::Remote(worktree) => worktree.abs_path.clone().into(), + Worktree::Local(worktree) => SanitizedPath::cast_arc(worktree.abs_path.clone()), + Worktree::Remote(worktree) => SanitizedPath::cast_arc(worktree.abs_path.clone()), } } @@ -1813,7 +1813,7 @@ impl LocalWorktree { // Otherwise, the FS watcher would do it on the `RootUpdated` event, // but with a noticeable delay, so we handle it proactively. local.update_abs_path_and_refresh( - Some(SanitizedPath::from(abs_path.clone())), + Some(SanitizedPath::new_arc(&abs_path)), cx, ); Task::ready(Ok(this.root_entry().cloned())) @@ -2090,7 +2090,7 @@ impl LocalWorktree { fn update_abs_path_and_refresh( &mut self, - new_path: Option, + new_path: Option>, cx: &Context, ) { if let Some(new_path) = new_path { @@ -2340,7 +2340,7 @@ impl Snapshot { pub fn new(id: u64, root_name: String, abs_path: Arc) -> Self { Snapshot { id: WorktreeId::from_usize(id as usize), - abs_path: abs_path.into(), + abs_path: SanitizedPath::from_arc(abs_path), root_char_bag: root_name.chars().map(|c| c.to_ascii_lowercase()).collect(), root_name, always_included_entries: Default::default(), @@ -2368,7 +2368,7 @@ impl Snapshot { // // This is definitely a bug, but it's not clear if we should handle it here or not. pub fn abs_path(&self) -> &Arc { - self.abs_path.as_path() + SanitizedPath::cast_arc_ref(&self.abs_path) } fn build_initial_update(&self, project_id: u64, worktree_id: u64) -> proto::UpdateWorktree { @@ -2464,7 +2464,7 @@ impl Snapshot { Some(removed_entry.path) } - fn update_abs_path(&mut self, abs_path: SanitizedPath, root_name: String) { + fn update_abs_path(&mut self, abs_path: Arc, root_name: String) { self.abs_path = abs_path; if root_name != self.root_name { self.root_char_bag = root_name.chars().map(|c| c.to_ascii_lowercase()).collect(); @@ -2483,7 +2483,7 @@ impl Snapshot { update.removed_entries.len() ); self.update_abs_path( - SanitizedPath::from(PathBuf::from_proto(update.abs_path)), + SanitizedPath::new_arc(&PathBuf::from_proto(update.abs_path)), update.root_name, ); @@ -3849,7 +3849,11 @@ impl BackgroundScanner { root_entry.is_ignored = true; state.insert_entry(root_entry.clone(), self.fs.as_ref(), self.watcher.as_ref()); } - state.enqueue_scan_dir(root_abs_path.into(), &root_entry, &scan_job_tx); + state.enqueue_scan_dir( + SanitizedPath::cast_arc(root_abs_path), + &root_entry, + &scan_job_tx, + ); } }; @@ -3930,8 +3934,9 @@ impl BackgroundScanner { self.forcibly_load_paths(&request.relative_paths).await; let root_path = self.state.lock().snapshot.abs_path.clone(); - let root_canonical_path = match self.fs.canonicalize(root_path.as_path()).await { - Ok(path) => SanitizedPath::from(path), + let root_canonical_path = self.fs.canonicalize(root_path.as_path()).await; + let root_canonical_path = match &root_canonical_path { + Ok(path) => SanitizedPath::new(path), Err(err) => { log::error!("failed to canonicalize root path {root_path:?}: {err}"); return true; @@ -3959,8 +3964,8 @@ impl BackgroundScanner { } self.reload_entries_for_paths( - root_path, - root_canonical_path, + &root_path, + &root_canonical_path, &request.relative_paths, abs_paths, None, @@ -3972,8 +3977,9 @@ impl BackgroundScanner { async fn process_events(&self, mut abs_paths: Vec) { let root_path = self.state.lock().snapshot.abs_path.clone(); - let root_canonical_path = match self.fs.canonicalize(root_path.as_path()).await { - Ok(path) => SanitizedPath::from(path), + let root_canonical_path = self.fs.canonicalize(root_path.as_path()).await; + let root_canonical_path = match &root_canonical_path { + Ok(path) => SanitizedPath::new(path), Err(err) => { let new_path = self .state @@ -3982,7 +3988,7 @@ impl BackgroundScanner { .root_file_handle .clone() .and_then(|handle| handle.current_path(&self.fs).log_err()) - .map(SanitizedPath::from) + .map(|path| SanitizedPath::new_arc(&path)) .filter(|new_path| *new_path != root_path); if let Some(new_path) = new_path.as_ref() { @@ -4011,7 +4017,7 @@ impl BackgroundScanner { abs_paths.sort_unstable(); abs_paths.dedup_by(|a, b| a.starts_with(b)); abs_paths.retain(|abs_path| { - let abs_path = SanitizedPath::from(abs_path); + let abs_path = &SanitizedPath::new(abs_path); let snapshot = &self.state.lock().snapshot; { @@ -4054,7 +4060,7 @@ impl BackgroundScanner { return false; }; - if abs_path.0.file_name() == Some(*GITIGNORE) { + if abs_path.file_name() == Some(*GITIGNORE) { for (_, repo) in snapshot.git_repositories.iter().filter(|(_, repo)| repo.directory_contains(&relative_path)) { if !dot_git_abs_paths.iter().any(|dot_git_abs_path| dot_git_abs_path == repo.common_dir_abs_path.as_ref()) { dot_git_abs_paths.push(repo.common_dir_abs_path.to_path_buf()); @@ -4093,8 +4099,8 @@ impl BackgroundScanner { let (scan_job_tx, scan_job_rx) = channel::unbounded(); log::debug!("received fs events {:?}", relative_paths); self.reload_entries_for_paths( - root_path, - root_canonical_path, + &root_path, + &root_canonical_path, &relative_paths, abs_paths, Some(scan_job_tx.clone()), @@ -4441,8 +4447,8 @@ impl BackgroundScanner { /// All list arguments should be sorted before calling this function async fn reload_entries_for_paths( &self, - root_abs_path: SanitizedPath, - root_canonical_path: SanitizedPath, + root_abs_path: &SanitizedPath, + root_canonical_path: &SanitizedPath, relative_paths: &[Arc], abs_paths: Vec, scan_queue_tx: Option>, @@ -4470,7 +4476,7 @@ impl BackgroundScanner { } } - anyhow::Ok(Some((metadata, SanitizedPath::from(canonical_path)))) + anyhow::Ok(Some((metadata, SanitizedPath::new_arc(&canonical_path)))) } else { Ok(None) }