From 117458f4f6c567ae691d4a1b716a6f5e8daef717 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 16 Nov 2022 14:58:11 +0100 Subject: [PATCH] Send worktree updates after project metadata has been sent --- crates/collab/src/db.rs | 3 +- crates/project/src/project.rs | 75 +++++++++++++++++++++---------- crates/workspace/src/workspace.rs | 5 ++- 3 files changed, 56 insertions(+), 27 deletions(-) diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index 785965905a..f058d3bfe1 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -1561,7 +1561,7 @@ where let query = format!( " DELETE FROM worktrees - WHERE project_id = ? AND worktree_id NOT IN ({params}) + WHERE project_id = ? AND id NOT IN ({params}) ", ); @@ -1580,6 +1580,7 @@ where WHERE project_id = $1 AND is_host = FALSE ", ) + .bind(project_id) .fetch(&mut tx); while let Some(connection_id) = db_guest_connection_ids.next().await { guest_connection_ids.push(ConnectionId(connection_id? as u32)); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 9ac10d1406..436b2d92a2 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -10,7 +10,11 @@ use anyhow::{anyhow, Context, Result}; use client::{proto, Client, PeerId, TypedEnvelope, UserStore}; use clock::ReplicaId; use collections::{hash_map, BTreeMap, HashMap, HashSet}; -use futures::{future::Shared, AsyncWriteExt, Future, FutureExt, StreamExt, TryFutureExt}; +use futures::{ + channel::{mpsc, oneshot}, + future::Shared, + AsyncWriteExt, Future, FutureExt, StreamExt, TryFutureExt, +}; use gpui::{ AnyModelHandle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, @@ -145,7 +149,7 @@ enum WorktreeHandle { enum ProjectClientState { Local { remote_id: u64, - metadata_changed: watch::Sender<()>, + metadata_changed: mpsc::UnboundedSender>, _maintain_metadata: Task<()>, _detect_unshare: Task>, }, @@ -533,7 +537,7 @@ impl Project { nonce: StdRng::from_entropy().gen(), }; for worktree in worktrees { - this.add_worktree(&worktree, cx); + let _ = this.add_worktree(&worktree, cx); } this }); @@ -728,14 +732,22 @@ impl Project { } } - fn metadata_changed(&mut self, cx: &mut ModelContext) { + fn metadata_changed(&mut self, cx: &mut ModelContext) -> impl Future { + let (tx, rx) = oneshot::channel(); if let Some(ProjectClientState::Local { metadata_changed, .. }) = &mut self.client_state { - *metadata_changed.borrow_mut() = (); + let _ = metadata_changed.unbounded_send(tx); } cx.notify(); + + async move { + // If the project is shared, this will resolve when the `_maintain_metadata` task has + // a chance to update the metadata. Otherwise, it will resolve right away because `tx` + // will get dropped. + let _ = rx.await; + } } pub fn collaborators(&self) -> &HashMap { @@ -1025,17 +1037,22 @@ impl Project { self.client_subscriptions .push(self.client.add_model_for_remote_entity(project_id, cx)); - self.metadata_changed(cx); + let _ = self.metadata_changed(cx); cx.emit(Event::RemoteIdChanged(Some(project_id))); cx.notify(); let mut status = self.client.status(); - let (metadata_changed_tx, mut metadata_changed_rx) = watch::channel(); + let (metadata_changed_tx, mut metadata_changed_rx) = mpsc::unbounded(); self.client_state = Some(ProjectClientState::Local { remote_id: project_id, metadata_changed: metadata_changed_tx, _maintain_metadata: cx.spawn_weak(move |this, cx| async move { - while let Some(()) = metadata_changed_rx.next().await { + while let Some(tx) = metadata_changed_rx.next().await { + let mut txs = vec![tx]; + while let Ok(Some(next_tx)) = metadata_changed_rx.try_next() { + txs.push(next_tx); + } + let Some(this) = this.upgrade(&cx) else { break }; this.read_with(&cx, |this, cx| { let worktrees = this @@ -1054,6 +1071,10 @@ impl Project { }) .await .log_err(); + + for tx in txs { + let _ = tx.send(()); + } } }), _detect_unshare: cx.spawn_weak(move |this, mut cx| { @@ -1105,7 +1126,7 @@ impl Project { } } - self.metadata_changed(cx); + let _ = self.metadata_changed(cx); cx.notify(); self.client.send(proto::UnshareProject { project_id: remote_id, @@ -4162,12 +4183,13 @@ impl Project { }); let worktree = worktree?; - let project_id = project.update(&mut cx, |project, cx| { - project.add_worktree(&worktree, cx); - project.remote_id() - }); + project + .update(&mut cx, |project, cx| project.add_worktree(&worktree, cx)) + .await; - if let Some(project_id) = project_id { + if let Some(project_id) = + project.read_with(&cx, |project, _| project.remote_id()) + { worktree .update(&mut cx, |worktree, cx| { worktree.as_local_mut().unwrap().share(project_id, cx) @@ -4191,7 +4213,11 @@ impl Project { }) } - pub fn remove_worktree(&mut self, id_to_remove: WorktreeId, cx: &mut ModelContext) { + pub fn remove_worktree( + &mut self, + id_to_remove: WorktreeId, + cx: &mut ModelContext, + ) -> impl Future { self.worktrees.retain(|worktree| { if let Some(worktree) = worktree.upgrade(cx) { let id = worktree.read(cx).id(); @@ -4205,11 +4231,14 @@ impl Project { false } }); - self.metadata_changed(cx); - cx.notify(); + self.metadata_changed(cx) } - fn add_worktree(&mut self, worktree: &ModelHandle, cx: &mut ModelContext) { + fn add_worktree( + &mut self, + worktree: &ModelHandle, + cx: &mut ModelContext, + ) -> impl Future { cx.observe(worktree, |_, _, cx| cx.notify()).detach(); if worktree.read(cx).is_local() { cx.subscribe(worktree, |this, worktree, event, cx| match event { @@ -4233,15 +4262,13 @@ impl Project { .push(WorktreeHandle::Weak(worktree.downgrade())); } - self.metadata_changed(cx); cx.observe_release(worktree, |this, worktree, cx| { - this.remove_worktree(worktree.id(), cx); - cx.notify(); + let _ = this.remove_worktree(worktree.id(), cx); }) .detach(); cx.emit(Event::WorktreeAdded); - cx.notify(); + self.metadata_changed(cx) } fn update_local_worktree_buffers( @@ -4558,11 +4585,11 @@ impl Project { } else { let worktree = Worktree::remote(remote_id, replica_id, worktree, client.clone(), cx); - this.add_worktree(&worktree, cx); + let _ = this.add_worktree(&worktree, cx); } } - this.metadata_changed(cx); + let _ = this.metadata_changed(cx); for (id, _) in old_worktrees_by_id { cx.emit(Event::WorktreeRemoved(id)); } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 9db524ee9b..2296741ed3 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1531,7 +1531,8 @@ impl Workspace { RemoveWorktreeFromProject(worktree_id): &RemoveWorktreeFromProject, cx: &mut ViewContext, ) { - self.project + let _ = self + .project .update(cx, |project, cx| project.remove_worktree(*worktree_id, cx)); } @@ -3177,7 +3178,7 @@ mod tests { // Remove a project folder project.update(cx, |project, cx| { - project.remove_worktree(worktree_id, cx); + let _ = project.remove_worktree(worktree_id, cx); }); assert_eq!( cx.current_window_title(window_id).as_deref(),