diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 5e0c6f7789..c19538f55c 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -141,12 +141,11 @@ impl Server { server .add_request_handler(Server::ping) .add_request_handler(Server::register_project) - .add_message_handler(Server::unregister_project) + .add_request_handler(Server::unregister_project) .add_request_handler(Server::join_project) .add_message_handler(Server::leave_project) .add_message_handler(Server::respond_to_join_project_request) - .add_request_handler(Server::register_worktree) - .add_message_handler(Server::unregister_worktree) + .add_message_handler(Server::update_project) .add_request_handler(Server::update_worktree) .add_message_handler(Server::start_language_server) .add_message_handler(Server::update_language_server) @@ -484,14 +483,15 @@ impl Server { user_id = state.user_id_for_connection(request.sender_id)?; project_id = state.register_project(request.sender_id, user_id); }; - self.update_user_contacts(user_id).await?; response.send(proto::RegisterProjectResponse { project_id })?; + self.update_user_contacts(user_id).await?; Ok(()) } async fn unregister_project( self: Arc, request: TypedEnvelope, + response: Response, ) -> Result<()> { let (user_id, project) = { let mut state = self.store_mut().await; @@ -529,6 +529,7 @@ impl Server { } self.update_user_contacts(user_id).await?; + response.send(proto::Ack {})?; Ok(()) } @@ -568,6 +569,7 @@ impl Server { response: Response, ) -> Result<()> { let project_id = request.payload.project_id; + let host_user_id; let guest_user_id; let host_connection_id; @@ -768,63 +770,28 @@ impl Server { Ok(()) } - async fn register_worktree( + async fn update_project( self: Arc, - request: TypedEnvelope, - response: Response, + request: TypedEnvelope, ) -> Result<()> { - let host_user_id; + let user_id; { let mut state = self.store_mut().await; - host_user_id = state.user_id_for_connection(request.sender_id)?; - + user_id = state.user_id_for_connection(request.sender_id)?; let guest_connection_ids = state .read_project(request.payload.project_id, request.sender_id)? .guest_connection_ids(); - state.register_worktree( + state.update_project( request.payload.project_id, - request.payload.worktree_id, + &request.payload.worktrees, request.sender_id, - Worktree { - root_name: request.payload.root_name.clone(), - visible: request.payload.visible, - ..Default::default() - }, )?; - broadcast(request.sender_id, guest_connection_ids, |connection_id| { self.peer .forward_send(request.sender_id, connection_id, request.payload.clone()) }); - } - self.update_user_contacts(host_user_id).await?; - response.send(proto::Ack {})?; - Ok(()) - } - - async fn unregister_worktree( - self: Arc, - request: TypedEnvelope, - ) -> Result<()> { - let host_user_id; - let project_id = request.payload.project_id; - let worktree_id = request.payload.worktree_id; - { - let mut state = self.store_mut().await; - let (_, guest_connection_ids) = - state.unregister_worktree(project_id, worktree_id, request.sender_id)?; - host_user_id = state.user_id_for_connection(request.sender_id)?; - broadcast(request.sender_id, guest_connection_ids, |conn_id| { - self.peer.send( - conn_id, - proto::UnregisterWorktree { - project_id, - worktree_id, - }, - ) - }); - } - self.update_user_contacts(host_user_id).await?; + }; + self.update_user_contacts(user_id).await?; Ok(()) } @@ -833,10 +800,11 @@ impl Server { request: TypedEnvelope, response: Response, ) -> Result<()> { - let connection_ids = self.store_mut().await.update_worktree( + let (connection_ids, metadata_changed) = self.store_mut().await.update_worktree( request.sender_id, request.payload.project_id, request.payload.worktree_id, + &request.payload.root_name, &request.payload.removed_entries, &request.payload.updated_entries, request.payload.scan_id, @@ -846,6 +814,13 @@ impl Server { self.peer .forward_send(request.sender_id, connection_id, request.payload.clone()) }); + if metadata_changed { + let user_id = self + .store() + .await + .user_id_for_connection(request.sender_id)?; + self.update_user_contacts(user_id).await?; + } response.send(proto::Ack {})?; Ok(()) } diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index 78227999bc..03e529df3e 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -312,19 +312,32 @@ impl Store { project_id } - pub fn register_worktree( + pub fn update_project( &mut self, project_id: u64, - worktree_id: u64, + worktrees: &[proto::WorktreeMetadata], connection_id: ConnectionId, - worktree: Worktree, ) -> Result<()> { let project = self .projects .get_mut(&project_id) .ok_or_else(|| anyhow!("no such project"))?; if project.host_connection_id == connection_id { - project.worktrees.insert(worktree_id, worktree); + let mut old_worktrees = mem::take(&mut project.worktrees); + for worktree in worktrees { + if let Some(old_worktree) = old_worktrees.remove(&worktree.id) { + project.worktrees.insert(worktree.id, old_worktree); + } else { + project.worktrees.insert( + worktree.id, + Worktree { + root_name: worktree.root_name.clone(), + visible: worktree.visible, + ..Default::default() + }, + ); + } + } Ok(()) } else { Err(anyhow!("no such project"))? @@ -374,27 +387,6 @@ impl Store { } } - pub fn unregister_worktree( - &mut self, - project_id: u64, - worktree_id: u64, - acting_connection_id: ConnectionId, - ) -> Result<(Worktree, Vec)> { - let project = self - .projects - .get_mut(&project_id) - .ok_or_else(|| anyhow!("no such project"))?; - if project.host_connection_id != acting_connection_id { - Err(anyhow!("not your worktree"))?; - } - - let worktree = project - .worktrees - .remove(&worktree_id) - .ok_or_else(|| anyhow!("no such worktree"))?; - Ok((worktree, project.guest_connection_ids())) - } - pub fn update_diagnostic_summary( &mut self, project_id: u64, @@ -573,15 +565,15 @@ impl Store { connection_id: ConnectionId, project_id: u64, worktree_id: u64, + worktree_root_name: &str, removed_entries: &[u64], updated_entries: &[proto::Entry], scan_id: u64, - ) -> Result> { + ) -> Result<(Vec, bool)> { let project = self.write_project(project_id, connection_id)?; - let worktree = project - .worktrees - .get_mut(&worktree_id) - .ok_or_else(|| anyhow!("no such worktree"))?; + let mut worktree = project.worktrees.entry(worktree_id).or_default(); + let metadata_changed = worktree_root_name != worktree.root_name; + worktree.root_name = worktree_root_name.to_string(); for entry_id in removed_entries { worktree.entries.remove(&entry_id); } @@ -590,7 +582,7 @@ impl Store { } worktree.scan_id = scan_id; let connection_ids = project.connection_ids(); - Ok(connection_ids) + Ok((connection_ids, metadata_changed)) } pub fn project_connection_ids( diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 6a8adbb2ae..85b6660021 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -282,8 +282,7 @@ impl Project { client.add_model_message_handler(Self::handle_update_language_server); client.add_model_message_handler(Self::handle_remove_collaborator); client.add_model_message_handler(Self::handle_join_project_request_cancelled); - client.add_model_message_handler(Self::handle_register_worktree); - client.add_model_message_handler(Self::handle_unregister_worktree); + client.add_model_message_handler(Self::handle_update_project); client.add_model_message_handler(Self::handle_unregister_project); client.add_model_message_handler(Self::handle_project_unshared); client.add_model_message_handler(Self::handle_update_buffer_file); @@ -338,7 +337,9 @@ impl Project { .await .log_err()?; } else { - this.update(&mut cx, |this, cx| this.unregister(cx)); + this.update(&mut cx, |this, cx| this.unregister(cx)) + .await + .log_err(); } } None @@ -638,30 +639,29 @@ impl Project { } } - fn unregister(&mut self, cx: &mut ModelContext) { + fn unregister(&mut self, cx: &mut ModelContext) -> Task> { self.unshared(cx); - for worktree in &self.worktrees { - if let Some(worktree) = worktree.upgrade(cx) { - worktree.update(cx, |worktree, _| { - worktree.as_local_mut().unwrap().unregister(); + if let ProjectClientState::Local { remote_id_rx, .. } = &mut self.client_state { + if let Some(remote_id) = *remote_id_rx.borrow() { + let request = self.client.request(proto::UnregisterProject { + project_id: remote_id, + }); + return cx.spawn(|this, mut cx| async move { + let response = request.await; + this.update(&mut cx, |this, cx| { + if let ProjectClientState::Local { remote_id_tx, .. } = + &mut this.client_state + { + *remote_id_tx.borrow_mut() = None; + } + this.subscriptions.clear(); + this.metadata_changed(cx); + }); + response.map(drop) }); } } - - if let ProjectClientState::Local { remote_id_tx, .. } = &mut self.client_state { - let mut remote_id = remote_id_tx.borrow_mut(); - if let Some(remote_id) = *remote_id { - self.client - .send(proto::UnregisterProject { - project_id: remote_id, - }) - .log_err(); - } - *remote_id = None; - } - - self.subscriptions.clear(); - self.metadata_changed(cx); + Task::ready(Ok(())) } fn register(&mut self, cx: &mut ModelContext) -> Task> { @@ -674,8 +674,6 @@ impl Project { let response = self.client.request(proto::RegisterProject {}); cx.spawn(|this, mut cx| async move { let remote_id = response.await?.project_id; - - let mut registrations = Vec::new(); this.update(&mut cx, |this, cx| { if let ProjectClientState::Local { remote_id_tx, .. } = &mut this.client_state { *remote_id_tx.borrow_mut() = Some(remote_id); @@ -683,22 +681,10 @@ impl Project { this.metadata_changed(cx); cx.emit(Event::RemoteIdChanged(Some(remote_id))); - this.subscriptions .push(this.client.add_model_for_remote_entity(remote_id, cx)); - - for worktree in &this.worktrees { - if let Some(worktree) = worktree.upgrade(cx) { - registrations.push(worktree.update(cx, |worktree, cx| { - let worktree = worktree.as_local_mut().unwrap(); - worktree.register(remote_id, cx) - })); - } - } - }); - - futures::future::try_join_all(registrations).await?; - Ok(()) + Ok(()) + }) }) } @@ -757,7 +743,27 @@ impl Project { } fn metadata_changed(&mut self, cx: &mut ModelContext) { + cx.notify(); self.project_store.update(cx, |_, cx| cx.notify()); + + if let ProjectClientState::Local { remote_id_rx, .. } = &self.client_state { + if let Some(project_id) = *remote_id_rx.borrow() { + self.client + .send(proto::UpdateProject { + project_id, + worktrees: self + .worktrees + .iter() + .filter_map(|worktree| { + worktree.upgrade(&cx).map(|worktree| { + worktree.read(cx).as_local().unwrap().metadata_proto() + }) + }) + .collect(), + }) + .log_err(); + } + } } pub fn collaborators(&self) -> &HashMap { @@ -3696,37 +3702,19 @@ impl Project { }); let worktree = worktree?; - let remote_project_id = project.update(&mut cx, |project, cx| { + let project_id = project.update(&mut cx, |project, cx| { project.add_worktree(&worktree, cx); - project.remote_id() + project.shared_remote_id() }); - if let Some(project_id) = remote_project_id { - // Because sharing is async, we may have *unshared* the project by the time it completes, - // in which case we need to register the worktree instead. - loop { - if project.read_with(&cx, |project, _| project.is_shared()) { - if worktree - .update(&mut cx, |worktree, cx| { - worktree.as_local_mut().unwrap().share(project_id, cx) - }) - .await - .is_ok() - { - break; - } - } else { - worktree - .update(&mut cx, |worktree, cx| { - worktree - .as_local_mut() - .unwrap() - .register(project_id, cx) - }) - .await?; - break; - } - } + // Because sharing is async, we may have *unshared* the project by the time it completes. + if let Some(project_id) = project_id { + worktree + .update(&mut cx, |worktree, cx| { + worktree.as_local_mut().unwrap().share(project_id, cx) + }) + .await + .log_err(); } Ok(worktree) @@ -4071,40 +4059,51 @@ impl Project { Ok(()) } - async fn handle_register_worktree( + async fn handle_update_project( this: ModelHandle, - envelope: TypedEnvelope, + envelope: TypedEnvelope, client: Arc, mut cx: AsyncAppContext, ) -> Result<()> { this.update(&mut cx, |this, cx| { - let remote_id = this.remote_id().ok_or_else(|| anyhow!("invalid project"))?; let replica_id = this.replica_id(); - let worktree = proto::Worktree { - id: envelope.payload.worktree_id, - root_name: envelope.payload.root_name, - entries: Default::default(), - diagnostic_summaries: Default::default(), - visible: envelope.payload.visible, - scan_id: 0, - }; - let (worktree, load_task) = - Worktree::remote(remote_id, replica_id, worktree, client, cx); - this.add_worktree(&worktree, cx); - load_task.detach(); - Ok(()) - }) - } + let remote_id = this.remote_id().ok_or_else(|| anyhow!("invalid project"))?; + + let mut old_worktrees_by_id = this + .worktrees + .drain(..) + .filter_map(|worktree| { + let worktree = worktree.upgrade(cx)?; + Some((worktree.read(cx).id(), worktree)) + }) + .collect::>(); + + for worktree in envelope.payload.worktrees { + if let Some(old_worktree) = + old_worktrees_by_id.remove(&WorktreeId::from_proto(worktree.id)) + { + this.worktrees.push(WorktreeHandle::Strong(old_worktree)); + } else { + let worktree = proto::Worktree { + id: worktree.id, + root_name: worktree.root_name, + entries: Default::default(), + diagnostic_summaries: Default::default(), + visible: worktree.visible, + scan_id: 0, + }; + let (worktree, load_task) = + Worktree::remote(remote_id, replica_id, worktree, client.clone(), cx); + this.add_worktree(&worktree, cx); + load_task.detach(); + } + } + + this.metadata_changed(cx); + for (id, _) in old_worktrees_by_id { + cx.emit(Event::WorktreeRemoved(id)); + } - async fn handle_unregister_worktree( - this: ModelHandle, - envelope: TypedEnvelope, - _: Arc, - mut cx: AsyncAppContext, - ) -> Result<()> { - this.update(&mut cx, |this, cx| { - let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id); - this.remove_worktree(worktree_id, cx); Ok(()) }) } diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 05eaecbc97..c1d892c283 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -68,7 +68,6 @@ pub struct LocalWorktree { last_scan_state_rx: watch::Receiver, _background_scanner_task: Option>, poll_task: Option>, - registration: Registration, share: Option, diagnostics: HashMap, Vec>>, diagnostic_summaries: TreeMap, @@ -129,13 +128,6 @@ enum ScanState { Err(Arc), } -#[derive(Debug, Eq, PartialEq)] -enum Registration { - None, - Pending, - Done { project_id: u64 }, -} - struct ShareState { project_id: u64, snapshots_tx: Sender, @@ -148,12 +140,6 @@ pub enum Event { impl Entity for Worktree { type Event = Event; - - fn release(&mut self, _: &mut MutableAppContext) { - if let Some(worktree) = self.as_local_mut() { - worktree.unregister(); - } - } } impl Worktree { @@ -479,7 +465,6 @@ impl LocalWorktree { background_snapshot: Arc::new(Mutex::new(snapshot)), last_scan_state_rx, _background_scanner_task: None, - registration: Registration::None, share: None, poll_task: None, diagnostics: Default::default(), @@ -601,6 +586,14 @@ impl LocalWorktree { self.snapshot.clone() } + pub fn metadata_proto(&self) -> proto::WorktreeMetadata { + proto::WorktreeMetadata { + id: self.id().to_proto(), + root_name: self.root_name().to_string(), + visible: self.visible, + } + } + fn load(&self, path: &Path, cx: &mut ModelContext) -> Task> { let handle = cx.handle(); let path = Arc::from(path); @@ -897,46 +890,7 @@ impl LocalWorktree { }) } - pub fn register( - &mut self, - project_id: u64, - cx: &mut ModelContext, - ) -> Task> { - if self.registration != Registration::None { - return Task::ready(Ok(())); - } - - self.registration = Registration::Pending; - let client = self.client.clone(); - let register_message = proto::RegisterWorktree { - project_id, - worktree_id: self.id().to_proto(), - root_name: self.root_name().to_string(), - visible: self.visible, - }; - let request = client.request(register_message); - cx.spawn(|this, mut cx| async move { - let response = request.await; - this.update(&mut cx, |this, _| { - let worktree = this.as_local_mut().unwrap(); - match response { - Ok(_) => { - if worktree.registration == Registration::Pending { - worktree.registration = Registration::Done { project_id }; - } - Ok(()) - } - Err(error) => { - worktree.registration = Registration::None; - Err(error) - } - } - }) - }) - } - pub fn share(&mut self, project_id: u64, cx: &mut ModelContext) -> Task> { - let register = self.register(project_id, cx); let (share_tx, share_rx) = oneshot::channel(); let (snapshots_to_send_tx, snapshots_to_send_rx) = smol::channel::unbounded::(); @@ -1041,7 +995,6 @@ impl LocalWorktree { } cx.spawn_weak(|this, cx| async move { - register.await?; if let Some(this) = this.upgrade(&cx) { this.read_with(&cx, |this, _| { let this = this.as_local().unwrap(); @@ -1054,20 +1007,6 @@ impl LocalWorktree { }) } - pub fn unregister(&mut self) { - self.unshare(); - if let Registration::Done { project_id } = self.registration { - self.client - .clone() - .send(proto::UnregisterWorktree { - project_id, - worktree_id: self.id().to_proto(), - }) - .log_err(); - } - self.registration = Registration::None; - } - pub fn unshare(&mut self) { self.share.take(); } diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 3b4d8cc4f9..44bc673fce 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -35,8 +35,7 @@ message Envelope { OpenBufferForSymbol open_buffer_for_symbol = 28; OpenBufferForSymbolResponse open_buffer_for_symbol_response = 29; - RegisterWorktree register_worktree = 30; - UnregisterWorktree unregister_worktree = 31; + UpdateProject update_project = 30; UpdateWorktree update_worktree = 32; CreateProjectEntry create_project_entry = 33; @@ -129,6 +128,11 @@ message UnregisterProject { uint64 project_id = 1; } +message UpdateProject { + uint64 project_id = 1; + repeated WorktreeMetadata worktrees = 2; +} + message RequestJoinProject { uint64 requester_id = 1; uint64 project_id = 2; @@ -177,18 +181,6 @@ message LeaveProject { uint64 project_id = 1; } -message RegisterWorktree { - uint64 project_id = 1; - uint64 worktree_id = 2; - string root_name = 3; - bool visible = 4; -} - -message UnregisterWorktree { - uint64 project_id = 1; - uint64 worktree_id = 2; -} - message UpdateWorktree { uint64 project_id = 1; uint64 worktree_id = 2; @@ -934,3 +926,9 @@ message ProjectMetadata { repeated string worktree_root_names = 3; repeated uint64 guests = 4; } + +message WorktreeMetadata { + uint64 id = 1; + string root_name = 2; + bool visible = 3; +} diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index b6d7836427..0fcb2eaea3 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -132,7 +132,6 @@ messages!( (Ping, Foreground), (ProjectUnshared, Foreground), (RegisterProject, Foreground), - (RegisterWorktree, Foreground), (ReloadBuffers, Foreground), (ReloadBuffersResponse, Foreground), (RemoveProjectCollaborator, Foreground), @@ -151,7 +150,6 @@ messages!( (Test, Foreground), (Unfollow, Foreground), (UnregisterProject, Foreground), - (UnregisterWorktree, Foreground), (UpdateBuffer, Foreground), (UpdateBufferFile, Foreground), (UpdateContacts, Foreground), @@ -159,6 +157,7 @@ messages!( (UpdateFollowers, Foreground), (UpdateInviteInfo, Foreground), (UpdateLanguageServer, Foreground), + (UpdateProject, Foreground), (UpdateWorktree, Foreground), ); @@ -192,7 +191,6 @@ request_messages!( (PerformRename, PerformRenameResponse), (PrepareRename, PrepareRenameResponse), (RegisterProject, RegisterProjectResponse), - (RegisterWorktree, Ack), (ReloadBuffers, ReloadBuffersResponse), (RequestContact, Ack), (RemoveContact, Ack), @@ -202,6 +200,7 @@ request_messages!( (SearchProject, SearchProjectResponse), (SendChannelMessage, SendChannelMessageResponse), (Test, Test), + (UnregisterProject, Ack), (UpdateBuffer, Ack), (UpdateWorktree, Ack), ); @@ -242,13 +241,12 @@ entity_messages!( StartLanguageServer, Unfollow, UnregisterProject, - UnregisterWorktree, UpdateBuffer, UpdateBufferFile, UpdateDiagnosticSummary, UpdateFollowers, UpdateLanguageServer, - RegisterWorktree, + UpdateProject, UpdateWorktree, );