fs: Bring back copy paste again (#25543)
Closes #25317 cc @0xtimsb Release Notes: - N/A
This commit is contained in:
parent
8e891c16f8
commit
8e1003ef59
3 changed files with 189 additions and 15 deletions
|
@ -1337,7 +1337,10 @@ impl FakeFs {
|
||||||
pub fn paths(&self, include_dot_git: bool) -> Vec<PathBuf> {
|
pub fn paths(&self, include_dot_git: bool) -> Vec<PathBuf> {
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
let mut queue = collections::VecDeque::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() {
|
while let Some((path, entry)) = queue.pop_front() {
|
||||||
if let FakeFsEntry::Dir { entries, .. } = &*entry.lock() {
|
if let FakeFsEntry::Dir { entries, .. } = &*entry.lock() {
|
||||||
for (name, entry) in entries {
|
for (name, entry) in entries {
|
||||||
|
@ -1358,7 +1361,10 @@ impl FakeFs {
|
||||||
pub fn directories(&self, include_dot_git: bool) -> Vec<PathBuf> {
|
pub fn directories(&self, include_dot_git: bool) -> Vec<PathBuf> {
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
let mut queue = collections::VecDeque::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() {
|
while let Some((path, entry)) = queue.pop_front() {
|
||||||
if let FakeFsEntry::Dir { entries, .. } = &*entry.lock() {
|
if let FakeFsEntry::Dir { entries, .. } = &*entry.lock() {
|
||||||
for (name, entry) in entries {
|
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 {
|
let Ok(item_relative_path) = item.strip_prefix(source) else {
|
||||||
continue;
|
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 is_dir {
|
||||||
if !options.overwrite && fs.metadata(&target_item).await.is_ok_and(|m| m.is_some()) {
|
if !options.overwrite && fs.metadata(&target_item).await.is_ok_and(|m| m.is_some()) {
|
||||||
if options.ignore_if_exists {
|
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]
|
#[gpui::test]
|
||||||
async fn test_copy_recursive(executor: BackgroundExecutor) {
|
async fn test_copy_recursive(executor: BackgroundExecutor) {
|
||||||
let fs = FakeFs::new(executor.clone());
|
let fs = FakeFs::new(executor.clone());
|
||||||
|
@ -2185,7 +2331,8 @@ mod tests {
|
||||||
"b": "B",
|
"b": "B",
|
||||||
"inner3": {
|
"inner3": {
|
||||||
"d": "D",
|
"d": "D",
|
||||||
}
|
},
|
||||||
|
"inner4": {}
|
||||||
},
|
},
|
||||||
"inner2": {
|
"inner2": {
|
||||||
"c": "C",
|
"c": "C",
|
||||||
|
@ -2203,6 +2350,17 @@ mod tests {
|
||||||
PathBuf::from(path!("/outer/inner1/inner3/d")),
|
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 source = Path::new(path!("/outer"));
|
||||||
let target = Path::new(path!("/outer/inner1/outer"));
|
let target = Path::new(path!("/outer/inner1/outer"));
|
||||||
|
@ -2223,6 +2381,22 @@ mod tests {
|
||||||
PathBuf::from(path!("/outer/inner1/outer/inner1/inner3/d")),
|
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]
|
#[gpui::test]
|
||||||
|
|
|
@ -4958,14 +4958,14 @@ async fn test_create_entry(cx: &mut gpui::TestAppContext) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
fs.paths(true),
|
fs.paths(true),
|
||||||
vec![
|
vec![
|
||||||
PathBuf::from("/"),
|
PathBuf::from(path!("/")),
|
||||||
PathBuf::from("/one"),
|
PathBuf::from(path!("/one")),
|
||||||
PathBuf::from("/one/two"),
|
PathBuf::from(path!("/one/two")),
|
||||||
PathBuf::from("/one/two/c.rs"),
|
PathBuf::from(path!("/one/two/c.rs")),
|
||||||
PathBuf::from("/one/two/three"),
|
PathBuf::from(path!("/one/two/three")),
|
||||||
PathBuf::from("/one/two/three/a.txt"),
|
PathBuf::from(path!("/one/two/three/a.txt")),
|
||||||
PathBuf::from("/one/two/three/b.."),
|
PathBuf::from(path!("/one/two/three/b..")),
|
||||||
PathBuf::from("/one/two/three/four"),
|
PathBuf::from(path!("/one/two/three/four")),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use util::{test::TempTree, ResultExt};
|
use util::{path, test::TempTree, ResultExt};
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_traversal(cx: &mut TestAppContext) {
|
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())
|
.map(|o| o.parse().unwrap())
|
||||||
.unwrap_or(20);
|
.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<dyn Fs>;
|
let fs = FakeFs::new(cx.background_executor.clone()) as Arc<dyn Fs>;
|
||||||
fs.as_fake().insert_tree(root_dir, json!({})).await;
|
fs.as_fake().insert_tree(root_dir, json!({})).await;
|
||||||
for _ in 0..initial_entries {
|
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())
|
.map(|o| o.parse().unwrap())
|
||||||
.unwrap_or(20);
|
.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<dyn Fs>;
|
let fs = FakeFs::new(cx.background_executor.clone()) as Arc<dyn Fs>;
|
||||||
fs.as_fake().insert_tree(root_dir, json!({})).await;
|
fs.as_fake().insert_tree(root_dir, json!({})).await;
|
||||||
for _ in 0..initial_entries {
|
for _ in 0..initial_entries {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue