Eagerly populate child entries when copying a directory via RPC
This commit is contained in:
parent
51adc6517e
commit
88fdd8606a
4 changed files with 98 additions and 36 deletions
|
@ -15,7 +15,12 @@ use text::Rope;
|
||||||
pub trait Fs: Send + Sync {
|
pub trait Fs: Send + Sync {
|
||||||
async fn create_dir(&self, path: &Path) -> Result<()>;
|
async fn create_dir(&self, path: &Path) -> Result<()>;
|
||||||
async fn create_file(&self, path: &Path, options: CreateOptions) -> Result<()>;
|
async fn create_file(&self, path: &Path, options: CreateOptions) -> Result<()>;
|
||||||
async fn copy(&self, source: &Path, target: &Path, options: CopyOptions) -> Result<()>;
|
async fn copy(
|
||||||
|
&self,
|
||||||
|
source: &Path,
|
||||||
|
target: &Path,
|
||||||
|
options: CopyOptions,
|
||||||
|
) -> Result<Vec<PathBuf>>;
|
||||||
async fn rename(&self, source: &Path, target: &Path, options: RenameOptions) -> Result<()>;
|
async fn rename(&self, source: &Path, target: &Path, options: RenameOptions) -> Result<()>;
|
||||||
async fn remove_dir(&self, path: &Path, options: RemoveOptions) -> Result<()>;
|
async fn remove_dir(&self, path: &Path, options: RemoveOptions) -> Result<()>;
|
||||||
async fn remove_file(&self, path: &Path, options: RemoveOptions) -> Result<()>;
|
async fn remove_file(&self, path: &Path, options: RemoveOptions) -> Result<()>;
|
||||||
|
@ -91,15 +96,21 @@ impl Fs for RealFs {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn copy(&self, source: &Path, target: &Path, options: CopyOptions) -> Result<()> {
|
async fn copy(
|
||||||
|
&self,
|
||||||
|
source: &Path,
|
||||||
|
target: &Path,
|
||||||
|
options: CopyOptions,
|
||||||
|
) -> Result<Vec<PathBuf>> {
|
||||||
if !options.overwrite && smol::fs::metadata(target).await.is_ok() {
|
if !options.overwrite && smol::fs::metadata(target).await.is_ok() {
|
||||||
if options.ignore_if_exists {
|
if options.ignore_if_exists {
|
||||||
return Ok(());
|
return Ok(Default::default());
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow!("{target:?} already exists"));
|
return Err(anyhow!("{target:?} already exists"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut paths = vec![target.to_path_buf()];
|
||||||
let metadata = smol::fs::metadata(source).await?;
|
let metadata = smol::fs::metadata(source).await?;
|
||||||
let _ = smol::fs::remove_dir_all(target).await;
|
let _ = smol::fs::remove_dir_all(target).await;
|
||||||
if metadata.is_dir() {
|
if metadata.is_dir() {
|
||||||
|
@ -109,15 +120,17 @@ impl Fs for RealFs {
|
||||||
if let Ok(child) = child {
|
if let Ok(child) = child {
|
||||||
let child_source_path = child.path();
|
let child_source_path = child.path();
|
||||||
let child_target_path = target.join(child.file_name());
|
let child_target_path = target.join(child.file_name());
|
||||||
self.copy(&child_source_path, &child_target_path, options)
|
paths.extend(
|
||||||
.await?;
|
self.copy(&child_source_path, &child_target_path, options)
|
||||||
|
.await?,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
smol::fs::copy(source, target).await?;
|
smol::fs::copy(source, target).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(paths)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn rename(&self, source: &Path, target: &Path, options: RenameOptions) -> Result<()> {
|
async fn rename(&self, source: &Path, target: &Path, options: RenameOptions) -> Result<()> {
|
||||||
|
@ -547,7 +560,12 @@ impl Fs for FakeFs {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn copy(&self, source: &Path, target: &Path, options: CopyOptions) -> Result<()> {
|
async fn copy(
|
||||||
|
&self,
|
||||||
|
source: &Path,
|
||||||
|
target: &Path,
|
||||||
|
options: CopyOptions,
|
||||||
|
) -> Result<Vec<PathBuf>> {
|
||||||
let source = normalize_path(source);
|
let source = normalize_path(source);
|
||||||
let target = normalize_path(target);
|
let target = normalize_path(target);
|
||||||
|
|
||||||
|
@ -557,7 +575,7 @@ impl Fs for FakeFs {
|
||||||
|
|
||||||
if !options.overwrite && state.entries.contains_key(&target) {
|
if !options.overwrite && state.entries.contains_key(&target) {
|
||||||
if options.ignore_if_exists {
|
if options.ignore_if_exists {
|
||||||
return Ok(());
|
return Ok(Default::default());
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow!("{target:?} already exists"));
|
return Err(anyhow!("{target:?} already exists"));
|
||||||
}
|
}
|
||||||
|
@ -570,15 +588,15 @@ impl Fs for FakeFs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut events = Vec::new();
|
let mut paths = Vec::new();
|
||||||
for (relative_path, entry) in new_entries {
|
for (relative_path, entry) in new_entries {
|
||||||
let new_path = normalize_path(&target.join(relative_path));
|
let new_path = normalize_path(&target.join(relative_path));
|
||||||
events.push(new_path.clone());
|
paths.push(new_path.clone());
|
||||||
state.entries.insert(new_path, entry);
|
state.entries.insert(new_path, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.emit_event(&events).await;
|
state.emit_event(&paths).await;
|
||||||
Ok(())
|
Ok(paths)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn remove_dir(&self, dir_path: &Path, options: RemoveOptions) -> Result<()> {
|
async fn remove_dir(&self, dir_path: &Path, options: RemoveOptions) -> Result<()> {
|
||||||
|
|
|
@ -784,7 +784,7 @@ impl Project {
|
||||||
entry_id: ProjectEntryId,
|
entry_id: ProjectEntryId,
|
||||||
new_path: impl Into<Arc<Path>>,
|
new_path: impl Into<Arc<Path>>,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Option<Task<Result<Entry>>> {
|
) -> Option<Task<Result<(Entry, Vec<Entry>)>>> {
|
||||||
let worktree = self.worktree_for_entry(entry_id, cx)?;
|
let worktree = self.worktree_for_entry(entry_id, cx)?;
|
||||||
let new_path = new_path.into();
|
let new_path = new_path.into();
|
||||||
if self.is_local() {
|
if self.is_local() {
|
||||||
|
@ -809,15 +809,24 @@ impl Project {
|
||||||
let entry = response
|
let entry = response
|
||||||
.entry
|
.entry
|
||||||
.ok_or_else(|| anyhow!("missing entry in response"))?;
|
.ok_or_else(|| anyhow!("missing entry in response"))?;
|
||||||
worktree
|
let (entry, child_entries) = worktree.update(&mut cx, |worktree, cx| {
|
||||||
.update(&mut cx, |worktree, cx| {
|
let worktree = worktree.as_remote().unwrap();
|
||||||
worktree.as_remote().unwrap().insert_entry(
|
let root_entry =
|
||||||
|
worktree.insert_entry(entry, response.worktree_scan_id as usize, cx);
|
||||||
|
let mut child_entries = Vec::new();
|
||||||
|
for entry in response.child_entries {
|
||||||
|
child_entries.push(worktree.insert_entry(
|
||||||
entry,
|
entry,
|
||||||
response.worktree_scan_id as usize,
|
response.worktree_scan_id as usize,
|
||||||
cx,
|
cx,
|
||||||
)
|
));
|
||||||
})
|
}
|
||||||
.await
|
(root_entry, child_entries)
|
||||||
|
});
|
||||||
|
Ok((
|
||||||
|
entry.await?,
|
||||||
|
futures::future::try_join_all(child_entries).await?,
|
||||||
|
))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4039,6 +4048,7 @@ impl Project {
|
||||||
.await?;
|
.await?;
|
||||||
Ok(proto::ProjectEntryResponse {
|
Ok(proto::ProjectEntryResponse {
|
||||||
entry: Some((&entry).into()),
|
entry: Some((&entry).into()),
|
||||||
|
child_entries: Default::default(),
|
||||||
worktree_scan_id: worktree_scan_id as u64,
|
worktree_scan_id: worktree_scan_id as u64,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -4067,6 +4077,7 @@ impl Project {
|
||||||
.await?;
|
.await?;
|
||||||
Ok(proto::ProjectEntryResponse {
|
Ok(proto::ProjectEntryResponse {
|
||||||
entry: Some((&entry).into()),
|
entry: Some((&entry).into()),
|
||||||
|
child_entries: Default::default(),
|
||||||
worktree_scan_id: worktree_scan_id as u64,
|
worktree_scan_id: worktree_scan_id as u64,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -4083,7 +4094,7 @@ impl Project {
|
||||||
.ok_or_else(|| anyhow!("worktree not found"))
|
.ok_or_else(|| anyhow!("worktree not found"))
|
||||||
})?;
|
})?;
|
||||||
let worktree_scan_id = worktree.read_with(&cx, |worktree, _| worktree.scan_id());
|
let worktree_scan_id = worktree.read_with(&cx, |worktree, _| worktree.scan_id());
|
||||||
let entry = worktree
|
let (entry, child_entries) = worktree
|
||||||
.update(&mut cx, |worktree, cx| {
|
.update(&mut cx, |worktree, cx| {
|
||||||
let new_path = PathBuf::from(OsString::from_vec(envelope.payload.new_path));
|
let new_path = PathBuf::from(OsString::from_vec(envelope.payload.new_path));
|
||||||
worktree
|
worktree
|
||||||
|
@ -4095,6 +4106,7 @@ impl Project {
|
||||||
.await?;
|
.await?;
|
||||||
Ok(proto::ProjectEntryResponse {
|
Ok(proto::ProjectEntryResponse {
|
||||||
entry: Some((&entry).into()),
|
entry: Some((&entry).into()),
|
||||||
|
child_entries: child_entries.iter().map(Into::into).collect(),
|
||||||
worktree_scan_id: worktree_scan_id as u64,
|
worktree_scan_id: worktree_scan_id as u64,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -4122,6 +4134,7 @@ impl Project {
|
||||||
.await?;
|
.await?;
|
||||||
Ok(proto::ProjectEntryResponse {
|
Ok(proto::ProjectEntryResponse {
|
||||||
entry: None,
|
entry: None,
|
||||||
|
child_entries: Default::default(),
|
||||||
worktree_scan_id: worktree_scan_id as u64,
|
worktree_scan_id: worktree_scan_id as u64,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -779,7 +779,7 @@ impl LocalWorktree {
|
||||||
entry_id: ProjectEntryId,
|
entry_id: ProjectEntryId,
|
||||||
new_path: impl Into<Arc<Path>>,
|
new_path: impl Into<Arc<Path>>,
|
||||||
cx: &mut ModelContext<Worktree>,
|
cx: &mut ModelContext<Worktree>,
|
||||||
) -> Option<Task<Result<Entry>>> {
|
) -> Option<Task<Result<(Entry, Vec<Entry>)>>> {
|
||||||
let old_path = self.entry_for_id(entry_id)?.path.clone();
|
let old_path = self.entry_for_id(entry_id)?.path.clone();
|
||||||
let new_path = new_path.into();
|
let new_path = new_path.into();
|
||||||
let abs_old_path = self.absolutize(&old_path);
|
let abs_old_path = self.absolutize(&old_path);
|
||||||
|
@ -794,23 +794,38 @@ impl LocalWorktree {
|
||||||
});
|
});
|
||||||
|
|
||||||
Some(cx.spawn(|this, mut cx| async move {
|
Some(cx.spawn(|this, mut cx| async move {
|
||||||
copy.await?;
|
let copied_paths = copy.await?;
|
||||||
let entry = this
|
let (entry, child_entries) = this.update(&mut cx, |this, cx| {
|
||||||
.update(&mut cx, |this, cx| {
|
let this = this.as_local_mut().unwrap();
|
||||||
this.as_local_mut().unwrap().refresh_entry(
|
let root_entry =
|
||||||
new_path.clone(),
|
this.refresh_entry(new_path.clone(), abs_new_path.clone(), None, cx);
|
||||||
abs_new_path,
|
|
||||||
None,
|
let mut child_entries = Vec::new();
|
||||||
cx,
|
for copied_path in copied_paths {
|
||||||
)
|
if copied_path != abs_new_path {
|
||||||
})
|
let relative_copied_path = copied_path.strip_prefix(this.abs_path())?;
|
||||||
.await?;
|
child_entries.push(this.refresh_entry(
|
||||||
|
relative_copied_path.into(),
|
||||||
|
copied_path,
|
||||||
|
None,
|
||||||
|
cx,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
anyhow::Ok((root_entry, child_entries))
|
||||||
|
})?;
|
||||||
|
let (entry, child_entries) = (
|
||||||
|
entry.await?,
|
||||||
|
futures::future::try_join_all(child_entries).await?,
|
||||||
|
);
|
||||||
|
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.poll_snapshot(cx);
|
this.poll_snapshot(cx);
|
||||||
this.as_local().unwrap().broadcast_snapshot()
|
this.as_local().unwrap().broadcast_snapshot()
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
Ok(entry)
|
Ok((entry, child_entries))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1202,8 +1217,23 @@ impl Snapshot {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_entry(&mut self, entry_id: ProjectEntryId) -> bool {
|
fn delete_entry(&mut self, entry_id: ProjectEntryId) -> bool {
|
||||||
if let Some(entry) = self.entries_by_id.remove(&entry_id, &()) {
|
if let Some(removed_entry) = self.entries_by_id.remove(&entry_id, &()) {
|
||||||
self.entries_by_path.remove(&PathKey(entry.path), &());
|
self.entries_by_path = {
|
||||||
|
let mut cursor = self.entries_by_path.cursor();
|
||||||
|
let mut new_entries_by_path =
|
||||||
|
cursor.slice(&TraversalTarget::Path(&removed_entry.path), Bias::Left, &());
|
||||||
|
while let Some(entry) = cursor.item() {
|
||||||
|
if entry.path.starts_with(&removed_entry.path) {
|
||||||
|
self.entries_by_id.remove(&entry.id, &());
|
||||||
|
cursor.next(&());
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new_entries_by_path.push_tree(cursor.suffix(&()), &());
|
||||||
|
new_entries_by_path
|
||||||
|
};
|
||||||
|
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
|
|
@ -224,7 +224,8 @@ message DeleteProjectEntry {
|
||||||
|
|
||||||
message ProjectEntryResponse {
|
message ProjectEntryResponse {
|
||||||
Entry entry = 1;
|
Entry entry = 1;
|
||||||
uint64 worktree_scan_id = 2;
|
repeated Entry child_entries = 2;
|
||||||
|
uint64 worktree_scan_id = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AddProjectCollaborator {
|
message AddProjectCollaborator {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue