Rip out project registration and use sharing/unsharing instead

Antonio Scandurra created

Change summary

crates/collab/src/integration_tests.rs      | 306 ++++++++++---------
crates/collab/src/rpc.rs                    |  60 +--
crates/collab/src/rpc/store.rs              |  87 +---
crates/contacts_panel/src/contacts_panel.rs |   8 
crates/project/src/project.rs               | 351 +++++++---------------
crates/rpc/proto/zed.proto                  |  17 
crates/rpc/src/proto.rs                     |   9 
7 files changed, 323 insertions(+), 515 deletions(-)

Detailed changes

crates/collab/src/integration_tests.rs 🔗

@@ -314,11 +314,14 @@ async fn test_share_project(
         .await;
 
     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
-    let project_id = project_a.read_with(cx_a, |project, _| project.remote_id().unwrap());
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
 
     // Join that project as client B
     let client_b_peer_id = client_b.peer_id;
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
     let replica_id_b = project_b.read_with(cx_b, |project, _| {
         assert_eq!(
             project
@@ -390,17 +393,7 @@ async fn test_share_project(
 
     // Client B can join again on a different window because they are already a participant.
     let client_b2 = server.create_client(cx_b2, "user_b").await;
-    let project_b2 = Project::remote(
-        project_id,
-        client_b2.client.clone(),
-        client_b2.user_store.clone(),
-        client_b2.project_store.clone(),
-        client_b2.language_registry.clone(),
-        FakeFs::new(cx_b2.background()),
-        cx_b2.to_async(),
-    )
-    .await
-    .unwrap();
+    let project_b2 = client_b2.build_remote_project(project_id, cx_b2).await;
     deterministic.run_until_parked();
     project_a.read_with(cx_a, |project, _| {
         assert_eq!(project.collaborators().len(), 2);
@@ -449,8 +442,12 @@ async fn test_unshare_project(
         .await;
 
     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
     let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
     assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
 
     project_b
@@ -467,7 +464,11 @@ async fn test_unshare_project(
     assert!(project_b.read_with(cx_b, |project, _| project.is_read_only()));
 
     // Client B can join again after client A re-shares.
-    let project_b2 = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let project_b2 = client_b.build_remote_project(project_id, cx_b).await;
     assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
     project_b2
         .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
@@ -517,9 +518,12 @@ async fn test_host_disconnect(
 
     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
     let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
-    project_a.read_with(cx_a, |project, _| project.remote_id().unwrap());
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
 
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
     assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
 
     let (_, workspace_b) =
@@ -574,7 +578,11 @@ async fn test_host_disconnect(
     });
 
     // Ensure guests can still join.
-    let project_b2 = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let project_b2 = client_b.build_remote_project(project_id, cx_b).await;
     assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
     project_b2
         .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
@@ -613,10 +621,14 @@ async fn test_propagate_saves_and_fs_changes(
         .await;
     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
     let worktree_a = project_a.read_with(cx_a, |p, cx| p.worktrees(cx).next().unwrap());
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
 
     // Join that worktree as clients B and C.
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
-    let project_c = client_c.build_remote_project(&project_a, cx_a, cx_c).await;
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
+    let project_c = client_c.build_remote_project(project_id, cx_c).await;
     let worktree_b = project_b.read_with(cx_b, |p, cx| p.worktrees(cx).next().unwrap());
     let worktree_c = project_c.read_with(cx_c, |p, cx| p.worktrees(cx).next().unwrap());
 
@@ -756,7 +768,11 @@ async fn test_fs_operations(
         )
         .await;
     let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
     let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap());
@@ -1016,7 +1032,11 @@ async fn test_buffer_conflict_after_save(cx_a: &mut TestAppContext, cx_b: &mut T
         )
         .await;
     let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     // Open a buffer as client B
     let buffer_b = project_b
@@ -1065,7 +1085,11 @@ async fn test_buffer_reloading(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont
         )
         .await;
     let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     // Open a buffer as client B
     let buffer_b = project_b
@@ -1114,7 +1138,11 @@ async fn test_editing_while_guest_opens_buffer(
         .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
         .await;
     let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     // Open a buffer as client A
     let buffer_a = project_a
@@ -1156,7 +1184,11 @@ async fn test_leaving_worktree_while_opening_buffer(
         .insert_tree("/dir", json!({ "a.txt": "a-contents" }))
         .await;
     let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     // See that a guest has joined as client A.
     project_a
@@ -1201,7 +1233,11 @@ async fn test_canceling_buffer_opening(
         )
         .await;
     let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     let buffer_a = project_a
         .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
@@ -1243,7 +1279,11 @@ async fn test_leaving_project(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte
         )
         .await;
     let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
-    let _project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let _project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     // Client A sees that a guest has joined.
     project_a
@@ -1257,7 +1297,7 @@ async fn test_leaving_project(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte
         .await;
 
     // Rejoin the project as client B
-    let _project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let _project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     // Client A sees that a guest has re-joined.
     project_a
@@ -1317,7 +1357,10 @@ async fn test_collaborating_with_diagnostics(
         )
         .await;
     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
-    let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
 
     // Cause the language server to start.
     let _buffer = cx_a
@@ -1335,7 +1378,7 @@ async fn test_collaborating_with_diagnostics(
         .unwrap();
 
     // Join the worktree as client B.
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     // Simulate a language server reporting errors for a file.
     let mut fake_language_server = fake_language_servers.next().await.unwrap();
@@ -1383,7 +1426,7 @@ async fn test_collaborating_with_diagnostics(
     });
 
     // Join project as client C and observe the diagnostics.
-    let project_c = client_c.build_remote_project(&project_a, cx_a, cx_c).await;
+    let project_c = client_c.build_remote_project(project_id, cx_c).await;
     deterministic.run_until_parked();
     project_c.read_with(cx_c, |project, cx| {
         assert_eq!(
@@ -1561,7 +1604,11 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu
         )
         .await;
     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     // Open a file in an editor as the guest.
     let buffer_b = project_b
@@ -1705,8 +1752,12 @@ async fn test_reloading_buffer_manually(cx_a: &mut TestAppContext, cx_b: &mut Te
         .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
         .await
         .unwrap();
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
 
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     let buffer_b = cx_b
         .background()
@@ -1805,7 +1856,11 @@ async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppCon
         .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" }))
         .await;
     let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await;
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     let buffer_b = cx_b
         .background()
@@ -1908,7 +1963,11 @@ async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
         )
         .await;
     let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     // Open the file on client B.
     let buffer_b = cx_b
@@ -2047,7 +2106,11 @@ async fn test_references(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
         )
         .await;
     let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await;
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     // Open the file on client B.
     let buffer_b = cx_b
@@ -2142,8 +2205,12 @@ async fn test_project_search(cx_a: &mut TestAppContext, cx_b: &mut TestAppContex
     worktree_2
         .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
         .await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
 
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     // Perform a search as the guest.
     let results = project_b
@@ -2212,7 +2279,11 @@ async fn test_document_highlights(cx_a: &mut TestAppContext, cx_b: &mut TestAppC
     client_a.language_registry.add(Arc::new(language));
 
     let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     // Open the file on client B.
     let buffer_b = cx_b
@@ -2309,7 +2380,11 @@ async fn test_lsp_hover(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
     client_a.language_registry.add(Arc::new(language));
 
     let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await;
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     // Open the file as the guest
     let buffer_b = cx_b
@@ -2414,7 +2489,11 @@ async fn test_project_symbols(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte
         )
         .await;
     let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await;
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     // Cause the language server to start.
     let _buffer = cx_b
@@ -2510,7 +2589,11 @@ async fn test_open_buffer_while_getting_definition_pointing_to_it(
         )
         .await;
     let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await;
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     let buffer_b1 = cx_b
         .background()
@@ -2581,9 +2664,13 @@ async fn test_collaborating_with_code_actions(
         )
         .await;
     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
 
     // Join the project as client B.
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
     let (_window_b, workspace_b) =
         cx_b.add_window(|cx| Workspace::new(project_b.clone(), |_, _| unimplemented!(), cx));
     let editor_b = workspace_b
@@ -2798,7 +2885,11 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
         )
         .await;
     let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     let (_window_b, workspace_b) =
         cx_b.add_window(|cx| Workspace::new(project_b.clone(), |_, _| unimplemented!(), cx));
@@ -3006,7 +3097,11 @@ async fn test_language_server_statuses(
         );
     });
 
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
     project_b.read_with(cx_b, |project, _| {
         let status = project.language_server_statuses().next().unwrap();
         assert_eq!(status.name, "the-language-server");
@@ -3485,79 +3580,6 @@ async fn test_contacts(
         [("user_a".to_string(), true), ("user_b".to_string(), true)]
     );
 
-    // Share a project as client A.
-    client_a.fs.create_dir(Path::new("/a")).await.unwrap();
-    let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
-
-    deterministic.run_until_parked();
-    assert_eq!(
-        contacts(&client_a, cx_a),
-        [("user_b".to_string(), true), ("user_c".to_string(), true)]
-    );
-    assert_eq!(
-        contacts(&client_b, cx_b),
-        [("user_a".to_string(), true), ("user_c".to_string(), true)]
-    );
-    assert_eq!(
-        contacts(&client_c, cx_c),
-        [("user_a".to_string(), true), ("user_b".to_string(), true)]
-    );
-
-    let _project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
-
-    deterministic.run_until_parked();
-    assert_eq!(
-        contacts(&client_a, cx_a),
-        [("user_b".to_string(), true), ("user_c".to_string(), true)]
-    );
-    assert_eq!(
-        contacts(&client_b, cx_b),
-        [("user_a".to_string(), true,), ("user_c".to_string(), true)]
-    );
-    assert_eq!(
-        contacts(&client_c, cx_c),
-        [("user_a".to_string(), true,), ("user_b".to_string(), true)]
-    );
-
-    // Add a local project as client B
-    client_a.fs.create_dir("/b".as_ref()).await.unwrap();
-    let (_project_b, _) = client_b.build_local_project("/b", cx_b).await;
-
-    deterministic.run_until_parked();
-    assert_eq!(
-        contacts(&client_a, cx_a),
-        [("user_b".to_string(), true), ("user_c".to_string(), true)]
-    );
-    assert_eq!(
-        contacts(&client_b, cx_b),
-        [("user_a".to_string(), true), ("user_c".to_string(), true)]
-    );
-    assert_eq!(
-        contacts(&client_c, cx_c),
-        [("user_a".to_string(), true,), ("user_b".to_string(), true)]
-    );
-
-    project_a
-        .condition(cx_a, |project, _| {
-            project.collaborators().contains_key(&client_b.peer_id)
-        })
-        .await;
-
-    cx_a.update(move |_| drop(project_a));
-    deterministic.run_until_parked();
-    assert_eq!(
-        contacts(&client_a, cx_a),
-        [("user_b".to_string(), true), ("user_c".to_string(), true)]
-    );
-    assert_eq!(
-        contacts(&client_b, cx_b),
-        [("user_a".to_string(), true), ("user_c".to_string(), true)]
-    );
-    assert_eq!(
-        contacts(&client_c, cx_c),
-        [("user_a".to_string(), true), ("user_b".to_string(), true)]
-    );
-
     server.disconnect_client(client_c.current_user_id(cx_c));
     server.forbid_connections();
     deterministic.advance_clock(rpc::RECEIVE_TIMEOUT);
@@ -3811,8 +3833,11 @@ async fn test_following(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
         )
         .await;
     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
-
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     // Client A opens some editors.
     let workspace_a = client_a.build_workspace(&project_a, cx_a);
@@ -4019,9 +4044,13 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T
         )
         .await;
     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
 
     // Client B joins the project.
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     // Client A opens some editors.
     let workspace_a = client_a.build_workspace(&project_a, cx_a);
@@ -4182,7 +4211,11 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont
         )
         .await;
     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
     // Client A opens some editors.
     let workspace_a = client_a.build_workspace(&project_a, cx_a);
@@ -4331,8 +4364,12 @@ async fn test_peers_simultaneously_following_each_other(
     client_a.fs.insert_tree("/a", json!({})).await;
     let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
     let workspace_a = client_a.build_workspace(&project_a, cx_a);
+    let project_id = project_a
+        .update(cx_a, |project, cx| project.share(cx))
+        .await
+        .unwrap();
 
-    let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
     let workspace_b = client_b.build_workspace(&project_b, cx_b);
 
     deterministic.run_until_parked();
@@ -4445,9 +4482,6 @@ async fn test_random_collaboration(
             cx,
         )
     });
-    let host_project_id = host_project
-        .update(&mut host_cx, |p, _| p.next_remote_id())
-        .await;
 
     let (collab_worktree, _) = host_project
         .update(&mut host_cx, |project, cx| {
@@ -4588,7 +4622,7 @@ async fn test_random_collaboration(
         .await;
     host_language_registry.add(Arc::new(language));
 
-    host_project
+    let host_project_id = host_project
         .update(&mut host_cx, |project, cx| project.share(cx))
         .await
         .unwrap();
@@ -5155,40 +5189,26 @@ impl TestClient {
         worktree
             .read_with(cx, |tree, _| tree.as_local().unwrap().scan_complete())
             .await;
-        project
-            .update(cx, |project, _| project.next_remote_id())
-            .await;
         (project, worktree.read_with(cx, |tree, _| tree.id()))
     }
 
     async fn build_remote_project(
         &self,
-        host_project: &ModelHandle<Project>,
-        host_cx: &mut TestAppContext,
+        host_project_id: u64,
         guest_cx: &mut TestAppContext,
     ) -> ModelHandle<Project> {
-        let host_project_id = host_project
-            .read_with(host_cx, |project, _| project.next_remote_id())
-            .await;
-        host_project
-            .update(host_cx, |project, cx| project.share(cx))
-            .await
-            .unwrap();
-        let languages = host_project.read_with(host_cx, |project, _| project.languages().clone());
         let project_b = guest_cx.spawn(|cx| {
             Project::remote(
                 host_project_id,
                 self.client.clone(),
                 self.user_store.clone(),
                 self.project_store.clone(),
-                languages,
+                self.language_registry.clone(),
                 FakeFs::new(cx.background()),
                 cx,
             )
         });
-
-        let project = project_b.await.unwrap();
-        project
+        project_b.await.unwrap()
     }
 
     fn build_workspace(

crates/collab/src/rpc.rs 🔗

@@ -151,11 +151,10 @@ impl Server {
             .add_message_handler(Server::leave_room)
             .add_request_handler(Server::call)
             .add_message_handler(Server::decline_call)
-            .add_request_handler(Server::register_project)
-            .add_request_handler(Server::unregister_project)
+            .add_request_handler(Server::share_project)
+            .add_message_handler(Server::unshare_project)
             .add_request_handler(Server::join_project)
             .add_message_handler(Server::leave_project)
-            .add_message_handler(Server::unshare_project)
             .add_message_handler(Server::update_project)
             .add_message_handler(Server::register_project_activity)
             .add_request_handler(Server::update_worktree)
@@ -470,18 +469,18 @@ impl Server {
     async fn sign_out(self: &mut Arc<Self>, connection_id: ConnectionId) -> Result<()> {
         self.peer.disconnect(connection_id);
 
-        let mut projects_to_unregister = Vec::new();
+        let mut projects_to_unshare = Vec::new();
         let removed_user_id;
         {
             let mut store = self.store().await;
             let removed_connection = store.remove_connection(connection_id)?;
 
             for (project_id, project) in removed_connection.hosted_projects {
-                projects_to_unregister.push(project_id);
+                projects_to_unshare.push(project_id);
                 broadcast(connection_id, project.guests.keys().copied(), |conn_id| {
                     self.peer.send(
                         conn_id,
-                        proto::UnregisterProject {
+                        proto::UnshareProject {
                             project_id: project_id.to_proto(),
                         },
                     )
@@ -514,7 +513,7 @@ impl Server {
 
         self.update_user_contacts(removed_user_id).await.trace_err();
 
-        for project_id in projects_to_unregister {
+        for project_id in projects_to_unshare {
             self.app_state
                 .db
                 .unregister_project(project_id)
@@ -687,10 +686,10 @@ impl Server {
         }
     }
 
-    async fn register_project(
+    async fn share_project(
         self: Arc<Server>,
-        request: TypedEnvelope<proto::RegisterProject>,
-        response: Response<proto::RegisterProject>,
+        request: TypedEnvelope<proto::ShareProject>,
+        response: Response<proto::ShareProject>,
     ) -> Result<()> {
         let user_id = self
             .store()
@@ -699,44 +698,15 @@ impl Server {
         let project_id = self.app_state.db.register_project(user_id).await?;
         self.store()
             .await
-            .register_project(request.sender_id, project_id)?;
+            .share_project(request.sender_id, project_id)?;
 
-        response.send(proto::RegisterProjectResponse {
+        response.send(proto::ShareProjectResponse {
             project_id: project_id.to_proto(),
         })?;
 
         Ok(())
     }
 
-    async fn unregister_project(
-        self: Arc<Server>,
-        request: TypedEnvelope<proto::UnregisterProject>,
-        response: Response<proto::UnregisterProject>,
-    ) -> Result<()> {
-        let project_id = ProjectId::from_proto(request.payload.project_id);
-        let project = self
-            .store()
-            .await
-            .unregister_project(project_id, request.sender_id)?;
-        self.app_state.db.unregister_project(project_id).await?;
-
-        broadcast(
-            request.sender_id,
-            project.guests.keys().copied(),
-            |conn_id| {
-                self.peer.send(
-                    conn_id,
-                    proto::UnregisterProject {
-                        project_id: project_id.to_proto(),
-                    },
-                )
-            },
-        );
-        response.send(proto::Ack {})?;
-
-        Ok(())
-    }
-
     async fn unshare_project(
         self: Arc<Server>,
         message: TypedEnvelope<proto::UnshareProject>,
@@ -746,9 +716,11 @@ impl Server {
             .store()
             .await
             .unshare_project(project_id, message.sender_id)?;
-        broadcast(message.sender_id, project.guest_connection_ids, |conn_id| {
-            self.peer.send(conn_id, message.payload.clone())
-        });
+        broadcast(
+            message.sender_id,
+            project.guest_connection_ids(),
+            |conn_id| self.peer.send(conn_id, message.payload.clone()),
+        );
 
         Ok(())
     }

crates/collab/src/rpc/store.rs 🔗

@@ -96,10 +96,6 @@ pub struct LeftProject {
     pub remove_collaborator: bool,
 }
 
-pub struct UnsharedProject {
-    pub guest_connection_ids: Vec<ConnectionId>,
-}
-
 #[derive(Copy, Clone)]
 pub struct Metrics {
     pub connections: usize,
@@ -201,9 +197,9 @@ impl Store {
             self.leave_channel(connection_id, channel_id);
         }
 
-        // Unregister and leave all projects.
+        // Unshare and leave all projects.
         for project_id in connection_projects {
-            if let Ok(project) = self.unregister_project(project_id, connection_id) {
+            if let Ok(project) = self.unshare_project(project_id, connection_id) {
                 result.hosted_projects.insert(project_id, project);
             } else if self.leave_project(connection_id, project_id).is_ok() {
                 result.guest_project_ids.insert(project_id);
@@ -566,7 +562,7 @@ impl Store {
         }
     }
 
-    pub fn register_project(
+    pub fn share_project(
         &mut self,
         host_connection_id: ConnectionId,
         project_id: ProjectId,
@@ -595,40 +591,7 @@ impl Store {
         Ok(())
     }
 
-    pub fn update_project(
-        &mut self,
-        project_id: ProjectId,
-        worktrees: &[proto::WorktreeMetadata],
-        connection_id: ConnectionId,
-    ) -> Result<()> {
-        let project = self
-            .projects
-            .get_mut(&project_id)
-            .ok_or_else(|| anyhow!("no such project"))?;
-        if project.host_connection_id == connection_id {
-            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"))?
-        }
-    }
-
-    pub fn unregister_project(
+    pub fn unshare_project(
         &mut self,
         project_id: ProjectId,
         connection_id: ConnectionId,
@@ -657,35 +620,37 @@ impl Store {
         }
     }
 
-    pub fn unshare_project(
+    pub fn update_project(
         &mut self,
         project_id: ProjectId,
+        worktrees: &[proto::WorktreeMetadata],
         connection_id: ConnectionId,
-    ) -> Result<UnsharedProject> {
+    ) -> Result<()> {
         let project = self
             .projects
             .get_mut(&project_id)
             .ok_or_else(|| anyhow!("no such project"))?;
-        anyhow::ensure!(
-            project.host_connection_id == connection_id,
-            "no such project"
-        );
-
-        let guest_connection_ids = project.guest_connection_ids();
-        project.active_replica_ids.clear();
-        project.guests.clear();
-        project.language_servers.clear();
-        project.worktrees.clear();
-
-        for connection_id in &guest_connection_ids {
-            if let Some(connection) = self.connections.get_mut(connection_id) {
-                connection.projects.remove(&project_id);
+        if project.host_connection_id == connection_id {
+            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(UnsharedProject {
-            guest_connection_ids,
-        })
+            Ok(())
+        } else {
+            Err(anyhow!("no such project"))?
+        }
     }
 
     pub fn update_diagnostic_summary(

crates/contacts_panel/src/contacts_panel.rs 🔗

@@ -845,14 +845,6 @@ mod tests {
             .detach();
         });
 
-        let request = server.receive::<proto::RegisterProject>().await.unwrap();
-        server
-            .respond(
-                request.receipt(),
-                proto::RegisterProjectResponse { project_id: 200 },
-            )
-            .await;
-
         let get_users_request = server.receive::<proto::GetUsers>().await.unwrap();
         server
             .respond(

crates/project/src/project.rs 🔗

@@ -35,7 +35,6 @@ use lsp::{
 };
 use lsp_command::*;
 use parking_lot::Mutex;
-use postage::stream::Stream;
 use postage::watch;
 use rand::prelude::*;
 use search::SearchQuery;
@@ -153,10 +152,8 @@ enum WorktreeHandle {
 
 enum ProjectClientState {
     Local {
-        is_shared: bool,
-        remote_id_tx: watch::Sender<Option<u64>>,
-        remote_id_rx: watch::Receiver<Option<u64>>,
-        _maintain_remote_id: Task<Option<()>>,
+        remote_id: Option<u64>,
+        _detect_unshare: Task<Option<()>>,
     },
     Remote {
         sharing_has_stopped: bool,
@@ -382,7 +379,6 @@ 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_update_project);
-        client.add_model_message_handler(Self::handle_unregister_project);
         client.add_model_message_handler(Self::handle_unshare_project);
         client.add_model_message_handler(Self::handle_create_buffer_for_peer);
         client.add_model_message_handler(Self::handle_update_buffer_file);
@@ -423,24 +419,19 @@ impl Project {
         cx: &mut MutableAppContext,
     ) -> ModelHandle<Self> {
         cx.add_model(|cx: &mut ModelContext<Self>| {
-            let (remote_id_tx, remote_id_rx) = watch::channel();
-            let _maintain_remote_id = cx.spawn_weak({
-                let mut status_rx = client.clone().status();
-                move |this, mut cx| async move {
-                    while let Some(status) = status_rx.recv().await {
-                        let this = this.upgrade(&cx)?;
-                        if status.is_connected() {
-                            this.update(&mut cx, |this, cx| this.register(cx))
-                                .await
-                                .log_err()?;
-                        } else {
-                            this.update(&mut cx, |this, cx| this.unregister(cx))
-                                .await
-                                .log_err();
+            let mut status = client.status();
+            let _detect_unshare = cx.spawn_weak(move |this, mut cx| {
+                async move {
+                    let is_connected = status.next().await.map_or(false, |s| s.is_connected());
+                    // Even if we're initially connected, any future change of the status means we momentarily disconnected.
+                    if !is_connected || status.next().await.is_some() {
+                        if let Some(this) = this.upgrade(&cx) {
+                            let _ = this.update(&mut cx, |this, cx| this.unshare(cx));
                         }
                     }
-                    None
+                    Ok(())
                 }
+                .log_err()
             });
 
             let handle = cx.weak_handle();
@@ -456,10 +447,8 @@ impl Project {
                 loading_local_worktrees: Default::default(),
                 buffer_snapshots: Default::default(),
                 client_state: ProjectClientState::Local {
-                    is_shared: false,
-                    remote_id_tx,
-                    remote_id_rx,
-                    _maintain_remote_id,
+                    remote_id: None,
+                    _detect_unshare,
                 },
                 opened_buffer: watch::channel(),
                 client_subscriptions: Vec::new(),
@@ -762,113 +751,9 @@ impl Project {
         &self.fs
     }
 
-    fn unregister(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
-        self.unshare(cx).log_err();
-        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;
-
-                    // Unregistering the project causes the server to send out a
-                    // contact update removing this project from the host's list
-                    // of online projects. Wait until this contact update has been
-                    // processed before clearing out this project's remote id, so
-                    // that there is no moment where this project appears in the
-                    // contact metadata and *also* has no remote id.
-                    this.update(&mut cx, |this, cx| {
-                        this.user_store()
-                            .update(cx, |store, _| store.contact_updates_done())
-                    })
-                    .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.client_subscriptions.clear();
-                        this.metadata_changed(cx);
-                    });
-                    response.map(drop)
-                });
-            }
-        }
-        Task::ready(Ok(()))
-    }
-
-    fn register(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
-        if let ProjectClientState::Local { remote_id_rx, .. } = &self.client_state {
-            if remote_id_rx.borrow().is_some() {
-                return Task::ready(Ok(()));
-            }
-
-            let response = self.client.request(proto::RegisterProject {});
-            cx.spawn(|this, mut cx| async move {
-                let remote_id = response.await?.project_id;
-                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);
-                    }
-
-                    this.metadata_changed(cx);
-                    cx.emit(Event::RemoteIdChanged(Some(remote_id)));
-                    this.client_subscriptions
-                        .push(this.client.add_model_for_remote_entity(remote_id, cx));
-                    Ok(())
-                })
-            })
-        } else {
-            Task::ready(Err(anyhow!("can't register a remote project")))
-        }
-    }
-
     pub fn remote_id(&self) -> Option<u64> {
         match &self.client_state {
-            ProjectClientState::Local { remote_id_rx, .. } => *remote_id_rx.borrow(),
-            ProjectClientState::Remote { remote_id, .. } => Some(*remote_id),
-        }
-    }
-
-    pub fn next_remote_id(&self) -> impl Future<Output = u64> {
-        let mut id = None;
-        let mut watch = None;
-        match &self.client_state {
-            ProjectClientState::Local { remote_id_rx, .. } => watch = Some(remote_id_rx.clone()),
-            ProjectClientState::Remote { remote_id, .. } => id = Some(*remote_id),
-        }
-
-        async move {
-            if let Some(id) = id {
-                return id;
-            }
-            let mut watch = watch.unwrap();
-            loop {
-                let id = *watch.borrow();
-                if let Some(id) = id {
-                    return id;
-                }
-                watch.next().await;
-            }
-        }
-    }
-
-    pub fn shared_remote_id(&self) -> Option<u64> {
-        match &self.client_state {
-            ProjectClientState::Local {
-                remote_id_rx,
-                is_shared,
-                ..
-            } => {
-                if *is_shared {
-                    *remote_id_rx.borrow()
-                } else {
-                    None
-                }
-            }
+            ProjectClientState::Local { remote_id, .. } => *remote_id,
             ProjectClientState::Remote { remote_id, .. } => Some(*remote_id),
         }
     }
@@ -881,7 +766,7 @@ impl Project {
     }
 
     fn metadata_changed(&mut self, cx: &mut ModelContext<Self>) {
-        if let ProjectClientState::Local { remote_id_rx, .. } = &self.client_state {
+        if let ProjectClientState::Local { remote_id, .. } = &self.client_state {
             // Broadcast worktrees only if the project is online.
             let worktrees = self
                 .worktrees
@@ -892,7 +777,7 @@ impl Project {
                         .map(|worktree| worktree.read(cx).as_local().unwrap().metadata_proto())
                 })
                 .collect();
-            if let Some(project_id) = *remote_id_rx.borrow() {
+            if let Some(project_id) = *remote_id {
                 self.client
                     .send(proto::UpdateProject {
                         project_id,
@@ -1164,113 +1049,105 @@ impl Project {
         }
     }
 
-    pub fn share(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
-        let project_id;
-        if let ProjectClientState::Local {
-            remote_id_rx,
-            is_shared,
-            ..
-        } = &mut self.client_state
-        {
-            if *is_shared {
-                return Task::ready(Ok(()));
-            }
-            *is_shared = true;
-            if let Some(id) = *remote_id_rx.borrow() {
-                project_id = id;
-            } else {
-                return Task::ready(Err(anyhow!("project hasn't been registered")));
+    pub fn share(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<u64>> {
+        if let ProjectClientState::Local { remote_id, .. } = &mut self.client_state {
+            if let Some(remote_id) = remote_id {
+                return Task::ready(Ok(*remote_id));
             }
-        } else {
-            return Task::ready(Err(anyhow!("can't share a remote project")));
-        };
 
-        for open_buffer in self.opened_buffers.values_mut() {
-            match open_buffer {
-                OpenBuffer::Strong(_) => {}
-                OpenBuffer::Weak(buffer) => {
-                    if let Some(buffer) = buffer.upgrade(cx) {
-                        *open_buffer = OpenBuffer::Strong(buffer);
+            let response = self.client.request(proto::ShareProject {});
+            cx.spawn(|this, mut cx| async move {
+                let project_id = response.await?.project_id;
+                let mut worktree_share_tasks = Vec::new();
+                this.update(&mut cx, |this, cx| {
+                    if let ProjectClientState::Local { remote_id, .. } = &mut this.client_state {
+                        *remote_id = Some(project_id);
                     }
-                }
-                OpenBuffer::Operations(_) => unreachable!(),
-            }
-        }
 
-        for worktree_handle in self.worktrees.iter_mut() {
-            match worktree_handle {
-                WorktreeHandle::Strong(_) => {}
-                WorktreeHandle::Weak(worktree) => {
-                    if let Some(worktree) = worktree.upgrade(cx) {
-                        *worktree_handle = WorktreeHandle::Strong(worktree);
+                    for open_buffer in this.opened_buffers.values_mut() {
+                        match open_buffer {
+                            OpenBuffer::Strong(_) => {}
+                            OpenBuffer::Weak(buffer) => {
+                                if let Some(buffer) = buffer.upgrade(cx) {
+                                    *open_buffer = OpenBuffer::Strong(buffer);
+                                }
+                            }
+                            OpenBuffer::Operations(_) => unreachable!(),
+                        }
                     }
-                }
-            }
-        }
 
-        let mut tasks = Vec::new();
-        for worktree in self.worktrees(cx).collect::<Vec<_>>() {
-            worktree.update(cx, |worktree, cx| {
-                let worktree = worktree.as_local_mut().unwrap();
-                tasks.push(worktree.share(project_id, cx));
-            });
-        }
+                    for worktree_handle in this.worktrees.iter_mut() {
+                        match worktree_handle {
+                            WorktreeHandle::Strong(_) => {}
+                            WorktreeHandle::Weak(worktree) => {
+                                if let Some(worktree) = worktree.upgrade(cx) {
+                                    *worktree_handle = WorktreeHandle::Strong(worktree);
+                                }
+                            }
+                        }
+                    }
 
-        for (server_id, status) in &self.language_server_statuses {
-            self.client
-                .send(proto::StartLanguageServer {
-                    project_id,
-                    server: Some(proto::LanguageServer {
-                        id: *server_id as u64,
-                        name: status.name.clone(),
-                    }),
-                })
-                .log_err();
-        }
+                    for worktree in this.worktrees(cx).collect::<Vec<_>>() {
+                        worktree.update(cx, |worktree, cx| {
+                            let worktree = worktree.as_local_mut().unwrap();
+                            worktree_share_tasks.push(worktree.share(project_id, cx));
+                        });
+                    }
 
-        cx.spawn(|this, mut cx| async move {
-            for task in tasks {
-                task.await?;
-            }
-            this.update(&mut cx, |_, cx| cx.notify());
-            Ok(())
-        })
+                    for (server_id, status) in &this.language_server_statuses {
+                        this.client
+                            .send(proto::StartLanguageServer {
+                                project_id,
+                                server: Some(proto::LanguageServer {
+                                    id: *server_id as u64,
+                                    name: status.name.clone(),
+                                }),
+                            })
+                            .log_err();
+                    }
+
+                    this.client_subscriptions
+                        .push(this.client.add_model_for_remote_entity(project_id, cx));
+                    this.metadata_changed(cx);
+                    cx.emit(Event::RemoteIdChanged(Some(project_id)));
+                    cx.notify();
+                });
+
+                futures::future::try_join_all(worktree_share_tasks).await?;
+                Ok(project_id)
+            })
+        } else {
+            Task::ready(Err(anyhow!("can't share a remote project")))
+        }
     }
 
     pub fn unshare(&mut self, cx: &mut ModelContext<Self>) -> Result<()> {
-        if let ProjectClientState::Local {
-            is_shared,
-            remote_id_rx,
-            ..
-        } = &mut self.client_state
-        {
-            if !*is_shared {
-                return Ok(());
-            }
-
-            *is_shared = false;
-            self.collaborators.clear();
-            self.shared_buffers.clear();
-            for worktree_handle in self.worktrees.iter_mut() {
-                if let WorktreeHandle::Strong(worktree) = worktree_handle {
-                    let is_visible = worktree.update(cx, |worktree, _| {
-                        worktree.as_local_mut().unwrap().unshare();
-                        worktree.is_visible()
-                    });
-                    if !is_visible {
-                        *worktree_handle = WorktreeHandle::Weak(worktree.downgrade());
+        if let ProjectClientState::Local { remote_id, .. } = &mut self.client_state {
+            if let Some(project_id) = remote_id.take() {
+                self.collaborators.clear();
+                self.shared_buffers.clear();
+                self.client_subscriptions.clear();
+
+                for worktree_handle in self.worktrees.iter_mut() {
+                    if let WorktreeHandle::Strong(worktree) = worktree_handle {
+                        let is_visible = worktree.update(cx, |worktree, _| {
+                            worktree.as_local_mut().unwrap().unshare();
+                            worktree.is_visible()
+                        });
+                        if !is_visible {
+                            *worktree_handle = WorktreeHandle::Weak(worktree.downgrade());
+                        }
                     }
                 }
-            }
 
-            for open_buffer in self.opened_buffers.values_mut() {
-                if let OpenBuffer::Strong(buffer) = open_buffer {
-                    *open_buffer = OpenBuffer::Weak(buffer.downgrade());
+                for open_buffer in self.opened_buffers.values_mut() {
+                    if let OpenBuffer::Strong(buffer) = open_buffer {
+                        *open_buffer = OpenBuffer::Weak(buffer.downgrade());
+                    }
                 }
-            }
 
-            cx.notify();
-            if let Some(project_id) = *remote_id_rx.borrow() {
+                self.metadata_changed(cx);
+                cx.notify();
                 self.client.send(proto::UnshareProject { project_id })?;
             }
 
@@ -1750,7 +1627,7 @@ impl Project {
     ) -> Option<()> {
         match event {
             BufferEvent::Operation(operation) => {
-                if let Some(project_id) = self.shared_remote_id() {
+                if let Some(project_id) = self.remote_id() {
                     let request = self.client.request(proto::UpdateBuffer {
                         project_id,
                         buffer_id: buffer.read(cx).remote_id(),
@@ -2155,7 +2032,7 @@ impl Project {
                                 )
                                 .ok();
 
-                            if let Some(project_id) = this.shared_remote_id() {
+                            if let Some(project_id) = this.remote_id() {
                                 this.client
                                     .send(proto::StartLanguageServer {
                                         project_id,
@@ -2562,7 +2439,7 @@ impl Project {
         language_server_id: usize,
         event: proto::update_language_server::Variant,
     ) {
-        if let Some(project_id) = self.shared_remote_id() {
+        if let Some(project_id) = self.remote_id() {
             self.client
                 .send(proto::UpdateLanguageServer {
                     project_id,
@@ -4273,7 +4150,7 @@ impl Project {
 
     pub fn is_shared(&self) -> bool {
         match &self.client_state {
-            ProjectClientState::Local { is_shared, .. } => *is_shared,
+            ProjectClientState::Local { remote_id, .. } => remote_id.is_some(),
             ProjectClientState::Remote { .. } => false,
         }
     }
@@ -4310,7 +4187,7 @@ impl Project {
 
                         let project_id = project.update(&mut cx, |project, cx| {
                             project.add_worktree(&worktree, cx);
-                            project.shared_remote_id()
+                            project.remote_id()
                         });
 
                         if let Some(project_id) = project_id {
@@ -4439,7 +4316,7 @@ impl Project {
                             renamed_buffers.push((cx.handle(), old_path));
                         }
 
-                        if let Some(project_id) = self.shared_remote_id() {
+                        if let Some(project_id) = self.remote_id() {
                             self.client
                                 .send(proto::UpdateBufferFile {
                                     project_id,
@@ -4552,16 +4429,6 @@ impl Project {
 
     // RPC message handlers
 
-    async fn handle_unregister_project(
-        this: ModelHandle<Self>,
-        _: TypedEnvelope<proto::UnregisterProject>,
-        _: Arc<Client>,
-        mut cx: AsyncAppContext,
-    ) -> Result<()> {
-        this.update(&mut cx, |this, cx| this.disconnected_from_host(cx));
-        Ok(())
-    }
-
     async fn handle_unshare_project(
         this: ModelHandle<Self>,
         _: TypedEnvelope<proto::UnshareProject>,
@@ -5987,10 +5854,10 @@ impl Entity for Project {
         self.project_store.update(cx, ProjectStore::prune_projects);
 
         match &self.client_state {
-            ProjectClientState::Local { remote_id_rx, .. } => {
-                if let Some(project_id) = *remote_id_rx.borrow() {
+            ProjectClientState::Local { remote_id, .. } => {
+                if let Some(project_id) = *remote_id {
                     self.client
-                        .send(proto::UnregisterProject { project_id })
+                        .send(proto::UnshareProject { project_id })
                         .log_err();
                 }
             }

crates/rpc/proto/zed.proto 🔗

@@ -22,15 +22,14 @@ message Envelope {
         DeclineCall decline_call = 13;
         RoomUpdated room_updated = 14;
 
-        RegisterProject register_project = 15;
-        RegisterProjectResponse register_project_response = 16;
-        UnregisterProject unregister_project = 17;
+        ShareProject share_project = 15;
+        ShareProjectResponse share_project_response = 16;
+        UnshareProject unshare_project = 17;
         JoinProject join_project = 21;
         JoinProjectResponse join_project_response = 22;
         LeaveProject leave_project = 23;
         AddProjectCollaborator add_project_collaborator = 24;
         RemoveProjectCollaborator remove_project_collaborator = 25;
-        UnshareProject unshare_project = 26;
 
         GetDefinition get_definition = 27;
         GetDefinitionResponse get_definition_response = 28;
@@ -195,13 +194,13 @@ message RoomUpdated {
     Room room = 1;
 }
 
-message RegisterProject {}
+message ShareProject {}
 
-message RegisterProjectResponse {
+message ShareProjectResponse {
     uint64 project_id = 1;
 }
 
-message UnregisterProject {
+message UnshareProject {
     uint64 project_id = 1;
 }
 
@@ -285,10 +284,6 @@ message RemoveProjectCollaborator {
     uint32 peer_id = 2;
 }
 
-message UnshareProject {
-    uint64 project_id = 1;
-}
-
 message GetDefinition {
      uint64 project_id = 1;
      uint64 buffer_id = 2;

crates/rpc/src/proto.rs 🔗

@@ -141,10 +141,8 @@ messages!(
     (PrepareRename, Background),
     (PrepareRenameResponse, Background),
     (ProjectEntryResponse, Foreground),
-    (RegisterProjectResponse, Foreground),
     (RemoveContact, Foreground),
     (Ping, Foreground),
-    (RegisterProject, Foreground),
     (RegisterProjectActivity, Foreground),
     (ReloadBuffers, Foreground),
     (ReloadBuffersResponse, Foreground),
@@ -158,11 +156,12 @@ messages!(
     (SearchProjectResponse, Background),
     (SendChannelMessage, Foreground),
     (SendChannelMessageResponse, Foreground),
+    (ShareProject, Foreground),
+    (ShareProjectResponse, Foreground),
     (ShowContacts, Foreground),
     (StartLanguageServer, Foreground),
     (Test, Foreground),
     (Unfollow, Foreground),
-    (UnregisterProject, Foreground),
     (UnshareProject, Foreground),
     (UpdateBuffer, Foreground),
     (UpdateBufferFile, Foreground),
@@ -212,7 +211,6 @@ request_messages!(
     (Ping, Ack),
     (PerformRename, PerformRenameResponse),
     (PrepareRename, PrepareRenameResponse),
-    (RegisterProject, RegisterProjectResponse),
     (ReloadBuffers, ReloadBuffersResponse),
     (RequestContact, Ack),
     (RemoveContact, Ack),
@@ -221,8 +219,8 @@ request_messages!(
     (SaveBuffer, BufferSaved),
     (SearchProject, SearchProjectResponse),
     (SendChannelMessage, SendChannelMessageResponse),
+    (ShareProject, ShareProjectResponse),
     (Test, Test),
-    (UnregisterProject, Ack),
     (UpdateBuffer, Ack),
     (UpdateWorktree, Ack),
 );
@@ -263,7 +261,6 @@ entity_messages!(
     SearchProject,
     StartLanguageServer,
     Unfollow,
-    UnregisterProject,
     UnshareProject,
     UpdateBuffer,
     UpdateBufferFile,