diff --git a/crates/client/src/test.rs b/crates/client/src/test.rs index e471398b53..b91a943002 100644 --- a/crates/client/src/test.rs +++ b/crates/client/src/test.rs @@ -172,6 +172,10 @@ impl FakeHttpClient { handler: Box::new(move |req| Box::pin(handler(req))), }) } + + pub fn with_404_response() -> Arc { + Self::new(|_| async move { Ok(ServerResponse::new(404)) }) + } } impl fmt::Debug for FakeHttpClient { diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 7ba4478834..0a387487e1 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -37,7 +37,7 @@ pub struct UserStore { users: HashMap>, current_user: watch::Receiver>>, contacts: Arc<[Contact]>, - rpc: Arc, + client: Arc, http: Arc, _maintain_contacts: Task<()>, _maintain_current_user: Task<()>, @@ -50,11 +50,15 @@ impl Entity for UserStore { } impl UserStore { - pub fn new(rpc: Arc, http: Arc, cx: &mut ModelContext) -> Self { + pub fn new( + client: Arc, + http: Arc, + cx: &mut ModelContext, + ) -> Self { let (mut current_user_tx, current_user_rx) = watch::channel(); let (mut update_contacts_tx, mut update_contacts_rx) = watch::channel::>(); - let update_contacts_subscription = rpc.subscribe( + let update_contacts_subscription = client.subscribe( cx, move |_: &mut Self, msg: TypedEnvelope, _, _| { let _ = update_contacts_tx.blocking_send(Some(msg.payload)); @@ -65,7 +69,7 @@ impl UserStore { users: Default::default(), current_user: current_user_rx, contacts: Arc::from([]), - rpc: rpc.clone(), + client: client.clone(), http, _maintain_contacts: cx.spawn_weak(|this, mut cx| async move { let _subscription = update_contacts_subscription; @@ -78,11 +82,11 @@ impl UserStore { } }), _maintain_current_user: cx.spawn_weak(|this, mut cx| async move { - let mut status = rpc.status(); + let mut status = client.status(); while let Some(status) = status.recv().await { match status { Status::Connected { .. } => { - if let Some((this, user_id)) = this.upgrade(&cx).zip(rpc.user_id()) { + if let Some((this, user_id)) = this.upgrade(&cx).zip(client.user_id()) { let user = this .update(&mut cx, |this, cx| this.fetch_user(user_id, cx)) .log_err() @@ -139,7 +143,7 @@ impl UserStore { mut user_ids: Vec, cx: &mut ModelContext, ) -> Task> { - let rpc = self.rpc.clone(); + let rpc = self.client.clone(); let http = self.http.clone(); user_ids.retain(|id| !self.users.contains_key(id)); cx.spawn_weak(|this, mut cx| async move { diff --git a/crates/project/src/lib.rs b/crates/project/src/lib.rs index d1d48f49ca..b200db63dd 100644 --- a/crates/project/src/lib.rs +++ b/crates/project/src/lib.rs @@ -3,7 +3,7 @@ mod ignore; mod worktree; use anyhow::Result; -use client::Client; +use client::{Client, UserStore}; use futures::Future; use fuzzy::{PathMatch, PathMatchCandidate, PathMatchCandidateSet}; use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task}; @@ -23,6 +23,7 @@ pub struct Project { active_entry: Option, languages: Arc, client: Arc, + user_store: ModelHandle, fs: Arc, } @@ -44,13 +45,19 @@ pub struct ProjectEntry { } impl Project { - pub fn new(languages: Arc, rpc: Arc, fs: Arc) -> Self { + pub fn new( + languages: Arc, + client: Arc, + user_store: ModelHandle, + fs: Arc, + ) -> Self { Self { worktrees: Default::default(), active_worktree: None, active_entry: None, languages, - client: rpc, + client, + user_store, fs, } } @@ -72,11 +79,13 @@ impl Project { cx: &mut ModelContext, ) -> Task>> { let fs = self.fs.clone(); - let rpc = self.client.clone(); + let client = self.client.clone(); + let user_store = self.user_store.clone(); let languages = self.languages.clone(); let path = Arc::from(abs_path); cx.spawn(|this, mut cx| async move { - let worktree = Worktree::open_local(rpc, path, fs, languages, &mut cx).await?; + let worktree = + Worktree::open_local(client, user_store, path, fs, languages, &mut cx).await?; this.update(&mut cx, |this, cx| { this.add_worktree(worktree.clone(), cx); }); @@ -91,10 +100,12 @@ impl Project { ) -> Task>> { let rpc = self.client.clone(); let languages = self.languages.clone(); + let user_store = self.user_store.clone(); cx.spawn(|this, mut cx| async move { rpc.authenticate_and_connect(&cx).await?; let worktree = - Worktree::open_remote(rpc.clone(), remote_id, languages, &mut cx).await?; + Worktree::open_remote(rpc.clone(), remote_id, languages, user_store, &mut cx) + .await?; this.update(&mut cx, |this, cx| { cx.subscribe(&worktree, move |this, _, event, cx| match event { worktree::Event::Closed => { @@ -329,6 +340,7 @@ impl Entity for Project { #[cfg(test)] mod tests { use super::*; + use client::{http::ServerResponse, test::FakeHttpClient}; use fs::RealFs; use gpui::TestAppContext; use language::LanguageRegistry; @@ -434,7 +446,9 @@ mod tests { fn build_project(cx: &mut TestAppContext) -> ModelHandle { let languages = Arc::new(LanguageRegistry::new()); let fs = Arc::new(RealFs); - let rpc = client::Client::new(); - cx.add_model(|_| Project::new(languages, rpc, fs)) + let client = client::Client::new(); + let http_client = FakeHttpClient::new(|_| async move { Ok(ServerResponse::new(404)) }); + let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); + cx.add_model(|_| Project::new(languages, client, user_store, fs)) } } diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 08310f2fa2..f21abf06de 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -4,7 +4,7 @@ use super::{ }; use ::ignore::gitignore::{Gitignore, GitignoreBuilder}; use anyhow::{anyhow, Context, Result}; -use client::{proto, Client, PeerId, TypedEnvelope}; +use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore}; use clock::ReplicaId; use futures::{Stream, StreamExt}; use fuzzy::CharBag; @@ -64,7 +64,7 @@ pub enum Event { } pub struct Collaborator { - pub user_id: u64, + pub user: Arc, pub peer_id: PeerId, pub replica_id: ReplicaId, } @@ -76,7 +76,7 @@ impl Entity for Worktree { match self { Self::Local(tree) => { if let Some(worktree_id) = *tree.remote_id.borrow() { - let rpc = tree.rpc.clone(); + let rpc = tree.client.clone(); cx.spawn(|_| async move { if let Err(err) = rpc.send(proto::CloseWorktree { worktree_id }).await { log::error!("error closing worktree: {}", err); @@ -124,14 +124,15 @@ impl Entity for Worktree { impl Worktree { pub async fn open_local( - rpc: Arc, + client: Arc, + user_store: ModelHandle, path: impl Into>, fs: Arc, languages: Arc, cx: &mut AsyncAppContext, ) -> Result> { let (tree, scan_states_tx) = - LocalWorktree::new(rpc, path, fs.clone(), languages, cx).await?; + LocalWorktree::new(client, user_store, path, fs.clone(), languages, cx).await?; tree.update(cx, |tree, cx| { let tree = tree.as_local_mut().unwrap(); let abs_path = tree.snapshot.abs_path.clone(); @@ -148,18 +149,22 @@ impl Worktree { } pub async fn open_remote( - rpc: Arc, + client: Arc, id: u64, languages: Arc, + user_store: ModelHandle, cx: &mut AsyncAppContext, ) -> Result> { - let response = rpc.request(proto::JoinWorktree { worktree_id: id }).await?; - Worktree::remote(response, rpc, languages, cx).await + let response = client + .request(proto::JoinWorktree { worktree_id: id }) + .await?; + Worktree::remote(response, client, user_store, languages, cx).await } async fn remote( join_response: proto::JoinWorktreeResponse, - rpc: Arc, + client: Arc, + user_store: ModelHandle, languages: Arc, cx: &mut AsyncAppContext, ) -> Result> { @@ -204,6 +209,26 @@ impl Worktree { }) .await; + let user_ids = peers.iter().map(|peer| peer.user_id).collect(); + user_store + .update(cx, |user_store, cx| user_store.load_users(user_ids, cx)) + .await?; + let mut collaborators = HashMap::with_capacity(peers.len()); + for peer in &peers { + let peer_id = PeerId(peer.peer_id); + let user = user_store + .update(cx, |user_store, cx| user_store.fetch_user(peer.user_id, cx)) + .await?; + collaborators.insert( + peer_id, + Collaborator { + peer_id, + user, + replica_id: peer.replica_id as ReplicaId, + }, + ); + } + let worktree = cx.update(|cx| { cx.add_model(|cx: &mut ModelContext| { let snapshot = Snapshot { @@ -249,12 +274,12 @@ impl Worktree { } let _subscriptions = vec![ - rpc.subscribe_to_entity(remote_id, cx, Self::handle_add_peer), - rpc.subscribe_to_entity(remote_id, cx, Self::handle_remove_peer), - rpc.subscribe_to_entity(remote_id, cx, Self::handle_update), - rpc.subscribe_to_entity(remote_id, cx, Self::handle_update_buffer), - rpc.subscribe_to_entity(remote_id, cx, Self::handle_buffer_saved), - rpc.subscribe_to_entity(remote_id, cx, Self::handle_unshare), + client.subscribe_to_entity(remote_id, cx, Self::handle_add_peer), + client.subscribe_to_entity(remote_id, cx, Self::handle_remove_peer), + client.subscribe_to_entity(remote_id, cx, Self::handle_update), + client.subscribe_to_entity(remote_id, cx, Self::handle_update_buffer), + client.subscribe_to_entity(remote_id, cx, Self::handle_buffer_saved), + client.subscribe_to_entity(remote_id, cx, Self::handle_unshare), ]; Worktree::Remote(RemoteWorktree { @@ -263,24 +288,12 @@ impl Worktree { snapshot, snapshot_rx, updates_tx, - client: rpc.clone(), + client: client.clone(), open_buffers: Default::default(), - collaborators: peers - .into_iter() - .map(|p| { - let peer_id = PeerId(p.peer_id); - ( - peer_id, - Collaborator { - peer_id, - user_id: p.user_id, - replica_id: p.replica_id as ReplicaId, - }, - ) - }) - .collect(), + collaborators, queued_operations: Default::default(), languages, + user_store, _subscriptions, }) }) @@ -737,7 +750,7 @@ impl Worktree { Worktree::Local(worktree) => worktree .remote_id .borrow() - .map(|id| (worktree.rpc.clone(), id)), + .map(|id| (worktree.client.clone(), id)), Worktree::Remote(worktree) => Some((worktree.client.clone(), worktree.remote_id)), } { cx.spawn(|worktree, mut cx| async move { @@ -791,7 +804,8 @@ pub struct LocalWorktree { collaborators: HashMap, queued_operations: Vec<(u64, Operation)>, languages: Arc, - rpc: Arc, + client: Arc, + user_store: ModelHandle, fs: Arc, language_servers: HashMap>, } @@ -803,7 +817,8 @@ struct WorktreeConfig { impl LocalWorktree { async fn new( - rpc: Arc, + client: Arc, + user_store: ModelHandle, path: impl Into>, fs: Arc, languages: Arc, @@ -857,7 +872,7 @@ impl LocalWorktree { let (mut remote_id_tx, remote_id_rx) = watch::channel(); let _maintain_remote_id_task = cx.spawn_weak({ - let rpc = rpc.clone(); + let rpc = client.clone(); move |this, cx| { async move { let mut status = rpc.status(); @@ -905,7 +920,8 @@ impl LocalWorktree { queued_operations: Default::default(), collaborators: Default::default(), languages, - rpc, + client, + user_store, fs, language_servers: Default::default(), }; @@ -1106,7 +1122,7 @@ impl LocalWorktree { peer_id, Collaborator { peer_id, - user_id: peer.user_id, + user: todo!(), replica_id: peer.replica_id as ReplicaId, }, ); @@ -1267,7 +1283,7 @@ impl LocalWorktree { pub fn share(&mut self, cx: &mut ModelContext) -> Task> { let snapshot = self.snapshot(); let share_request = self.share_request(cx); - let rpc = self.rpc.clone(); + let rpc = self.client.clone(); cx.spawn(|this, mut cx| async move { let share_request = if let Some(request) = share_request.await { request @@ -1321,7 +1337,7 @@ impl LocalWorktree { pub fn unshare(&mut self, cx: &mut ModelContext) { self.share.take(); - let rpc = self.rpc.clone(); + let rpc = self.client.clone(); let remote_id = self.remote_id(); cx.foreground() .spawn( @@ -1399,6 +1415,7 @@ pub struct RemoteWorktree { open_buffers: HashMap, collaborators: HashMap, languages: Arc, + user_store: ModelHandle, queued_operations: Vec<(u64, Operation)>, _subscriptions: Vec, } @@ -1530,7 +1547,7 @@ impl RemoteWorktree { peer_id, Collaborator { peer_id, - user_id: peer.user_id, + user: todo!(), replica_id: peer.replica_id as ReplicaId, }, ); @@ -2009,7 +2026,7 @@ impl language::File for File { ) -> Task> { self.worktree.update(cx, |worktree, cx| match worktree { Worktree::Local(worktree) => { - let rpc = worktree.rpc.clone(); + let rpc = worktree.client.clone(); let worktree_id = *worktree.remote_id.borrow(); let save = worktree.save(self.path.clone(), text, cx); cx.background().spawn(async move { @@ -2976,7 +2993,7 @@ mod tests { use crate::fs::FakeFs; use anyhow::Result; use buffer::Point; - use client::test::FakeServer; + use client::test::{FakeHttpClient, FakeServer}; use fs::RealFs; use language::{tree_sitter_rust, LanguageServerConfig}; use language::{Diagnostic, LanguageConfig}; @@ -2992,7 +3009,7 @@ mod tests { use util::test::temp_tree; #[gpui::test] - async fn test_traversal(cx: gpui::TestAppContext) { + async fn test_traversal(mut cx: gpui::TestAppContext) { let fs = FakeFs::new(); fs.insert_tree( "/root", @@ -3006,8 +3023,13 @@ mod tests { ) .await; + let client = Client::new(); + let http_client = FakeHttpClient::with_404_response(); + let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); + let tree = Worktree::open_local( - Client::new(), + client, + user_store, Arc::from(Path::new("/root")), Arc::new(fs), Default::default(), @@ -3038,8 +3060,14 @@ mod tests { let dir = temp_tree(json!({ "file1": "the old contents", })); + + let client = Client::new(); + let http_client = FakeHttpClient::with_404_response(); + let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); + let tree = Worktree::open_local( - Client::new(), + client, + user_store, dir.path(), Arc::new(RealFs), Default::default(), @@ -3068,8 +3096,13 @@ mod tests { })); let file_path = dir.path().join("file1"); + let client = Client::new(); + let http_client = FakeHttpClient::with_404_response(); + let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); + let tree = Worktree::open_local( - Client::new(), + client, + user_store, file_path.clone(), Arc::new(RealFs), Default::default(), @@ -3113,9 +3146,12 @@ mod tests { let user_id = 5; let mut client = Client::new(); + let http_client = FakeHttpClient::with_404_response(); + let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); let server = FakeServer::for_client(user_id, &mut client, &cx).await; let tree = Worktree::open_local( client, + user_store.clone(), dir.path(), Arc::new(RealFs), Default::default(), @@ -3170,6 +3206,7 @@ mod tests { peers: Vec::new(), }, Client::new(), + user_store, Default::default(), &mut cx.to_async(), ) @@ -3262,7 +3299,7 @@ mod tests { } #[gpui::test] - async fn test_rescan_with_gitignore(cx: gpui::TestAppContext) { + async fn test_rescan_with_gitignore(mut cx: gpui::TestAppContext) { let dir = temp_tree(json!({ ".git": {}, ".gitignore": "ignored-dir\n", @@ -3274,8 +3311,13 @@ mod tests { } })); + let client = Client::new(); + let http_client = FakeHttpClient::with_404_response(); + let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); + let tree = Worktree::open_local( - Client::new(), + client, + user_store, dir.path(), Arc::new(RealFs), Default::default(), @@ -3313,6 +3355,8 @@ mod tests { let user_id = 100; let mut client = Client::new(); let server = FakeServer::for_client(user_id, &mut client, &cx).await; + let http_client = FakeHttpClient::with_404_response(); + let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); let fs = Arc::new(FakeFs::new()); fs.insert_tree( @@ -3330,6 +3374,7 @@ mod tests { let worktree = Worktree::open_local( client.clone(), + user_store, "/path/to/the-dir".as_ref(), fs, Default::default(), @@ -3376,8 +3421,13 @@ mod tests { "file2": "def", "file3": "ghi", })); + let client = Client::new(); + let http_client = FakeHttpClient::with_404_response(); + let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); + let tree = Worktree::open_local( - Client::new(), + client, + user_store, dir.path(), Arc::new(RealFs), Default::default(), @@ -3509,8 +3559,13 @@ mod tests { let initial_contents = "aaa\nbbbbb\nc\n"; let dir = temp_tree(json!({ "the-file": initial_contents })); + let client = Client::new(); + let http_client = FakeHttpClient::with_404_response(); + let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); + let tree = Worktree::open_local( - Client::new(), + client, + user_store, dir.path(), Arc::new(RealFs), Default::default(), @@ -3619,8 +3674,13 @@ mod tests { "b.rs": "const y: i32 = 1", })); + let client = Client::new(); + let http_client = FakeHttpClient::with_404_response(); + let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); + let tree = Worktree::open_local( - Client::new(), + client, + user_store, dir.path(), Arc::new(RealFs), Arc::new(languages), diff --git a/crates/project_panel/src/lib.rs b/crates/project_panel/src/lib.rs index 385b7dbca2..4d05ed317a 100644 --- a/crates/project_panel/src/lib.rs +++ b/crates/project_panel/src/lib.rs @@ -621,6 +621,7 @@ mod tests { Project::new( params.languages.clone(), params.client.clone(), + params.user_store.clone(), params.fs.clone(), ) }); diff --git a/crates/server/src/rpc.rs b/crates/server/src/rpc.rs index 9aa65bd8a1..9f4c724770 100644 --- a/crates/server/src/rpc.rs +++ b/crates/server/src/rpc.rs @@ -961,7 +961,7 @@ mod tests { // Connect to a server as 2 clients. let mut server = TestServer::start().await; let (client_a, _) = server.create_client(&mut cx_a, "user_a").await; - let (client_b, _) = server.create_client(&mut cx_b, "user_b").await; + let (client_b, user_store_b) = server.create_client(&mut cx_b, "user_b").await; cx_a.foreground().forbid_parking(); @@ -998,6 +998,7 @@ mod tests { client_b.clone(), worktree_id, lang_registry.clone(), + user_store_b, &mut cx_b.to_async(), ) .await @@ -1163,8 +1164,8 @@ mod tests { // Connect to a server as 3 clients. let mut server = TestServer::start().await; let (client_a, _) = server.create_client(&mut cx_a, "user_a").await; - let (client_b, _) = server.create_client(&mut cx_b, "user_b").await; - let (client_c, _) = server.create_client(&mut cx_c, "user_c").await; + let (client_b, user_store_b) = server.create_client(&mut cx_b, "user_b").await; + let (client_c, user_store_c) = server.create_client(&mut cx_c, "user_c").await; let fs = Arc::new(FakeFs::new()); @@ -1201,6 +1202,7 @@ mod tests { client_b.clone(), worktree_id, lang_registry.clone(), + user_store_b, &mut cx_b.to_async(), ) .await @@ -1209,6 +1211,7 @@ mod tests { client_c.clone(), worktree_id, lang_registry.clone(), + user_store_c, &mut cx_c.to_async(), ) .await @@ -1302,7 +1305,7 @@ mod tests { // Connect to a server as 2 clients. let mut server = TestServer::start().await; let (client_a, _) = server.create_client(&mut cx_a, "user_a").await; - let (client_b, _) = server.create_client(&mut cx_b, "user_b").await; + let (client_b, user_store_b) = server.create_client(&mut cx_b, "user_b").await; // Share a local worktree as client A let fs = Arc::new(FakeFs::new()); @@ -1337,6 +1340,7 @@ mod tests { client_b.clone(), worktree_id, lang_registry.clone(), + user_store_b, &mut cx_b.to_async(), ) .await @@ -1387,7 +1391,7 @@ mod tests { // Connect to a server as 2 clients. let mut server = TestServer::start().await; let (client_a, _) = server.create_client(&mut cx_a, "user_a").await; - let (client_b, _) = server.create_client(&mut cx_b, "user_b").await; + let (client_b, user_store_b) = server.create_client(&mut cx_b, "user_b").await; // Share a local worktree as client A let fs = Arc::new(FakeFs::new()); @@ -1421,6 +1425,7 @@ mod tests { client_b.clone(), worktree_id, lang_registry.clone(), + user_store_b, &mut cx_b.to_async(), ) .await @@ -1453,7 +1458,7 @@ mod tests { // Connect to a server as 2 clients. let mut server = TestServer::start().await; let (client_a, _) = server.create_client(&mut cx_a, "user_a").await; - let (client_b, _) = server.create_client(&mut cx_b, "user_b").await; + let (client_b, user_store_b) = server.create_client(&mut cx_b, "user_b").await; // Share a local worktree as client A let fs = Arc::new(FakeFs::new()); @@ -1487,6 +1492,7 @@ mod tests { client_b.clone(), worktree_id, lang_registry.clone(), + user_store_b, &mut cx_b.to_async(), ) .await @@ -1513,7 +1519,7 @@ mod tests { // Connect to a server as 2 clients. let mut server = TestServer::start().await; let (client_a, _) = server.create_client(&mut cx_a, "user_a").await; - let (client_b, _) = server.create_client(&mut cx_a, "user_b").await; + let (client_b, user_store_b) = server.create_client(&mut cx_a, "user_b").await; // Share a local worktree as client A let fs = Arc::new(FakeFs::new()); @@ -1548,6 +1554,7 @@ mod tests { client_b.clone(), worktree_id, lang_registry.clone(), + user_store_b, &mut cx_b.to_async(), ) .await @@ -1587,7 +1594,7 @@ mod tests { // Connect to a server as 2 clients. let mut server = TestServer::start().await; let (client_a, _) = server.create_client(&mut cx_a, "user_a").await; - let (client_b, _) = server.create_client(&mut cx_a, "user_b").await; + let (client_b, user_store_b) = server.create_client(&mut cx_a, "user_b").await; // Share a local worktree as client A let fs = Arc::new(FakeFs::new()); @@ -1656,6 +1663,7 @@ mod tests { client_b.clone(), worktree_id, lang_registry.clone(), + user_store_b, &mut cx_b.to_async(), ) .await @@ -2165,6 +2173,7 @@ mod tests { client_b.clone(), worktree_id, lang_registry.clone(), + user_store_b.clone(), &mut cx_b.to_async(), ) .await diff --git a/crates/workspace/src/lib.rs b/crates/workspace/src/lib.rs index 5901d694d3..ea75277b9c 100644 --- a/crates/workspace/src/lib.rs +++ b/crates/workspace/src/lib.rs @@ -348,6 +348,7 @@ impl Workspace { Project::new( params.languages.clone(), params.client.clone(), + params.user_store.clone(), params.fs.clone(), ) });