From 2dffc5f6e1526a4494f34bfaca8c738998712d6b Mon Sep 17 00:00:00 2001 From: Thorsten Ball Date: Thu, 15 Feb 2024 19:07:10 +0100 Subject: [PATCH] Fix case-only renaming of files in project panel (#7835) This is a follow-up to #7768 but now also fixes #5211. Explanation is relatively simple: case-only renames previously failed because while Zed would think that `foobar` and `FOOBAR` are different, the filesystem would give us an file-already-exists error when renaming. So what we're doing here is to check whether we're on a case-insensitive filesystem and if so, we overwrite the old file. Release Notes: - Fixed case-only renaming of files in project panel. ([#5211](https://github.com/zed-industries/zed/issues/5211)). Proof: https://github.com/zed-industries/zed/assets/1185253/57d5063f-09d9-47b1-a2df-3d7edefca97d --- crates/project/src/worktree.rs | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 054a5b9933..de767bea0e 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -93,6 +93,7 @@ pub struct LocalWorktree { diagnostic_summaries: HashMap, HashMap>, client: Arc, fs: Arc, + fs_case_sensitive: bool, visible: bool, } @@ -314,6 +315,13 @@ impl Worktree { .await .context("failed to stat worktree path")?; + let fs_case_sensitive = fs.is_case_sensitive().await.unwrap_or_else(|e| { + log::error!( + "Failed to determine whether filesystem is case sensitive (falling back to true) due to error: {e:#}" + ); + true + }); + let closure_fs = Arc::clone(&fs); let closure_next_entry_id = Arc::clone(&next_entry_id); let closure_abs_path = abs_path.to_path_buf(); @@ -435,6 +443,7 @@ impl Worktree { diagnostic_summaries: Default::default(), client, fs, + fs_case_sensitive, visible, }) }) @@ -1301,9 +1310,29 @@ impl LocalWorktree { let abs_old_path = self.absolutize(&old_path); let abs_new_path = self.absolutize(&new_path); let fs = self.fs.clone(); + let case_sensitive = self.fs_case_sensitive; let rename = cx.background_executor().spawn(async move { - fs.rename(&abs_old_path?, &abs_new_path?, Default::default()) - .await + let abs_old_path = abs_old_path?; + let abs_new_path = abs_new_path?; + + let abs_old_path_lower = abs_old_path.to_str().map(|p| p.to_lowercase()); + let abs_new_path_lower = abs_new_path.to_str().map(|p| p.to_lowercase()); + + // If we're on a case-insensitive FS and we're doing a case-only rename (i.e. `foobar` to `FOOBAR`) + // we want to overwrite, because otherwise we run into a file-already-exists error. + let overwrite = !case_sensitive + && abs_old_path != abs_new_path + && abs_old_path_lower == abs_new_path_lower; + + fs.rename( + &abs_old_path, + &abs_new_path, + fs::RenameOptions { + overwrite, + ..Default::default() + }, + ) + .await }); cx.spawn(|this, mut cx| async move {