Introduce a worktree::Collaborator struct that holds the user_id

Nathan Sobo , Max Brunsfeld , and Antonio Scandurra created

We can use this to render avatars.

Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-Authored-By: Antonio Scandurra <me@as-cii.com>

Change summary

crates/project/src/worktree.rs | 64 +++++++++++++++++++++++++++---------
crates/server/src/rpc.rs       | 14 +++---
2 files changed, 55 insertions(+), 23 deletions(-)

Detailed changes

crates/project/src/worktree.rs 🔗

@@ -63,6 +63,12 @@ pub enum Event {
     Closed,
 }
 
+pub struct Collaborator {
+    pub user_id: u64,
+    pub peer_id: PeerId,
+    pub replica_id: ReplicaId,
+}
+
 impl Entity for Worktree {
     type Event = Event;
 
@@ -259,9 +265,19 @@ impl Worktree {
                     updates_tx,
                     client: rpc.clone(),
                     open_buffers: Default::default(),
-                    peers: peers
+                    collaborators: peers
                         .into_iter()
-                        .map(|p| (PeerId(p.peer_id), p.replica_id as ReplicaId))
+                        .map(|p| {
+                            let peer_id = PeerId(p.peer_id);
+                            (
+                                peer_id,
+                                Collaborator {
+                                    peer_id,
+                                    user_id: p.user_id,
+                                    replica_id: p.replica_id as ReplicaId,
+                                },
+                            )
+                        })
                         .collect(),
                     queued_operations: Default::default(),
                     languages,
@@ -390,10 +406,10 @@ impl Worktree {
             .close_remote_buffer(envelope, cx)
     }
 
-    pub fn peers(&self) -> &HashMap<PeerId, ReplicaId> {
+    pub fn collaborators(&self) -> &HashMap<PeerId, Collaborator> {
         match self {
-            Worktree::Local(worktree) => &worktree.peers,
-            Worktree::Remote(worktree) => &worktree.peers,
+            Worktree::Local(worktree) => &worktree.collaborators,
+            Worktree::Remote(worktree) => &worktree.collaborators,
         }
     }
 
@@ -772,7 +788,7 @@ pub struct LocalWorktree {
     open_buffers: HashMap<usize, WeakModelHandle<Buffer>>,
     shared_buffers: HashMap<PeerId, HashMap<u64, ModelHandle<Buffer>>>,
     diagnostics: HashMap<PathBuf, Vec<lsp::Diagnostic>>,
-    peers: HashMap<PeerId, ReplicaId>,
+    collaborators: HashMap<PeerId, Collaborator>,
     queued_operations: Vec<(u64, Operation)>,
     languages: Arc<LanguageRegistry>,
     rpc: Arc<Client>,
@@ -887,7 +903,7 @@ impl LocalWorktree {
                 shared_buffers: Default::default(),
                 diagnostics: Default::default(),
                 queued_operations: Default::default(),
-                peers: Default::default(),
+                collaborators: Default::default(),
                 languages,
                 rpc,
                 fs,
@@ -1085,8 +1101,15 @@ impl LocalWorktree {
             .peer
             .as_ref()
             .ok_or_else(|| anyhow!("empty peer"))?;
-        self.peers
-            .insert(PeerId(peer.peer_id), peer.replica_id as ReplicaId);
+        let peer_id = PeerId(peer.peer_id);
+        self.collaborators.insert(
+            peer_id,
+            Collaborator {
+                peer_id,
+                user_id: peer.user_id,
+                replica_id: peer.replica_id as ReplicaId,
+            },
+        );
         cx.notify();
 
         Ok(())
@@ -1099,9 +1122,10 @@ impl LocalWorktree {
     ) -> Result<()> {
         let peer_id = PeerId(envelope.payload.peer_id);
         let replica_id = self
-            .peers
+            .collaborators
             .remove(&peer_id)
-            .ok_or_else(|| anyhow!("unknown peer {:?}", peer_id))?;
+            .ok_or_else(|| anyhow!("unknown peer {:?}", peer_id))?
+            .replica_id;
         self.shared_buffers.remove(&peer_id);
         for (_, buffer) in &self.open_buffers {
             if let Some(buffer) = buffer.upgrade(cx) {
@@ -1373,7 +1397,7 @@ pub struct RemoteWorktree {
     updates_tx: postage::mpsc::Sender<proto::UpdateWorktree>,
     replica_id: ReplicaId,
     open_buffers: HashMap<usize, RemoteBuffer>,
-    peers: HashMap<PeerId, ReplicaId>,
+    collaborators: HashMap<PeerId, Collaborator>,
     languages: Arc<LanguageRegistry>,
     queued_operations: Vec<(u64, Operation)>,
     _subscriptions: Vec<client::Subscription>,
@@ -1501,8 +1525,15 @@ impl RemoteWorktree {
             .peer
             .as_ref()
             .ok_or_else(|| anyhow!("empty peer"))?;
-        self.peers
-            .insert(PeerId(peer.peer_id), peer.replica_id as ReplicaId);
+        let peer_id = PeerId(peer.peer_id);
+        self.collaborators.insert(
+            peer_id,
+            Collaborator {
+                peer_id,
+                user_id: peer.user_id,
+                replica_id: peer.replica_id as ReplicaId,
+            },
+        );
         cx.notify();
         Ok(())
     }
@@ -1514,9 +1545,10 @@ impl RemoteWorktree {
     ) -> Result<()> {
         let peer_id = PeerId(envelope.payload.peer_id);
         let replica_id = self
-            .peers
+            .collaborators
             .remove(&peer_id)
-            .ok_or_else(|| anyhow!("unknown peer {:?}", peer_id))?;
+            .ok_or_else(|| anyhow!("unknown peer {:?}", peer_id))?
+            .replica_id;
         for (_, buffer) in &self.open_buffers {
             if let Some(buffer) = buffer.upgrade(cx) {
                 buffer.update(cx, |buffer, cx| buffer.remove_peer(replica_id, cx));

crates/server/src/rpc.rs 🔗

@@ -1005,9 +1005,9 @@ mod tests {
         let replica_id_b = worktree_b.read_with(&cx_b, |tree, _| tree.replica_id());
         worktree_a
             .condition(&cx_a, |tree, _| {
-                tree.peers()
+                tree.collaborators()
                     .values()
-                    .any(|replica_id| *replica_id == replica_id_b)
+                    .any(|collaborator| collaborator.replica_id == replica_id_b)
             })
             .await;
 
@@ -1054,7 +1054,7 @@ mod tests {
         // Dropping the worktree removes client B from client A's peers.
         cx_b.update(move |_| drop(worktree_b));
         worktree_a
-            .condition(&cx_a, |tree, _| tree.peers().is_empty())
+            .condition(&cx_a, |tree, _| tree.collaborators().is_empty())
             .await;
     }
 
@@ -1492,7 +1492,7 @@ mod tests {
         .await
         .unwrap();
         worktree_a
-            .condition(&cx_a, |tree, _| tree.peers().len() == 1)
+            .condition(&cx_a, |tree, _| tree.collaborators().len() == 1)
             .await;
 
         let buffer_b = cx_b
@@ -1501,7 +1501,7 @@ mod tests {
         cx_b.update(|_| drop(worktree_b));
         drop(buffer_b);
         worktree_a
-            .condition(&cx_a, |tree, _| tree.peers().len() == 0)
+            .condition(&cx_a, |tree, _| tree.collaborators().len() == 0)
             .await;
     }
 
@@ -1553,13 +1553,13 @@ mod tests {
         .await
         .unwrap();
         worktree_a
-            .condition(&cx_a, |tree, _| tree.peers().len() == 1)
+            .condition(&cx_a, |tree, _| tree.collaborators().len() == 1)
             .await;
 
         // Drop client B's connection and ensure client A observes client B leaving the worktree.
         client_b.disconnect(&cx_b.to_async()).await.unwrap();
         worktree_a
-            .condition(&cx_a, |tree, _| tree.peers().len() == 0)
+            .condition(&cx_a, |tree, _| tree.collaborators().len() == 0)
             .await;
     }