Eagerly populate child entries when copying a directory via RPC

This commit is contained in:
Antonio Scandurra 2022-05-30 18:01:46 +02:00
parent 51adc6517e
commit 88fdd8606a
4 changed files with 98 additions and 36 deletions

View file

@ -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());
paths.extend(
self.copy(&child_source_path, &child_target_path, options) self.copy(&child_source_path, &child_target_path, options)
.await?; .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<()> {

View file

@ -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,
}) })
} }

View file

@ -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,
let mut child_entries = Vec::new();
for copied_path in copied_paths {
if copied_path != abs_new_path {
let relative_copied_path = copied_path.strip_prefix(this.abs_path())?;
child_entries.push(this.refresh_entry(
relative_copied_path.into(),
copied_path,
None, None,
cx, cx,
) ));
}) }
.await?; }
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

View file

@ -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 {