@@ -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(
@@ -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();
}
}