From 8e1003ef59aafe0cee189f38f256b507cd18f9a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=B0=8F=E7=99=BD?= <364772080@qq.com> Date: Tue, 25 Feb 2025 17:36:07 +0800 Subject: [PATCH] fs: Bring back copy paste again (#25543) Closes #25317 cc @0xtimsb Release Notes: - N/A --- crates/fs/src/fs.rs | 182 +++++++++++++++++++++++++- crates/project/src/project_tests.rs | 16 +-- crates/worktree/src/worktree_tests.rs | 6 +- 3 files changed, 189 insertions(+), 15 deletions(-) diff --git a/crates/fs/src/fs.rs b/crates/fs/src/fs.rs index 628c216ccd9209117fb982383b3632f52b7dbcc2..35ffad95118566d503200776363b310cfed692de 100644 --- a/crates/fs/src/fs.rs +++ b/crates/fs/src/fs.rs @@ -1337,7 +1337,10 @@ impl FakeFs { pub fn paths(&self, include_dot_git: bool) -> Vec { let mut result = Vec::new(); let mut queue = collections::VecDeque::new(); - queue.push_back((PathBuf::from("/"), self.state.lock().root.clone())); + queue.push_back(( + PathBuf::from(util::path!("/")), + self.state.lock().root.clone(), + )); while let Some((path, entry)) = queue.pop_front() { if let FakeFsEntry::Dir { entries, .. } = &*entry.lock() { for (name, entry) in entries { @@ -1358,7 +1361,10 @@ impl FakeFs { pub fn directories(&self, include_dot_git: bool) -> Vec { let mut result = Vec::new(); let mut queue = collections::VecDeque::new(); - queue.push_back((PathBuf::from("/"), self.state.lock().root.clone())); + queue.push_back(( + PathBuf::from(util::path!("/")), + self.state.lock().root.clone(), + )); while let Some((path, entry)) = queue.pop_front() { if let FakeFsEntry::Dir { entries, .. } = &*entry.lock() { for (name, entry) in entries { @@ -2020,7 +2026,11 @@ pub async fn copy_recursive<'a>( let Ok(item_relative_path) = item.strip_prefix(source) else { continue; }; - let target_item = target.join(item_relative_path); + let target_item = if item_relative_path == Path::new("") { + target.to_path_buf() + } else { + target.join(item_relative_path) + }; if is_dir { if !options.overwrite && fs.metadata(&target_item).await.is_ok_and(|m| m.is_some()) { if options.ignore_if_exists { @@ -2174,6 +2184,142 @@ mod tests { ); } + #[gpui::test] + async fn test_copy_recursive_with_single_file(executor: BackgroundExecutor) { + let fs = FakeFs::new(executor.clone()); + fs.insert_tree( + path!("/outer"), + json!({ + "a": "A", + "b": "B", + "inner": {} + }), + ) + .await; + + assert_eq!( + fs.files(), + vec![ + PathBuf::from(path!("/outer/a")), + PathBuf::from(path!("/outer/b")), + ] + ); + + let source = Path::new(path!("/outer/a")); + let target = Path::new(path!("/outer/a copy")); + copy_recursive(fs.as_ref(), source, target, Default::default()) + .await + .unwrap(); + + assert_eq!( + fs.files(), + vec![ + PathBuf::from(path!("/outer/a")), + PathBuf::from(path!("/outer/a copy")), + PathBuf::from(path!("/outer/b")), + ] + ); + + let source = Path::new(path!("/outer/a")); + let target = Path::new(path!("/outer/inner/a copy")); + copy_recursive(fs.as_ref(), source, target, Default::default()) + .await + .unwrap(); + + assert_eq!( + fs.files(), + vec![ + PathBuf::from(path!("/outer/a")), + PathBuf::from(path!("/outer/a copy")), + PathBuf::from(path!("/outer/b")), + PathBuf::from(path!("/outer/inner/a copy")), + ] + ); + } + + #[gpui::test] + async fn test_copy_recursive_with_single_dir(executor: BackgroundExecutor) { + let fs = FakeFs::new(executor.clone()); + fs.insert_tree( + path!("/outer"), + json!({ + "a": "A", + "empty": {}, + "non-empty": { + "b": "B", + } + }), + ) + .await; + + assert_eq!( + fs.files(), + vec![ + PathBuf::from(path!("/outer/a")), + PathBuf::from(path!("/outer/non-empty/b")), + ] + ); + assert_eq!( + fs.directories(false), + vec![ + PathBuf::from(path!("/")), + PathBuf::from(path!("/outer")), + PathBuf::from(path!("/outer/empty")), + PathBuf::from(path!("/outer/non-empty")), + ] + ); + + let source = Path::new(path!("/outer/empty")); + let target = Path::new(path!("/outer/empty copy")); + copy_recursive(fs.as_ref(), source, target, Default::default()) + .await + .unwrap(); + + assert_eq!( + fs.files(), + vec![ + PathBuf::from(path!("/outer/a")), + PathBuf::from(path!("/outer/non-empty/b")), + ] + ); + assert_eq!( + fs.directories(false), + vec![ + PathBuf::from(path!("/")), + PathBuf::from(path!("/outer")), + PathBuf::from(path!("/outer/empty")), + PathBuf::from(path!("/outer/empty copy")), + PathBuf::from(path!("/outer/non-empty")), + ] + ); + + let source = Path::new(path!("/outer/non-empty")); + let target = Path::new(path!("/outer/non-empty copy")); + copy_recursive(fs.as_ref(), source, target, Default::default()) + .await + .unwrap(); + + assert_eq!( + fs.files(), + vec![ + PathBuf::from(path!("/outer/a")), + PathBuf::from(path!("/outer/non-empty/b")), + PathBuf::from(path!("/outer/non-empty copy/b")), + ] + ); + assert_eq!( + fs.directories(false), + vec![ + PathBuf::from(path!("/")), + PathBuf::from(path!("/outer")), + PathBuf::from(path!("/outer/empty")), + PathBuf::from(path!("/outer/empty copy")), + PathBuf::from(path!("/outer/non-empty")), + PathBuf::from(path!("/outer/non-empty copy")), + ] + ); + } + #[gpui::test] async fn test_copy_recursive(executor: BackgroundExecutor) { let fs = FakeFs::new(executor.clone()); @@ -2185,7 +2331,8 @@ mod tests { "b": "B", "inner3": { "d": "D", - } + }, + "inner4": {} }, "inner2": { "c": "C", @@ -2203,6 +2350,17 @@ mod tests { PathBuf::from(path!("/outer/inner1/inner3/d")), ] ); + assert_eq!( + fs.directories(false), + vec![ + PathBuf::from(path!("/")), + PathBuf::from(path!("/outer")), + PathBuf::from(path!("/outer/inner1")), + PathBuf::from(path!("/outer/inner2")), + PathBuf::from(path!("/outer/inner1/inner3")), + PathBuf::from(path!("/outer/inner1/inner4")), + ] + ); let source = Path::new(path!("/outer")); let target = Path::new(path!("/outer/inner1/outer")); @@ -2223,6 +2381,22 @@ mod tests { PathBuf::from(path!("/outer/inner1/outer/inner1/inner3/d")), ] ); + assert_eq!( + fs.directories(false), + vec![ + PathBuf::from(path!("/")), + PathBuf::from(path!("/outer")), + PathBuf::from(path!("/outer/inner1")), + PathBuf::from(path!("/outer/inner2")), + PathBuf::from(path!("/outer/inner1/inner3")), + PathBuf::from(path!("/outer/inner1/inner4")), + PathBuf::from(path!("/outer/inner1/outer")), + PathBuf::from(path!("/outer/inner1/outer/inner1")), + PathBuf::from(path!("/outer/inner1/outer/inner2")), + PathBuf::from(path!("/outer/inner1/outer/inner1/inner3")), + PathBuf::from(path!("/outer/inner1/outer/inner1/inner4")), + ] + ); } #[gpui::test] diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 150fc9fa56c3daf59c6929f75f4cd97c12897c11..c6e51f930ae88fc91f79ad0d3ebe4a04436b7614 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -4958,14 +4958,14 @@ async fn test_create_entry(cx: &mut gpui::TestAppContext) { assert_eq!( fs.paths(true), vec![ - PathBuf::from("/"), - PathBuf::from("/one"), - PathBuf::from("/one/two"), - PathBuf::from("/one/two/c.rs"), - PathBuf::from("/one/two/three"), - PathBuf::from("/one/two/three/a.txt"), - PathBuf::from("/one/two/three/b.."), - PathBuf::from("/one/two/three/four"), + PathBuf::from(path!("/")), + PathBuf::from(path!("/one")), + PathBuf::from(path!("/one/two")), + PathBuf::from(path!("/one/two/c.rs")), + PathBuf::from(path!("/one/two/three")), + PathBuf::from(path!("/one/two/three/a.txt")), + PathBuf::from(path!("/one/two/three/b..")), + PathBuf::from(path!("/one/two/three/four")), ] ); diff --git a/crates/worktree/src/worktree_tests.rs b/crates/worktree/src/worktree_tests.rs index 405c1b752bb67fcc26db89dd6dbdc73068d530a7..cd2c2e051d5b69887937f1a85cfc86fe501a27c3 100644 --- a/crates/worktree/src/worktree_tests.rs +++ b/crates/worktree/src/worktree_tests.rs @@ -26,7 +26,7 @@ use std::{ sync::Arc, time::Duration, }; -use util::{test::TempTree, ResultExt}; +use util::{path, test::TempTree, ResultExt}; #[gpui::test] async fn test_traversal(cx: &mut TestAppContext) { @@ -1650,7 +1650,7 @@ async fn test_random_worktree_operations_during_initial_scan( .map(|o| o.parse().unwrap()) .unwrap_or(20); - let root_dir = Path::new("/test"); + let root_dir = Path::new(path!("/test")); let fs = FakeFs::new(cx.background_executor.clone()) as Arc; fs.as_fake().insert_tree(root_dir, json!({})).await; for _ in 0..initial_entries { @@ -1741,7 +1741,7 @@ async fn test_random_worktree_changes(cx: &mut TestAppContext, mut rng: StdRng) .map(|o| o.parse().unwrap()) .unwrap_or(20); - let root_dir = Path::new("/test"); + let root_dir = Path::new(path!("/test")); let fs = FakeFs::new(cx.background_executor.clone()) as Arc; fs.as_fake().insert_tree(root_dir, json!({})).await; for _ in 0..initial_entries {