1// mod store;
2
3// use super::{
4// auth::process_auth_header,
5// db::{ChannelId, MessageId, UserId},
6// AppState,
7// };
8// use anyhow::anyhow;
9// use async_std::{
10// sync::{RwLock, RwLockReadGuard, RwLockWriteGuard},
11// task,
12// };
13// use async_tungstenite::{tungstenite::protocol::Role, WebSocketStream};
14// use collections::{HashMap, HashSet};
15// use futures::{channel::mpsc, future::BoxFuture, FutureExt, SinkExt, StreamExt};
16// use log::{as_debug, as_display};
17// use rpc::{
18// proto::{self, AnyTypedEnvelope, EntityMessage, EnvelopedMessage, RequestMessage},
19// Connection, ConnectionId, Peer, TypedEnvelope,
20// };
21// use sha1::{Digest as _, Sha1};
22// use std::{
23// any::TypeId,
24// future::Future,
25// marker::PhantomData,
26// ops::{Deref, DerefMut},
27// rc::Rc,
28// sync::Arc,
29// time::{Duration, Instant},
30// };
31// use store::{Store, Worktree};
32// use time::OffsetDateTime;
33// use util::ResultExt;
34
35// type MessageHandler = Box<
36// dyn Send
37// + Sync
38// + Fn(Arc<Server>, Box<dyn AnyTypedEnvelope>) -> BoxFuture<'static, tide::Result<()>>,
39// >;
40
41// pub struct Server {
42// peer: Arc<Peer>,
43// store: RwLock<Store>,
44// app_state: Arc<AppState>,
45// handlers: HashMap<TypeId, MessageHandler>,
46// notifications: Option<mpsc::UnboundedSender<()>>,
47// }
48
49// pub trait Executor: Send + Clone {
50// type Timer: Send + Future;
51// fn spawn_detached<F: 'static + Send + Future<Output = ()>>(&self, future: F);
52// fn timer(&self, duration: Duration) -> Self::Timer;
53// }
54
55// #[derive(Clone)]
56// pub struct RealExecutor;
57
58// const MESSAGE_COUNT_PER_PAGE: usize = 100;
59// const MAX_MESSAGE_LEN: usize = 1024;
60
61// struct StoreReadGuard<'a> {
62// guard: RwLockReadGuard<'a, Store>,
63// _not_send: PhantomData<Rc<()>>,
64// }
65
66// struct StoreWriteGuard<'a> {
67// guard: RwLockWriteGuard<'a, Store>,
68// _not_send: PhantomData<Rc<()>>,
69// }
70
71// impl Server {
72// pub fn new(
73// app_state: Arc<AppState>,
74// peer: Arc<Peer>,
75// notifications: Option<mpsc::UnboundedSender<()>>,
76// ) -> Arc<Self> {
77// let mut server = Self {
78// peer,
79// app_state,
80// store: Default::default(),
81// handlers: Default::default(),
82// notifications,
83// };
84
85// server
86// .add_request_handler(Server::ping)
87// .add_request_handler(Server::register_project)
88// .add_message_handler(Server::unregister_project)
89// .add_request_handler(Server::share_project)
90// .add_message_handler(Server::unshare_project)
91// .add_sync_request_handler(Server::join_project)
92// .add_message_handler(Server::leave_project)
93// .add_request_handler(Server::register_worktree)
94// .add_message_handler(Server::unregister_worktree)
95// .add_request_handler(Server::update_worktree)
96// .add_message_handler(Server::start_language_server)
97// .add_message_handler(Server::update_language_server)
98// .add_message_handler(Server::update_diagnostic_summary)
99// .add_request_handler(Server::forward_project_request::<proto::GetDefinition>)
100// .add_request_handler(Server::forward_project_request::<proto::GetReferences>)
101// .add_request_handler(Server::forward_project_request::<proto::SearchProject>)
102// .add_request_handler(Server::forward_project_request::<proto::GetDocumentHighlights>)
103// .add_request_handler(Server::forward_project_request::<proto::GetProjectSymbols>)
104// .add_request_handler(Server::forward_project_request::<proto::OpenBufferForSymbol>)
105// .add_request_handler(Server::forward_project_request::<proto::OpenBufferById>)
106// .add_request_handler(Server::forward_project_request::<proto::OpenBufferByPath>)
107// .add_request_handler(Server::forward_project_request::<proto::GetCompletions>)
108// .add_request_handler(
109// Server::forward_project_request::<proto::ApplyCompletionAdditionalEdits>,
110// )
111// .add_request_handler(Server::forward_project_request::<proto::GetCodeActions>)
112// .add_request_handler(Server::forward_project_request::<proto::ApplyCodeAction>)
113// .add_request_handler(Server::forward_project_request::<proto::PrepareRename>)
114// .add_request_handler(Server::forward_project_request::<proto::PerformRename>)
115// .add_request_handler(Server::forward_project_request::<proto::ReloadBuffers>)
116// .add_request_handler(Server::forward_project_request::<proto::FormatBuffers>)
117// .add_request_handler(Server::update_buffer)
118// .add_message_handler(Server::update_buffer_file)
119// .add_message_handler(Server::buffer_reloaded)
120// .add_message_handler(Server::buffer_saved)
121// .add_request_handler(Server::save_buffer)
122// .add_request_handler(Server::get_channels)
123// .add_request_handler(Server::get_users)
124// .add_request_handler(Server::join_channel)
125// .add_message_handler(Server::leave_channel)
126// .add_request_handler(Server::send_channel_message)
127// .add_request_handler(Server::follow)
128// .add_message_handler(Server::unfollow)
129// .add_message_handler(Server::update_followers)
130// .add_request_handler(Server::get_channel_messages);
131
132// Arc::new(server)
133// }
134
135// fn add_message_handler<F, Fut, M>(&mut self, handler: F) -> &mut Self
136// where
137// F: 'static + Send + Sync + Fn(Arc<Self>, TypedEnvelope<M>) -> Fut,
138// Fut: 'static + Send + Future<Output = tide::Result<()>>,
139// M: EnvelopedMessage,
140// {
141// let prev_handler = self.handlers.insert(
142// TypeId::of::<M>(),
143// Box::new(move |server, envelope| {
144// let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap();
145// (handler)(server, *envelope).boxed()
146// }),
147// );
148// if prev_handler.is_some() {
149// panic!("registered a handler for the same message twice");
150// }
151// self
152// }
153
154// fn add_request_handler<F, Fut, M>(&mut self, handler: F) -> &mut Self
155// where
156// F: 'static + Send + Sync + Fn(Arc<Self>, TypedEnvelope<M>) -> Fut,
157// Fut: 'static + Send + Future<Output = tide::Result<M::Response>>,
158// M: RequestMessage,
159// {
160// self.add_message_handler(move |server, envelope| {
161// let receipt = envelope.receipt();
162// let response = (handler)(server.clone(), envelope);
163// async move {
164// match response.await {
165// Ok(response) => {
166// server.peer.respond(receipt, response)?;
167// Ok(())
168// }
169// Err(error) => {
170// server.peer.respond_with_error(
171// receipt,
172// proto::Error {
173// message: error.to_string(),
174// },
175// )?;
176// Err(error)
177// }
178// }
179// }
180// })
181// }
182
183// /// Handle a request while holding a lock to the store. This is useful when we're registering
184// /// a connection but we want to respond on the connection before anybody else can send on it.
185// fn add_sync_request_handler<F, M>(&mut self, handler: F) -> &mut Self
186// where
187// F: 'static
188// + Send
189// + Sync
190// + Fn(Arc<Self>, &mut Store, TypedEnvelope<M>) -> tide::Result<M::Response>,
191// M: RequestMessage,
192// {
193// let handler = Arc::new(handler);
194// self.add_message_handler(move |server, envelope| {
195// let receipt = envelope.receipt();
196// let handler = handler.clone();
197// async move {
198// let mut store = server.store.write().await;
199// let response = (handler)(server.clone(), &mut *store, envelope);
200// match response {
201// Ok(response) => {
202// server.peer.respond(receipt, response)?;
203// Ok(())
204// }
205// Err(error) => {
206// server.peer.respond_with_error(
207// receipt,
208// proto::Error {
209// message: error.to_string(),
210// },
211// )?;
212// Err(error)
213// }
214// }
215// }
216// })
217// }
218
219// pub fn handle_connection<E: Executor>(
220// self: &Arc<Self>,
221// connection: Connection,
222// addr: String,
223// user_id: UserId,
224// mut send_connection_id: Option<mpsc::Sender<ConnectionId>>,
225// executor: E,
226// ) -> impl Future<Output = ()> {
227// let mut this = self.clone();
228// async move {
229// let (connection_id, handle_io, mut incoming_rx) = this
230// .peer
231// .add_connection(connection, {
232// let executor = executor.clone();
233// move |duration| {
234// let timer = executor.timer(duration);
235// async move {
236// timer.await;
237// }
238// }
239// })
240// .await;
241
242// if let Some(send_connection_id) = send_connection_id.as_mut() {
243// let _ = send_connection_id.send(connection_id).await;
244// }
245
246// {
247// let mut state = this.state_mut().await;
248// state.add_connection(connection_id, user_id);
249// this.update_contacts_for_users(&*state, &[user_id]);
250// }
251
252// let handle_io = handle_io.fuse();
253// futures::pin_mut!(handle_io);
254// loop {
255// let next_message = incoming_rx.next().fuse();
256// futures::pin_mut!(next_message);
257// futures::select_biased! {
258// result = handle_io => {
259// if let Err(err) = result {
260// log::error!("error handling rpc connection {:?} - {:?}", addr, err);
261// }
262// break;
263// }
264// message = next_message => {
265// if let Some(message) = message {
266// let start_time = Instant::now();
267// let type_name = message.payload_type_name();
268// log::info!(connection_id = connection_id.0, type_name = type_name; "rpc message received");
269// if let Some(handler) = this.handlers.get(&message.payload_type_id()) {
270// let notifications = this.notifications.clone();
271// let is_background = message.is_background();
272// let handle_message = (handler)(this.clone(), message);
273// let handle_message = async move {
274// if let Err(err) = handle_message.await {
275// log::error!(connection_id = connection_id.0, type = type_name, error = as_display!(err); "rpc message error");
276// } else {
277// log::info!(connection_id = connection_id.0, type = type_name, duration = as_debug!(start_time.elapsed()); "rpc message handled");
278// }
279// if let Some(mut notifications) = notifications {
280// let _ = notifications.send(()).await;
281// }
282// };
283// if is_background {
284// executor.spawn_detached(handle_message);
285// } else {
286// handle_message.await;
287// }
288// } else {
289// log::warn!("unhandled message: {}", type_name);
290// }
291// } else {
292// log::info!(address = as_debug!(addr); "rpc connection closed");
293// break;
294// }
295// }
296// }
297// }
298
299// if let Err(err) = this.sign_out(connection_id).await {
300// log::error!("error signing out connection {:?} - {:?}", addr, err);
301// }
302// }
303// }
304
305// async fn sign_out(self: &mut Arc<Self>, connection_id: ConnectionId) -> tide::Result<()> {
306// self.peer.disconnect(connection_id);
307// let mut state = self.state_mut().await;
308// let removed_connection = state.remove_connection(connection_id)?;
309
310// for (project_id, project) in removed_connection.hosted_projects {
311// if let Some(share) = project.share {
312// broadcast(
313// connection_id,
314// share.guests.keys().copied().collect(),
315// |conn_id| {
316// self.peer
317// .send(conn_id, proto::UnshareProject { project_id })
318// },
319// );
320// }
321// }
322
323// for (project_id, peer_ids) in removed_connection.guest_project_ids {
324// broadcast(connection_id, peer_ids, |conn_id| {
325// self.peer.send(
326// conn_id,
327// proto::RemoveProjectCollaborator {
328// project_id,
329// peer_id: connection_id.0,
330// },
331// )
332// });
333// }
334
335// self.update_contacts_for_users(&*state, removed_connection.contact_ids.iter());
336// Ok(())
337// }
338
339// async fn ping(self: Arc<Server>, _: TypedEnvelope<proto::Ping>) -> tide::Result<proto::Ack> {
340// Ok(proto::Ack {})
341// }
342
343// async fn register_project(
344// self: Arc<Server>,
345// request: TypedEnvelope<proto::RegisterProject>,
346// ) -> tide::Result<proto::RegisterProjectResponse> {
347// let project_id = {
348// let mut state = self.state_mut().await;
349// let user_id = state.user_id_for_connection(request.sender_id)?;
350// state.register_project(request.sender_id, user_id)
351// };
352// Ok(proto::RegisterProjectResponse { project_id })
353// }
354
355// async fn unregister_project(
356// self: Arc<Server>,
357// request: TypedEnvelope<proto::UnregisterProject>,
358// ) -> tide::Result<()> {
359// let mut state = self.state_mut().await;
360// let project = state.unregister_project(request.payload.project_id, request.sender_id)?;
361// self.update_contacts_for_users(&*state, &project.authorized_user_ids());
362// Ok(())
363// }
364
365// async fn share_project(
366// self: Arc<Server>,
367// request: TypedEnvelope<proto::ShareProject>,
368// ) -> tide::Result<proto::Ack> {
369// let mut state = self.state_mut().await;
370// let project = state.share_project(request.payload.project_id, request.sender_id)?;
371// self.update_contacts_for_users(&mut *state, &project.authorized_user_ids);
372// Ok(proto::Ack {})
373// }
374
375// async fn unshare_project(
376// self: Arc<Server>,
377// request: TypedEnvelope<proto::UnshareProject>,
378// ) -> tide::Result<()> {
379// let project_id = request.payload.project_id;
380// let mut state = self.state_mut().await;
381// let project = state.unshare_project(project_id, request.sender_id)?;
382// broadcast(request.sender_id, project.connection_ids, |conn_id| {
383// self.peer
384// .send(conn_id, proto::UnshareProject { project_id })
385// });
386// self.update_contacts_for_users(&mut *state, &project.authorized_user_ids);
387// Ok(())
388// }
389
390// fn join_project(
391// self: Arc<Server>,
392// state: &mut Store,
393// request: TypedEnvelope<proto::JoinProject>,
394// ) -> tide::Result<proto::JoinProjectResponse> {
395// let project_id = request.payload.project_id;
396
397// let user_id = state.user_id_for_connection(request.sender_id)?;
398// let (response, connection_ids, contact_user_ids) = state
399// .join_project(request.sender_id, user_id, project_id)
400// .and_then(|joined| {
401// let share = joined.project.share()?;
402// let peer_count = share.guests.len();
403// let mut collaborators = Vec::with_capacity(peer_count);
404// collaborators.push(proto::Collaborator {
405// peer_id: joined.project.host_connection_id.0,
406// replica_id: 0,
407// user_id: joined.project.host_user_id.to_proto(),
408// });
409// let worktrees = share
410// .worktrees
411// .iter()
412// .filter_map(|(id, shared_worktree)| {
413// let worktree = joined.project.worktrees.get(&id)?;
414// Some(proto::Worktree {
415// id: *id,
416// root_name: worktree.root_name.clone(),
417// entries: shared_worktree.entries.values().cloned().collect(),
418// diagnostic_summaries: shared_worktree
419// .diagnostic_summaries
420// .values()
421// .cloned()
422// .collect(),
423// visible: worktree.visible,
424// })
425// })
426// .collect();
427// for (peer_conn_id, (peer_replica_id, peer_user_id)) in &share.guests {
428// if *peer_conn_id != request.sender_id {
429// collaborators.push(proto::Collaborator {
430// peer_id: peer_conn_id.0,
431// replica_id: *peer_replica_id as u32,
432// user_id: peer_user_id.to_proto(),
433// });
434// }
435// }
436// let response = proto::JoinProjectResponse {
437// worktrees,
438// replica_id: joined.replica_id as u32,
439// collaborators,
440// language_servers: joined.project.language_servers.clone(),
441// };
442// let connection_ids = joined.project.connection_ids();
443// let contact_user_ids = joined.project.authorized_user_ids();
444// Ok((response, connection_ids, contact_user_ids))
445// })?;
446
447// broadcast(request.sender_id, connection_ids, |conn_id| {
448// self.peer.send(
449// conn_id,
450// proto::AddProjectCollaborator {
451// project_id,
452// collaborator: Some(proto::Collaborator {
453// peer_id: request.sender_id.0,
454// replica_id: response.replica_id,
455// user_id: user_id.to_proto(),
456// }),
457// },
458// )
459// });
460// self.update_contacts_for_users(state, &contact_user_ids);
461// Ok(response)
462// }
463
464// async fn leave_project(
465// self: Arc<Server>,
466// request: TypedEnvelope<proto::LeaveProject>,
467// ) -> tide::Result<()> {
468// let sender_id = request.sender_id;
469// let project_id = request.payload.project_id;
470// let mut state = self.state_mut().await;
471// let worktree = state.leave_project(sender_id, project_id)?;
472// broadcast(sender_id, worktree.connection_ids, |conn_id| {
473// self.peer.send(
474// conn_id,
475// proto::RemoveProjectCollaborator {
476// project_id,
477// peer_id: sender_id.0,
478// },
479// )
480// });
481// self.update_contacts_for_users(&*state, &worktree.authorized_user_ids);
482// Ok(())
483// }
484
485// async fn register_worktree(
486// self: Arc<Server>,
487// request: TypedEnvelope<proto::RegisterWorktree>,
488// ) -> tide::Result<proto::Ack> {
489// let mut contact_user_ids = HashSet::default();
490// for github_login in &request.payload.authorized_logins {
491// let contact_user_id = self.app_state.db.create_user(github_login, false).await?;
492// contact_user_ids.insert(contact_user_id);
493// }
494
495// let mut state = self.state_mut().await;
496// let host_user_id = state.user_id_for_connection(request.sender_id)?;
497// contact_user_ids.insert(host_user_id);
498
499// let contact_user_ids = contact_user_ids.into_iter().collect::<Vec<_>>();
500// let guest_connection_ids = state
501// .read_project(request.payload.project_id, request.sender_id)?
502// .guest_connection_ids();
503// state.register_worktree(
504// request.payload.project_id,
505// request.payload.worktree_id,
506// request.sender_id,
507// Worktree {
508// authorized_user_ids: contact_user_ids.clone(),
509// root_name: request.payload.root_name.clone(),
510// visible: request.payload.visible,
511// },
512// )?;
513
514// broadcast(request.sender_id, guest_connection_ids, |connection_id| {
515// self.peer
516// .forward_send(request.sender_id, connection_id, request.payload.clone())
517// });
518// self.update_contacts_for_users(&*state, &contact_user_ids);
519// Ok(proto::Ack {})
520// }
521
522// async fn unregister_worktree(
523// self: Arc<Server>,
524// request: TypedEnvelope<proto::UnregisterWorktree>,
525// ) -> tide::Result<()> {
526// let project_id = request.payload.project_id;
527// let worktree_id = request.payload.worktree_id;
528// let mut state = self.state_mut().await;
529// let (worktree, guest_connection_ids) =
530// state.unregister_worktree(project_id, worktree_id, request.sender_id)?;
531// broadcast(request.sender_id, guest_connection_ids, |conn_id| {
532// self.peer.send(
533// conn_id,
534// proto::UnregisterWorktree {
535// project_id,
536// worktree_id,
537// },
538// )
539// });
540// self.update_contacts_for_users(&*state, &worktree.authorized_user_ids);
541// Ok(())
542// }
543
544// async fn update_worktree(
545// self: Arc<Server>,
546// request: TypedEnvelope<proto::UpdateWorktree>,
547// ) -> tide::Result<proto::Ack> {
548// let connection_ids = self.state_mut().await.update_worktree(
549// request.sender_id,
550// request.payload.project_id,
551// request.payload.worktree_id,
552// &request.payload.removed_entries,
553// &request.payload.updated_entries,
554// )?;
555
556// broadcast(request.sender_id, connection_ids, |connection_id| {
557// self.peer
558// .forward_send(request.sender_id, connection_id, request.payload.clone())
559// });
560
561// Ok(proto::Ack {})
562// }
563
564// async fn update_diagnostic_summary(
565// self: Arc<Server>,
566// request: TypedEnvelope<proto::UpdateDiagnosticSummary>,
567// ) -> tide::Result<()> {
568// let summary = request
569// .payload
570// .summary
571// .clone()
572// .ok_or_else(|| anyhow!("invalid summary"))?;
573// let receiver_ids = self.state_mut().await.update_diagnostic_summary(
574// request.payload.project_id,
575// request.payload.worktree_id,
576// request.sender_id,
577// summary,
578// )?;
579
580// broadcast(request.sender_id, receiver_ids, |connection_id| {
581// self.peer
582// .forward_send(request.sender_id, connection_id, request.payload.clone())
583// });
584// Ok(())
585// }
586
587// async fn start_language_server(
588// self: Arc<Server>,
589// request: TypedEnvelope<proto::StartLanguageServer>,
590// ) -> tide::Result<()> {
591// let receiver_ids = self.state_mut().await.start_language_server(
592// request.payload.project_id,
593// request.sender_id,
594// request
595// .payload
596// .server
597// .clone()
598// .ok_or_else(|| anyhow!("invalid language server"))?,
599// )?;
600// broadcast(request.sender_id, receiver_ids, |connection_id| {
601// self.peer
602// .forward_send(request.sender_id, connection_id, request.payload.clone())
603// });
604// Ok(())
605// }
606
607// async fn update_language_server(
608// self: Arc<Server>,
609// request: TypedEnvelope<proto::UpdateLanguageServer>,
610// ) -> tide::Result<()> {
611// let receiver_ids = self
612// .state()
613// .await
614// .project_connection_ids(request.payload.project_id, request.sender_id)?;
615// broadcast(request.sender_id, receiver_ids, |connection_id| {
616// self.peer
617// .forward_send(request.sender_id, connection_id, request.payload.clone())
618// });
619// Ok(())
620// }
621
622// async fn forward_project_request<T>(
623// self: Arc<Server>,
624// request: TypedEnvelope<T>,
625// ) -> tide::Result<T::Response>
626// where
627// T: EntityMessage + RequestMessage,
628// {
629// let host_connection_id = self
630// .state()
631// .await
632// .read_project(request.payload.remote_entity_id(), request.sender_id)?
633// .host_connection_id;
634// Ok(self
635// .peer
636// .forward_request(request.sender_id, host_connection_id, request.payload)
637// .await?)
638// }
639
640// async fn save_buffer(
641// self: Arc<Server>,
642// request: TypedEnvelope<proto::SaveBuffer>,
643// ) -> tide::Result<proto::BufferSaved> {
644// let host = self
645// .state()
646// .await
647// .read_project(request.payload.project_id, request.sender_id)?
648// .host_connection_id;
649// let response = self
650// .peer
651// .forward_request(request.sender_id, host, request.payload.clone())
652// .await?;
653
654// let mut guests = self
655// .state()
656// .await
657// .read_project(request.payload.project_id, request.sender_id)?
658// .connection_ids();
659// guests.retain(|guest_connection_id| *guest_connection_id != request.sender_id);
660// broadcast(host, guests, |conn_id| {
661// self.peer.forward_send(host, conn_id, response.clone())
662// });
663
664// Ok(response)
665// }
666
667// async fn update_buffer(
668// self: Arc<Server>,
669// request: TypedEnvelope<proto::UpdateBuffer>,
670// ) -> tide::Result<proto::Ack> {
671// let receiver_ids = self
672// .state()
673// .await
674// .project_connection_ids(request.payload.project_id, request.sender_id)?;
675// broadcast(request.sender_id, receiver_ids, |connection_id| {
676// self.peer
677// .forward_send(request.sender_id, connection_id, request.payload.clone())
678// });
679// Ok(proto::Ack {})
680// }
681
682// async fn update_buffer_file(
683// self: Arc<Server>,
684// request: TypedEnvelope<proto::UpdateBufferFile>,
685// ) -> tide::Result<()> {
686// let receiver_ids = self
687// .state()
688// .await
689// .project_connection_ids(request.payload.project_id, request.sender_id)?;
690// broadcast(request.sender_id, receiver_ids, |connection_id| {
691// self.peer
692// .forward_send(request.sender_id, connection_id, request.payload.clone())
693// });
694// Ok(())
695// }
696
697// async fn buffer_reloaded(
698// self: Arc<Server>,
699// request: TypedEnvelope<proto::BufferReloaded>,
700// ) -> tide::Result<()> {
701// let receiver_ids = self
702// .state()
703// .await
704// .project_connection_ids(request.payload.project_id, request.sender_id)?;
705// broadcast(request.sender_id, receiver_ids, |connection_id| {
706// self.peer
707// .forward_send(request.sender_id, connection_id, request.payload.clone())
708// });
709// Ok(())
710// }
711
712// async fn buffer_saved(
713// self: Arc<Server>,
714// request: TypedEnvelope<proto::BufferSaved>,
715// ) -> tide::Result<()> {
716// let receiver_ids = self
717// .state()
718// .await
719// .project_connection_ids(request.payload.project_id, request.sender_id)?;
720// broadcast(request.sender_id, receiver_ids, |connection_id| {
721// self.peer
722// .forward_send(request.sender_id, connection_id, request.payload.clone())
723// });
724// Ok(())
725// }
726
727// async fn follow(
728// self: Arc<Self>,
729// request: TypedEnvelope<proto::Follow>,
730// ) -> tide::Result<proto::FollowResponse> {
731// let leader_id = ConnectionId(request.payload.leader_id);
732// let follower_id = request.sender_id;
733// if !self
734// .state()
735// .await
736// .project_connection_ids(request.payload.project_id, follower_id)?
737// .contains(&leader_id)
738// {
739// Err(anyhow!("no such peer"))?;
740// }
741// let mut response = self
742// .peer
743// .forward_request(request.sender_id, leader_id, request.payload)
744// .await?;
745// response
746// .views
747// .retain(|view| view.leader_id != Some(follower_id.0));
748// Ok(response)
749// }
750
751// async fn unfollow(
752// self: Arc<Self>,
753// request: TypedEnvelope<proto::Unfollow>,
754// ) -> tide::Result<()> {
755// let leader_id = ConnectionId(request.payload.leader_id);
756// if !self
757// .state()
758// .await
759// .project_connection_ids(request.payload.project_id, request.sender_id)?
760// .contains(&leader_id)
761// {
762// Err(anyhow!("no such peer"))?;
763// }
764// self.peer
765// .forward_send(request.sender_id, leader_id, request.payload)?;
766// Ok(())
767// }
768
769// async fn update_followers(
770// self: Arc<Self>,
771// request: TypedEnvelope<proto::UpdateFollowers>,
772// ) -> tide::Result<()> {
773// let connection_ids = self
774// .state()
775// .await
776// .project_connection_ids(request.payload.project_id, request.sender_id)?;
777// let leader_id = request
778// .payload
779// .variant
780// .as_ref()
781// .and_then(|variant| match variant {
782// proto::update_followers::Variant::CreateView(payload) => payload.leader_id,
783// proto::update_followers::Variant::UpdateView(payload) => payload.leader_id,
784// proto::update_followers::Variant::UpdateActiveView(payload) => payload.leader_id,
785// });
786// for follower_id in &request.payload.follower_ids {
787// let follower_id = ConnectionId(*follower_id);
788// if connection_ids.contains(&follower_id) && Some(follower_id.0) != leader_id {
789// self.peer
790// .forward_send(request.sender_id, follower_id, request.payload.clone())?;
791// }
792// }
793// Ok(())
794// }
795
796// async fn get_channels(
797// self: Arc<Server>,
798// request: TypedEnvelope<proto::GetChannels>,
799// ) -> tide::Result<proto::GetChannelsResponse> {
800// let user_id = self
801// .state()
802// .await
803// .user_id_for_connection(request.sender_id)?;
804// let channels = self.app_state.db.get_accessible_channels(user_id).await?;
805// Ok(proto::GetChannelsResponse {
806// channels: channels
807// .into_iter()
808// .map(|chan| proto::Channel {
809// id: chan.id.to_proto(),
810// name: chan.name,
811// })
812// .collect(),
813// })
814// }
815
816// async fn get_users(
817// self: Arc<Server>,
818// request: TypedEnvelope<proto::GetUsers>,
819// ) -> tide::Result<proto::GetUsersResponse> {
820// let user_ids = request
821// .payload
822// .user_ids
823// .into_iter()
824// .map(UserId::from_proto)
825// .collect();
826// let users = self
827// .app_state
828// .db
829// .get_users_by_ids(user_ids)
830// .await?
831// .into_iter()
832// .map(|user| proto::User {
833// id: user.id.to_proto(),
834// avatar_url: format!("https://github.com/{}.png?size=128", user.github_login),
835// github_login: user.github_login,
836// })
837// .collect();
838// Ok(proto::GetUsersResponse { users })
839// }
840
841// fn update_contacts_for_users<'a>(
842// self: &Arc<Self>,
843// state: &Store,
844// user_ids: impl IntoIterator<Item = &'a UserId>,
845// ) {
846// for user_id in user_ids {
847// let contacts = state.contacts_for_user(*user_id);
848// for connection_id in state.connection_ids_for_user(*user_id) {
849// self.peer
850// .send(
851// connection_id,
852// proto::UpdateContacts {
853// contacts: contacts.clone(),
854// },
855// )
856// .log_err();
857// }
858// }
859// }
860
861// async fn join_channel(
862// self: Arc<Self>,
863// request: TypedEnvelope<proto::JoinChannel>,
864// ) -> tide::Result<proto::JoinChannelResponse> {
865// let user_id = self
866// .state()
867// .await
868// .user_id_for_connection(request.sender_id)?;
869// let channel_id = ChannelId::from_proto(request.payload.channel_id);
870// if !self
871// .app_state
872// .db
873// .can_user_access_channel(user_id, channel_id)
874// .await?
875// {
876// Err(anyhow!("access denied"))?;
877// }
878
879// self.state_mut()
880// .await
881// .join_channel(request.sender_id, channel_id);
882// let messages = self
883// .app_state
884// .db
885// .get_channel_messages(channel_id, MESSAGE_COUNT_PER_PAGE, None)
886// .await?
887// .into_iter()
888// .map(|msg| proto::ChannelMessage {
889// id: msg.id.to_proto(),
890// body: msg.body,
891// timestamp: msg.sent_at.unix_timestamp() as u64,
892// sender_id: msg.sender_id.to_proto(),
893// nonce: Some(msg.nonce.as_u128().into()),
894// })
895// .collect::<Vec<_>>();
896// Ok(proto::JoinChannelResponse {
897// done: messages.len() < MESSAGE_COUNT_PER_PAGE,
898// messages,
899// })
900// }
901
902// async fn leave_channel(
903// self: Arc<Self>,
904// request: TypedEnvelope<proto::LeaveChannel>,
905// ) -> tide::Result<()> {
906// let user_id = self
907// .state()
908// .await
909// .user_id_for_connection(request.sender_id)?;
910// let channel_id = ChannelId::from_proto(request.payload.channel_id);
911// if !self
912// .app_state
913// .db
914// .can_user_access_channel(user_id, channel_id)
915// .await?
916// {
917// Err(anyhow!("access denied"))?;
918// }
919
920// self.state_mut()
921// .await
922// .leave_channel(request.sender_id, channel_id);
923
924// Ok(())
925// }
926
927// async fn send_channel_message(
928// self: Arc<Self>,
929// request: TypedEnvelope<proto::SendChannelMessage>,
930// ) -> tide::Result<proto::SendChannelMessageResponse> {
931// let channel_id = ChannelId::from_proto(request.payload.channel_id);
932// let user_id;
933// let connection_ids;
934// {
935// let state = self.state().await;
936// user_id = state.user_id_for_connection(request.sender_id)?;
937// connection_ids = state.channel_connection_ids(channel_id)?;
938// }
939
940// // Validate the message body.
941// let body = request.payload.body.trim().to_string();
942// if body.len() > MAX_MESSAGE_LEN {
943// return Err(anyhow!("message is too long"))?;
944// }
945// if body.is_empty() {
946// return Err(anyhow!("message can't be blank"))?;
947// }
948
949// let timestamp = OffsetDateTime::now_utc();
950// let nonce = request
951// .payload
952// .nonce
953// .ok_or_else(|| anyhow!("nonce can't be blank"))?;
954
955// let message_id = self
956// .app_state
957// .db
958// .create_channel_message(channel_id, user_id, &body, timestamp, nonce.clone().into())
959// .await?
960// .to_proto();
961// let message = proto::ChannelMessage {
962// sender_id: user_id.to_proto(),
963// id: message_id,
964// body,
965// timestamp: timestamp.unix_timestamp() as u64,
966// nonce: Some(nonce),
967// };
968// broadcast(request.sender_id, connection_ids, |conn_id| {
969// self.peer.send(
970// conn_id,
971// proto::ChannelMessageSent {
972// channel_id: channel_id.to_proto(),
973// message: Some(message.clone()),
974// },
975// )
976// });
977// Ok(proto::SendChannelMessageResponse {
978// message: Some(message),
979// })
980// }
981
982// async fn get_channel_messages(
983// self: Arc<Self>,
984// request: TypedEnvelope<proto::GetChannelMessages>,
985// ) -> tide::Result<proto::GetChannelMessagesResponse> {
986// let user_id = self
987// .state()
988// .await
989// .user_id_for_connection(request.sender_id)?;
990// let channel_id = ChannelId::from_proto(request.payload.channel_id);
991// if !self
992// .app_state
993// .db
994// .can_user_access_channel(user_id, channel_id)
995// .await?
996// {
997// Err(anyhow!("access denied"))?;
998// }
999
1000// let messages = self
1001// .app_state
1002// .db
1003// .get_channel_messages(
1004// channel_id,
1005// MESSAGE_COUNT_PER_PAGE,
1006// Some(MessageId::from_proto(request.payload.before_message_id)),
1007// )
1008// .await?
1009// .into_iter()
1010// .map(|msg| proto::ChannelMessage {
1011// id: msg.id.to_proto(),
1012// body: msg.body,
1013// timestamp: msg.sent_at.unix_timestamp() as u64,
1014// sender_id: msg.sender_id.to_proto(),
1015// nonce: Some(msg.nonce.as_u128().into()),
1016// })
1017// .collect::<Vec<_>>();
1018
1019// Ok(proto::GetChannelMessagesResponse {
1020// done: messages.len() < MESSAGE_COUNT_PER_PAGE,
1021// messages,
1022// })
1023// }
1024
1025// async fn state<'a>(self: &'a Arc<Self>) -> StoreReadGuard<'a> {
1026// #[cfg(test)]
1027// async_std::task::yield_now().await;
1028// let guard = self.store.read().await;
1029// #[cfg(test)]
1030// async_std::task::yield_now().await;
1031// StoreReadGuard {
1032// guard,
1033// _not_send: PhantomData,
1034// }
1035// }
1036
1037// async fn state_mut<'a>(self: &'a Arc<Self>) -> StoreWriteGuard<'a> {
1038// #[cfg(test)]
1039// async_std::task::yield_now().await;
1040// let guard = self.store.write().await;
1041// #[cfg(test)]
1042// async_std::task::yield_now().await;
1043// StoreWriteGuard {
1044// guard,
1045// _not_send: PhantomData,
1046// }
1047// }
1048// }
1049
1050// impl<'a> Deref for StoreReadGuard<'a> {
1051// type Target = Store;
1052
1053// fn deref(&self) -> &Self::Target {
1054// &*self.guard
1055// }
1056// }
1057
1058// impl<'a> Deref for StoreWriteGuard<'a> {
1059// type Target = Store;
1060
1061// fn deref(&self) -> &Self::Target {
1062// &*self.guard
1063// }
1064// }
1065
1066// impl<'a> DerefMut for StoreWriteGuard<'a> {
1067// fn deref_mut(&mut self) -> &mut Self::Target {
1068// &mut *self.guard
1069// }
1070// }
1071
1072// impl<'a> Drop for StoreWriteGuard<'a> {
1073// fn drop(&mut self) {
1074// #[cfg(test)]
1075// self.check_invariants();
1076// }
1077// }
1078
1079// impl Executor for RealExecutor {
1080// type Timer = Timer;
1081
1082// fn spawn_detached<F: 'static + Send + Future<Output = ()>>(&self, future: F) {
1083// task::spawn(future);
1084// }
1085
1086// fn timer(&self, duration: Duration) -> Self::Timer {
1087// Timer::after(duration)
1088// }
1089// }
1090
1091// fn broadcast<F>(sender_id: ConnectionId, receiver_ids: Vec<ConnectionId>, mut f: F)
1092// where
1093// F: FnMut(ConnectionId) -> anyhow::Result<()>,
1094// {
1095// for receiver_id in receiver_ids {
1096// if receiver_id != sender_id {
1097// f(receiver_id).log_err();
1098// }
1099// }
1100// }
1101
1102pub fn routes(peer: Arc<Peer>) -> Router<Body> {
1103 Router::new()
1104}
1105
1106// pub fn add_routes(app: &mut tide::Server<Arc<AppState>>, rpc: &Arc<Peer>) {
1107// let server = Server::new(app.state().clone(), rpc.clone(), None);
1108// app.at("/rpc").get(move |request: Request<Arc<AppState>>| {
1109// let server = server.clone();
1110// async move {
1111// const WEBSOCKET_GUID: &str = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
1112
1113// let connection_upgrade = header_contains_ignore_case(&request, CONNECTION, "upgrade");
1114// let upgrade_to_websocket = header_contains_ignore_case(&request, UPGRADE, "websocket");
1115// let upgrade_requested = connection_upgrade && upgrade_to_websocket;
1116// let client_protocol_version: Option<u32> = request
1117// .header("X-Zed-Protocol-Version")
1118// .and_then(|v| v.as_str().parse().ok());
1119
1120// if !upgrade_requested || client_protocol_version != Some(rpc::PROTOCOL_VERSION) {
1121// return Ok(Response::new(StatusCode::UpgradeRequired));
1122// }
1123
1124// let header = match request.header("Sec-Websocket-Key") {
1125// Some(h) => h.as_str(),
1126// None => return Err(anyhow!("expected sec-websocket-key"))?,
1127// };
1128
1129// let user_id = process_auth_header(&request).await?;
1130
1131// let mut response = Response::new(StatusCode::SwitchingProtocols);
1132// response.insert_header(UPGRADE, "websocket");
1133// response.insert_header(CONNECTION, "Upgrade");
1134// let hash = Sha1::new().chain(header).chain(WEBSOCKET_GUID).finalize();
1135// response.insert_header("Sec-Websocket-Accept", base64::encode(&hash[..]));
1136// response.insert_header("Sec-Websocket-Version", "13");
1137
1138// let http_res: &mut tide::http::Response = response.as_mut();
1139// let upgrade_receiver = http_res.recv_upgrade().await;
1140// let addr = request.remote().unwrap_or("unknown").to_string();
1141// task::spawn(async move {
1142// if let Some(stream) = upgrade_receiver.await {
1143// server
1144// .handle_connection(
1145// Connection::new(
1146// WebSocketStream::from_raw_socket(stream, Role::Server, None).await,
1147// ),
1148// addr,
1149// user_id,
1150// None,
1151// RealExecutor,
1152// )
1153// .await;
1154// }
1155// });
1156
1157// Ok(response)
1158// }
1159// });
1160// }
1161
1162// fn header_contains_ignore_case<T>(
1163// request: &tide::Request<T>,
1164// header_name: HeaderName,
1165// value: &str,
1166// ) -> bool {
1167// request
1168// .header(header_name)
1169// .map(|h| {
1170// h.as_str()
1171// .split(',')
1172// .any(|s| s.trim().eq_ignore_ascii_case(value.trim()))
1173// })
1174// .unwrap_or(false)
1175// }
1176
1177// #[cfg(test)]
1178// mod tests {
1179// use super::*;
1180// use crate::{
1181// db::{tests::TestDb, UserId},
1182// AppState, Config,
1183// };
1184// use ::rpc::Peer;
1185// use client::{
1186// self, test::FakeHttpClient, Channel, ChannelDetails, ChannelList, Client, Credentials,
1187// EstablishConnectionError, UserStore, RECEIVE_TIMEOUT,
1188// };
1189// use collections::BTreeMap;
1190// use editor::{
1191// self, ConfirmCodeAction, ConfirmCompletion, ConfirmRename, Editor, Input, Redo, Rename,
1192// ToOffset, ToggleCodeActions, Undo,
1193// };
1194// use gpui::{
1195// executor::{self, Deterministic},
1196// geometry::vector::vec2f,
1197// ModelHandle, TestAppContext, ViewHandle,
1198// };
1199// use language::{
1200// range_to_lsp, tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language,
1201// LanguageConfig, LanguageRegistry, OffsetRangeExt, Point, Rope,
1202// };
1203// use lsp::{self, FakeLanguageServer};
1204// use parking_lot::Mutex;
1205// use project::{
1206// fs::{FakeFs, Fs as _},
1207// search::SearchQuery,
1208// worktree::WorktreeHandle,
1209// DiagnosticSummary, Project, ProjectPath, WorktreeId,
1210// };
1211// use rand::prelude::*;
1212// use rpc::PeerId;
1213// use serde_json::json;
1214// use settings::Settings;
1215// use sqlx::types::time::OffsetDateTime;
1216// use std::{
1217// env,
1218// ops::Deref,
1219// path::{Path, PathBuf},
1220// rc::Rc,
1221// sync::{
1222// atomic::{AtomicBool, Ordering::SeqCst},
1223// Arc,
1224// },
1225// time::Duration,
1226// };
1227// use theme::ThemeRegistry;
1228// use workspace::{Item, SplitDirection, ToggleFollow, Workspace, WorkspaceParams};
1229
1230// #[cfg(test)]
1231// #[ctor::ctor]
1232// fn init_logger() {
1233// if std::env::var("RUST_LOG").is_ok() {
1234// env_logger::init();
1235// }
1236// }
1237
1238// #[gpui::test(iterations = 10)]
1239// async fn test_share_project(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
1240// let (window_b, _) = cx_b.add_window(|_| EmptyView);
1241// let lang_registry = Arc::new(LanguageRegistry::test());
1242// let fs = FakeFs::new(cx_a.background());
1243// cx_a.foreground().forbid_parking();
1244
1245// // Connect to a server as 2 clients.
1246// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
1247// let client_a = server.create_client(cx_a, "user_a").await;
1248// let client_b = server.create_client(cx_b, "user_b").await;
1249
1250// // Share a project as client A
1251// fs.insert_tree(
1252// "/a",
1253// json!({
1254// ".zed.toml": r#"collaborators = ["user_b"]"#,
1255// "a.txt": "a-contents",
1256// "b.txt": "b-contents",
1257// }),
1258// )
1259// .await;
1260// let project_a = cx_a.update(|cx| {
1261// Project::local(
1262// client_a.clone(),
1263// client_a.user_store.clone(),
1264// lang_registry.clone(),
1265// fs.clone(),
1266// cx,
1267// )
1268// });
1269// let (worktree_a, _) = project_a
1270// .update(cx_a, |p, cx| {
1271// p.find_or_create_local_worktree("/a", true, cx)
1272// })
1273// .await
1274// .unwrap();
1275// let worktree_id = worktree_a.read_with(cx_a, |tree, _| tree.id());
1276// worktree_a
1277// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
1278// .await;
1279// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
1280// project_a.update(cx_a, |p, cx| p.share(cx)).await.unwrap();
1281
1282// // Join that project as client B
1283// let project_b = Project::remote(
1284// project_id,
1285// client_b.clone(),
1286// client_b.user_store.clone(),
1287// lang_registry.clone(),
1288// fs.clone(),
1289// &mut cx_b.to_async(),
1290// )
1291// .await
1292// .unwrap();
1293
1294// let replica_id_b = project_b.read_with(cx_b, |project, _| {
1295// assert_eq!(
1296// project
1297// .collaborators()
1298// .get(&client_a.peer_id)
1299// .unwrap()
1300// .user
1301// .github_login,
1302// "user_a"
1303// );
1304// project.replica_id()
1305// });
1306// project_a
1307// .condition(&cx_a, |tree, _| {
1308// tree.collaborators()
1309// .get(&client_b.peer_id)
1310// .map_or(false, |collaborator| {
1311// collaborator.replica_id == replica_id_b
1312// && collaborator.user.github_login == "user_b"
1313// })
1314// })
1315// .await;
1316
1317// // Open the same file as client B and client A.
1318// let buffer_b = project_b
1319// .update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.txt"), cx))
1320// .await
1321// .unwrap();
1322// buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "b-contents"));
1323// project_a.read_with(cx_a, |project, cx| {
1324// assert!(project.has_open_buffer((worktree_id, "b.txt"), cx))
1325// });
1326// let buffer_a = project_a
1327// .update(cx_a, |p, cx| p.open_buffer((worktree_id, "b.txt"), cx))
1328// .await
1329// .unwrap();
1330
1331// let editor_b = cx_b.add_view(window_b, |cx| Editor::for_buffer(buffer_b, None, cx));
1332
1333// // TODO
1334// // // Create a selection set as client B and see that selection set as client A.
1335// // buffer_a
1336// // .condition(&cx_a, |buffer, _| buffer.selection_sets().count() == 1)
1337// // .await;
1338
1339// // Edit the buffer as client B and see that edit as client A.
1340// editor_b.update(cx_b, |editor, cx| {
1341// editor.handle_input(&Input("ok, ".into()), cx)
1342// });
1343// buffer_a
1344// .condition(&cx_a, |buffer, _| buffer.text() == "ok, b-contents")
1345// .await;
1346
1347// // TODO
1348// // // Remove the selection set as client B, see those selections disappear as client A.
1349// cx_b.update(move |_| drop(editor_b));
1350// // buffer_a
1351// // .condition(&cx_a, |buffer, _| buffer.selection_sets().count() == 0)
1352// // .await;
1353
1354// // Dropping the client B's project removes client B from client A's collaborators.
1355// cx_b.update(move |_| drop(project_b));
1356// project_a
1357// .condition(&cx_a, |project, _| project.collaborators().is_empty())
1358// .await;
1359// }
1360
1361// #[gpui::test(iterations = 10)]
1362// async fn test_unshare_project(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
1363// let lang_registry = Arc::new(LanguageRegistry::test());
1364// let fs = FakeFs::new(cx_a.background());
1365// cx_a.foreground().forbid_parking();
1366
1367// // Connect to a server as 2 clients.
1368// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
1369// let client_a = server.create_client(cx_a, "user_a").await;
1370// let client_b = server.create_client(cx_b, "user_b").await;
1371
1372// // Share a project as client A
1373// fs.insert_tree(
1374// "/a",
1375// json!({
1376// ".zed.toml": r#"collaborators = ["user_b"]"#,
1377// "a.txt": "a-contents",
1378// "b.txt": "b-contents",
1379// }),
1380// )
1381// .await;
1382// let project_a = cx_a.update(|cx| {
1383// Project::local(
1384// client_a.clone(),
1385// client_a.user_store.clone(),
1386// lang_registry.clone(),
1387// fs.clone(),
1388// cx,
1389// )
1390// });
1391// let (worktree_a, _) = project_a
1392// .update(cx_a, |p, cx| {
1393// p.find_or_create_local_worktree("/a", true, cx)
1394// })
1395// .await
1396// .unwrap();
1397// worktree_a
1398// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
1399// .await;
1400// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
1401// let worktree_id = worktree_a.read_with(cx_a, |tree, _| tree.id());
1402// project_a.update(cx_a, |p, cx| p.share(cx)).await.unwrap();
1403// assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
1404
1405// // Join that project as client B
1406// let project_b = Project::remote(
1407// project_id,
1408// client_b.clone(),
1409// client_b.user_store.clone(),
1410// lang_registry.clone(),
1411// fs.clone(),
1412// &mut cx_b.to_async(),
1413// )
1414// .await
1415// .unwrap();
1416// project_b
1417// .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
1418// .await
1419// .unwrap();
1420
1421// // Unshare the project as client A
1422// project_a.update(cx_a, |project, cx| project.unshare(cx));
1423// project_b
1424// .condition(cx_b, |project, _| project.is_read_only())
1425// .await;
1426// assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared()));
1427// cx_b.update(|_| {
1428// drop(project_b);
1429// });
1430
1431// // Share the project again and ensure guests can still join.
1432// project_a
1433// .update(cx_a, |project, cx| project.share(cx))
1434// .await
1435// .unwrap();
1436// assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
1437
1438// let project_b2 = Project::remote(
1439// project_id,
1440// client_b.clone(),
1441// client_b.user_store.clone(),
1442// lang_registry.clone(),
1443// fs.clone(),
1444// &mut cx_b.to_async(),
1445// )
1446// .await
1447// .unwrap();
1448// project_b2
1449// .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
1450// .await
1451// .unwrap();
1452// }
1453
1454// #[gpui::test(iterations = 10)]
1455// async fn test_host_disconnect(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
1456// let lang_registry = Arc::new(LanguageRegistry::test());
1457// let fs = FakeFs::new(cx_a.background());
1458// cx_a.foreground().forbid_parking();
1459
1460// // Connect to a server as 2 clients.
1461// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
1462// let client_a = server.create_client(cx_a, "user_a").await;
1463// let client_b = server.create_client(cx_b, "user_b").await;
1464
1465// // Share a project as client A
1466// fs.insert_tree(
1467// "/a",
1468// json!({
1469// ".zed.toml": r#"collaborators = ["user_b"]"#,
1470// "a.txt": "a-contents",
1471// "b.txt": "b-contents",
1472// }),
1473// )
1474// .await;
1475// let project_a = cx_a.update(|cx| {
1476// Project::local(
1477// client_a.clone(),
1478// client_a.user_store.clone(),
1479// lang_registry.clone(),
1480// fs.clone(),
1481// cx,
1482// )
1483// });
1484// let (worktree_a, _) = project_a
1485// .update(cx_a, |p, cx| {
1486// p.find_or_create_local_worktree("/a", true, cx)
1487// })
1488// .await
1489// .unwrap();
1490// worktree_a
1491// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
1492// .await;
1493// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
1494// let worktree_id = worktree_a.read_with(cx_a, |tree, _| tree.id());
1495// project_a.update(cx_a, |p, cx| p.share(cx)).await.unwrap();
1496// assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
1497
1498// // Join that project as client B
1499// let project_b = Project::remote(
1500// project_id,
1501// client_b.clone(),
1502// client_b.user_store.clone(),
1503// lang_registry.clone(),
1504// fs.clone(),
1505// &mut cx_b.to_async(),
1506// )
1507// .await
1508// .unwrap();
1509// project_b
1510// .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
1511// .await
1512// .unwrap();
1513
1514// // Drop client A's connection. Collaborators should disappear and the project should not be shown as shared.
1515// server.disconnect_client(client_a.current_user_id(cx_a));
1516// cx_a.foreground().advance_clock(rpc::RECEIVE_TIMEOUT);
1517// project_a
1518// .condition(cx_a, |project, _| project.collaborators().is_empty())
1519// .await;
1520// project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
1521// project_b
1522// .condition(cx_b, |project, _| project.is_read_only())
1523// .await;
1524// assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared()));
1525// cx_b.update(|_| {
1526// drop(project_b);
1527// });
1528
1529// // Await reconnection
1530// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
1531
1532// // Share the project again and ensure guests can still join.
1533// project_a
1534// .update(cx_a, |project, cx| project.share(cx))
1535// .await
1536// .unwrap();
1537// assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
1538
1539// let project_b2 = Project::remote(
1540// project_id,
1541// client_b.clone(),
1542// client_b.user_store.clone(),
1543// lang_registry.clone(),
1544// fs.clone(),
1545// &mut cx_b.to_async(),
1546// )
1547// .await
1548// .unwrap();
1549// project_b2
1550// .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
1551// .await
1552// .unwrap();
1553// }
1554
1555// #[gpui::test(iterations = 10)]
1556// async fn test_propagate_saves_and_fs_changes(
1557// cx_a: &mut TestAppContext,
1558// cx_b: &mut TestAppContext,
1559// cx_c: &mut TestAppContext,
1560// ) {
1561// let lang_registry = Arc::new(LanguageRegistry::test());
1562// let fs = FakeFs::new(cx_a.background());
1563// cx_a.foreground().forbid_parking();
1564
1565// // Connect to a server as 3 clients.
1566// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
1567// let client_a = server.create_client(cx_a, "user_a").await;
1568// let client_b = server.create_client(cx_b, "user_b").await;
1569// let client_c = server.create_client(cx_c, "user_c").await;
1570
1571// // Share a worktree as client A.
1572// fs.insert_tree(
1573// "/a",
1574// json!({
1575// ".zed.toml": r#"collaborators = ["user_b", "user_c"]"#,
1576// "file1": "",
1577// "file2": ""
1578// }),
1579// )
1580// .await;
1581// let project_a = cx_a.update(|cx| {
1582// Project::local(
1583// client_a.clone(),
1584// client_a.user_store.clone(),
1585// lang_registry.clone(),
1586// fs.clone(),
1587// cx,
1588// )
1589// });
1590// let (worktree_a, _) = project_a
1591// .update(cx_a, |p, cx| {
1592// p.find_or_create_local_worktree("/a", true, cx)
1593// })
1594// .await
1595// .unwrap();
1596// worktree_a
1597// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
1598// .await;
1599// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
1600// let worktree_id = worktree_a.read_with(cx_a, |tree, _| tree.id());
1601// project_a.update(cx_a, |p, cx| p.share(cx)).await.unwrap();
1602
1603// // Join that worktree as clients B and C.
1604// let project_b = Project::remote(
1605// project_id,
1606// client_b.clone(),
1607// client_b.user_store.clone(),
1608// lang_registry.clone(),
1609// fs.clone(),
1610// &mut cx_b.to_async(),
1611// )
1612// .await
1613// .unwrap();
1614// let project_c = Project::remote(
1615// project_id,
1616// client_c.clone(),
1617// client_c.user_store.clone(),
1618// lang_registry.clone(),
1619// fs.clone(),
1620// &mut cx_c.to_async(),
1621// )
1622// .await
1623// .unwrap();
1624// let worktree_b = project_b.read_with(cx_b, |p, cx| p.worktrees(cx).next().unwrap());
1625// let worktree_c = project_c.read_with(cx_c, |p, cx| p.worktrees(cx).next().unwrap());
1626
1627// // Open and edit a buffer as both guests B and C.
1628// let buffer_b = project_b
1629// .update(cx_b, |p, cx| p.open_buffer((worktree_id, "file1"), cx))
1630// .await
1631// .unwrap();
1632// let buffer_c = project_c
1633// .update(cx_c, |p, cx| p.open_buffer((worktree_id, "file1"), cx))
1634// .await
1635// .unwrap();
1636// buffer_b.update(cx_b, |buf, cx| buf.edit([0..0], "i-am-b, ", cx));
1637// buffer_c.update(cx_c, |buf, cx| buf.edit([0..0], "i-am-c, ", cx));
1638
1639// // Open and edit that buffer as the host.
1640// let buffer_a = project_a
1641// .update(cx_a, |p, cx| p.open_buffer((worktree_id, "file1"), cx))
1642// .await
1643// .unwrap();
1644
1645// buffer_a
1646// .condition(cx_a, |buf, _| buf.text() == "i-am-c, i-am-b, ")
1647// .await;
1648// buffer_a.update(cx_a, |buf, cx| {
1649// buf.edit([buf.len()..buf.len()], "i-am-a", cx)
1650// });
1651
1652// // Wait for edits to propagate
1653// buffer_a
1654// .condition(cx_a, |buf, _| buf.text() == "i-am-c, i-am-b, i-am-a")
1655// .await;
1656// buffer_b
1657// .condition(cx_b, |buf, _| buf.text() == "i-am-c, i-am-b, i-am-a")
1658// .await;
1659// buffer_c
1660// .condition(cx_c, |buf, _| buf.text() == "i-am-c, i-am-b, i-am-a")
1661// .await;
1662
1663// // Edit the buffer as the host and concurrently save as guest B.
1664// let save_b = buffer_b.update(cx_b, |buf, cx| buf.save(cx));
1665// buffer_a.update(cx_a, |buf, cx| buf.edit([0..0], "hi-a, ", cx));
1666// save_b.await.unwrap();
1667// assert_eq!(
1668// fs.load("/a/file1".as_ref()).await.unwrap(),
1669// "hi-a, i-am-c, i-am-b, i-am-a"
1670// );
1671// buffer_a.read_with(cx_a, |buf, _| assert!(!buf.is_dirty()));
1672// buffer_b.read_with(cx_b, |buf, _| assert!(!buf.is_dirty()));
1673// buffer_c.condition(cx_c, |buf, _| !buf.is_dirty()).await;
1674
1675// worktree_a.flush_fs_events(cx_a).await;
1676
1677// // Make changes on host's file system, see those changes on guest worktrees.
1678// fs.rename(
1679// "/a/file1".as_ref(),
1680// "/a/file1-renamed".as_ref(),
1681// Default::default(),
1682// )
1683// .await
1684// .unwrap();
1685
1686// fs.rename("/a/file2".as_ref(), "/a/file3".as_ref(), Default::default())
1687// .await
1688// .unwrap();
1689// fs.insert_file(Path::new("/a/file4"), "4".into()).await;
1690
1691// worktree_a
1692// .condition(&cx_a, |tree, _| {
1693// tree.paths()
1694// .map(|p| p.to_string_lossy())
1695// .collect::<Vec<_>>()
1696// == [".zed.toml", "file1-renamed", "file3", "file4"]
1697// })
1698// .await;
1699// worktree_b
1700// .condition(&cx_b, |tree, _| {
1701// tree.paths()
1702// .map(|p| p.to_string_lossy())
1703// .collect::<Vec<_>>()
1704// == [".zed.toml", "file1-renamed", "file3", "file4"]
1705// })
1706// .await;
1707// worktree_c
1708// .condition(&cx_c, |tree, _| {
1709// tree.paths()
1710// .map(|p| p.to_string_lossy())
1711// .collect::<Vec<_>>()
1712// == [".zed.toml", "file1-renamed", "file3", "file4"]
1713// })
1714// .await;
1715
1716// // Ensure buffer files are updated as well.
1717// buffer_a
1718// .condition(&cx_a, |buf, _| {
1719// buf.file().unwrap().path().to_str() == Some("file1-renamed")
1720// })
1721// .await;
1722// buffer_b
1723// .condition(&cx_b, |buf, _| {
1724// buf.file().unwrap().path().to_str() == Some("file1-renamed")
1725// })
1726// .await;
1727// buffer_c
1728// .condition(&cx_c, |buf, _| {
1729// buf.file().unwrap().path().to_str() == Some("file1-renamed")
1730// })
1731// .await;
1732// }
1733
1734// #[gpui::test(iterations = 10)]
1735// async fn test_buffer_conflict_after_save(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
1736// cx_a.foreground().forbid_parking();
1737// let lang_registry = Arc::new(LanguageRegistry::test());
1738// let fs = FakeFs::new(cx_a.background());
1739
1740// // Connect to a server as 2 clients.
1741// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
1742// let client_a = server.create_client(cx_a, "user_a").await;
1743// let client_b = server.create_client(cx_b, "user_b").await;
1744
1745// // Share a project as client A
1746// fs.insert_tree(
1747// "/dir",
1748// json!({
1749// ".zed.toml": r#"collaborators = ["user_b", "user_c"]"#,
1750// "a.txt": "a-contents",
1751// }),
1752// )
1753// .await;
1754
1755// let project_a = cx_a.update(|cx| {
1756// Project::local(
1757// client_a.clone(),
1758// client_a.user_store.clone(),
1759// lang_registry.clone(),
1760// fs.clone(),
1761// cx,
1762// )
1763// });
1764// let (worktree_a, _) = project_a
1765// .update(cx_a, |p, cx| {
1766// p.find_or_create_local_worktree("/dir", true, cx)
1767// })
1768// .await
1769// .unwrap();
1770// worktree_a
1771// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
1772// .await;
1773// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
1774// let worktree_id = worktree_a.read_with(cx_a, |tree, _| tree.id());
1775// project_a.update(cx_a, |p, cx| p.share(cx)).await.unwrap();
1776
1777// // Join that project as client B
1778// let project_b = Project::remote(
1779// project_id,
1780// client_b.clone(),
1781// client_b.user_store.clone(),
1782// lang_registry.clone(),
1783// fs.clone(),
1784// &mut cx_b.to_async(),
1785// )
1786// .await
1787// .unwrap();
1788
1789// // Open a buffer as client B
1790// let buffer_b = project_b
1791// .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
1792// .await
1793// .unwrap();
1794
1795// buffer_b.update(cx_b, |buf, cx| buf.edit([0..0], "world ", cx));
1796// buffer_b.read_with(cx_b, |buf, _| {
1797// assert!(buf.is_dirty());
1798// assert!(!buf.has_conflict());
1799// });
1800
1801// buffer_b.update(cx_b, |buf, cx| buf.save(cx)).await.unwrap();
1802// buffer_b
1803// .condition(&cx_b, |buffer_b, _| !buffer_b.is_dirty())
1804// .await;
1805// buffer_b.read_with(cx_b, |buf, _| {
1806// assert!(!buf.has_conflict());
1807// });
1808
1809// buffer_b.update(cx_b, |buf, cx| buf.edit([0..0], "hello ", cx));
1810// buffer_b.read_with(cx_b, |buf, _| {
1811// assert!(buf.is_dirty());
1812// assert!(!buf.has_conflict());
1813// });
1814// }
1815
1816// #[gpui::test(iterations = 10)]
1817// async fn test_buffer_reloading(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
1818// cx_a.foreground().forbid_parking();
1819// let lang_registry = Arc::new(LanguageRegistry::test());
1820// let fs = FakeFs::new(cx_a.background());
1821
1822// // Connect to a server as 2 clients.
1823// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
1824// let client_a = server.create_client(cx_a, "user_a").await;
1825// let client_b = server.create_client(cx_b, "user_b").await;
1826
1827// // Share a project as client A
1828// fs.insert_tree(
1829// "/dir",
1830// json!({
1831// ".zed.toml": r#"collaborators = ["user_b", "user_c"]"#,
1832// "a.txt": "a-contents",
1833// }),
1834// )
1835// .await;
1836
1837// let project_a = cx_a.update(|cx| {
1838// Project::local(
1839// client_a.clone(),
1840// client_a.user_store.clone(),
1841// lang_registry.clone(),
1842// fs.clone(),
1843// cx,
1844// )
1845// });
1846// let (worktree_a, _) = project_a
1847// .update(cx_a, |p, cx| {
1848// p.find_or_create_local_worktree("/dir", true, cx)
1849// })
1850// .await
1851// .unwrap();
1852// worktree_a
1853// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
1854// .await;
1855// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
1856// let worktree_id = worktree_a.read_with(cx_a, |tree, _| tree.id());
1857// project_a.update(cx_a, |p, cx| p.share(cx)).await.unwrap();
1858
1859// // Join that project as client B
1860// let project_b = Project::remote(
1861// project_id,
1862// client_b.clone(),
1863// client_b.user_store.clone(),
1864// lang_registry.clone(),
1865// fs.clone(),
1866// &mut cx_b.to_async(),
1867// )
1868// .await
1869// .unwrap();
1870// let _worktree_b = project_b.update(cx_b, |p, cx| p.worktrees(cx).next().unwrap());
1871
1872// // Open a buffer as client B
1873// let buffer_b = project_b
1874// .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
1875// .await
1876// .unwrap();
1877// buffer_b.read_with(cx_b, |buf, _| {
1878// assert!(!buf.is_dirty());
1879// assert!(!buf.has_conflict());
1880// });
1881
1882// fs.save(Path::new("/dir/a.txt"), &"new contents".into())
1883// .await
1884// .unwrap();
1885// buffer_b
1886// .condition(&cx_b, |buf, _| {
1887// buf.text() == "new contents" && !buf.is_dirty()
1888// })
1889// .await;
1890// buffer_b.read_with(cx_b, |buf, _| {
1891// assert!(!buf.has_conflict());
1892// });
1893// }
1894
1895// #[gpui::test(iterations = 10)]
1896// async fn test_editing_while_guest_opens_buffer(
1897// cx_a: &mut TestAppContext,
1898// cx_b: &mut TestAppContext,
1899// ) {
1900// cx_a.foreground().forbid_parking();
1901// let lang_registry = Arc::new(LanguageRegistry::test());
1902// let fs = FakeFs::new(cx_a.background());
1903
1904// // Connect to a server as 2 clients.
1905// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
1906// let client_a = server.create_client(cx_a, "user_a").await;
1907// let client_b = server.create_client(cx_b, "user_b").await;
1908
1909// // Share a project as client A
1910// fs.insert_tree(
1911// "/dir",
1912// json!({
1913// ".zed.toml": r#"collaborators = ["user_b"]"#,
1914// "a.txt": "a-contents",
1915// }),
1916// )
1917// .await;
1918// let project_a = cx_a.update(|cx| {
1919// Project::local(
1920// client_a.clone(),
1921// client_a.user_store.clone(),
1922// lang_registry.clone(),
1923// fs.clone(),
1924// cx,
1925// )
1926// });
1927// let (worktree_a, _) = project_a
1928// .update(cx_a, |p, cx| {
1929// p.find_or_create_local_worktree("/dir", true, cx)
1930// })
1931// .await
1932// .unwrap();
1933// worktree_a
1934// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
1935// .await;
1936// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
1937// let worktree_id = worktree_a.read_with(cx_a, |tree, _| tree.id());
1938// project_a.update(cx_a, |p, cx| p.share(cx)).await.unwrap();
1939
1940// // Join that project as client B
1941// let project_b = Project::remote(
1942// project_id,
1943// client_b.clone(),
1944// client_b.user_store.clone(),
1945// lang_registry.clone(),
1946// fs.clone(),
1947// &mut cx_b.to_async(),
1948// )
1949// .await
1950// .unwrap();
1951
1952// // Open a buffer as client A
1953// let buffer_a = project_a
1954// .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
1955// .await
1956// .unwrap();
1957
1958// // Start opening the same buffer as client B
1959// let buffer_b = cx_b
1960// .background()
1961// .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
1962
1963// // Edit the buffer as client A while client B is still opening it.
1964// cx_b.background().simulate_random_delay().await;
1965// buffer_a.update(cx_a, |buf, cx| buf.edit([0..0], "X", cx));
1966// cx_b.background().simulate_random_delay().await;
1967// buffer_a.update(cx_a, |buf, cx| buf.edit([1..1], "Y", cx));
1968
1969// let text = buffer_a.read_with(cx_a, |buf, _| buf.text());
1970// let buffer_b = buffer_b.await.unwrap();
1971// buffer_b.condition(&cx_b, |buf, _| buf.text() == text).await;
1972// }
1973
1974// #[gpui::test(iterations = 10)]
1975// async fn test_leaving_worktree_while_opening_buffer(
1976// cx_a: &mut TestAppContext,
1977// cx_b: &mut TestAppContext,
1978// ) {
1979// cx_a.foreground().forbid_parking();
1980// let lang_registry = Arc::new(LanguageRegistry::test());
1981// let fs = FakeFs::new(cx_a.background());
1982
1983// // Connect to a server as 2 clients.
1984// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
1985// let client_a = server.create_client(cx_a, "user_a").await;
1986// let client_b = server.create_client(cx_b, "user_b").await;
1987
1988// // Share a project as client A
1989// fs.insert_tree(
1990// "/dir",
1991// json!({
1992// ".zed.toml": r#"collaborators = ["user_b"]"#,
1993// "a.txt": "a-contents",
1994// }),
1995// )
1996// .await;
1997// let project_a = cx_a.update(|cx| {
1998// Project::local(
1999// client_a.clone(),
2000// client_a.user_store.clone(),
2001// lang_registry.clone(),
2002// fs.clone(),
2003// cx,
2004// )
2005// });
2006// let (worktree_a, _) = project_a
2007// .update(cx_a, |p, cx| {
2008// p.find_or_create_local_worktree("/dir", true, cx)
2009// })
2010// .await
2011// .unwrap();
2012// worktree_a
2013// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
2014// .await;
2015// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
2016// let worktree_id = worktree_a.read_with(cx_a, |tree, _| tree.id());
2017// project_a.update(cx_a, |p, cx| p.share(cx)).await.unwrap();
2018
2019// // Join that project as client B
2020// let project_b = Project::remote(
2021// project_id,
2022// client_b.clone(),
2023// client_b.user_store.clone(),
2024// lang_registry.clone(),
2025// fs.clone(),
2026// &mut cx_b.to_async(),
2027// )
2028// .await
2029// .unwrap();
2030
2031// // See that a guest has joined as client A.
2032// project_a
2033// .condition(&cx_a, |p, _| p.collaborators().len() == 1)
2034// .await;
2035
2036// // Begin opening a buffer as client B, but leave the project before the open completes.
2037// let buffer_b = cx_b
2038// .background()
2039// .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)));
2040// cx_b.update(|_| drop(project_b));
2041// drop(buffer_b);
2042
2043// // See that the guest has left.
2044// project_a
2045// .condition(&cx_a, |p, _| p.collaborators().len() == 0)
2046// .await;
2047// }
2048
2049// #[gpui::test(iterations = 10)]
2050// async fn test_leaving_project(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
2051// cx_a.foreground().forbid_parking();
2052// let lang_registry = Arc::new(LanguageRegistry::test());
2053// let fs = FakeFs::new(cx_a.background());
2054
2055// // Connect to a server as 2 clients.
2056// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
2057// let client_a = server.create_client(cx_a, "user_a").await;
2058// let client_b = server.create_client(cx_b, "user_b").await;
2059
2060// // Share a project as client A
2061// fs.insert_tree(
2062// "/a",
2063// json!({
2064// ".zed.toml": r#"collaborators = ["user_b"]"#,
2065// "a.txt": "a-contents",
2066// "b.txt": "b-contents",
2067// }),
2068// )
2069// .await;
2070// let project_a = cx_a.update(|cx| {
2071// Project::local(
2072// client_a.clone(),
2073// client_a.user_store.clone(),
2074// lang_registry.clone(),
2075// fs.clone(),
2076// cx,
2077// )
2078// });
2079// let (worktree_a, _) = project_a
2080// .update(cx_a, |p, cx| {
2081// p.find_or_create_local_worktree("/a", true, cx)
2082// })
2083// .await
2084// .unwrap();
2085// worktree_a
2086// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
2087// .await;
2088// let project_id = project_a
2089// .update(cx_a, |project, _| project.next_remote_id())
2090// .await;
2091// project_a
2092// .update(cx_a, |project, cx| project.share(cx))
2093// .await
2094// .unwrap();
2095
2096// // Join that project as client B
2097// let _project_b = Project::remote(
2098// project_id,
2099// client_b.clone(),
2100// client_b.user_store.clone(),
2101// lang_registry.clone(),
2102// fs.clone(),
2103// &mut cx_b.to_async(),
2104// )
2105// .await
2106// .unwrap();
2107
2108// // Client A sees that a guest has joined.
2109// project_a
2110// .condition(cx_a, |p, _| p.collaborators().len() == 1)
2111// .await;
2112
2113// // Drop client B's connection and ensure client A observes client B leaving the project.
2114// client_b.disconnect(&cx_b.to_async()).unwrap();
2115// project_a
2116// .condition(cx_a, |p, _| p.collaborators().len() == 0)
2117// .await;
2118
2119// // Rejoin the project as client B
2120// let _project_b = Project::remote(
2121// project_id,
2122// client_b.clone(),
2123// client_b.user_store.clone(),
2124// lang_registry.clone(),
2125// fs.clone(),
2126// &mut cx_b.to_async(),
2127// )
2128// .await
2129// .unwrap();
2130
2131// // Client A sees that a guest has re-joined.
2132// project_a
2133// .condition(cx_a, |p, _| p.collaborators().len() == 1)
2134// .await;
2135
2136// // Simulate connection loss for client B and ensure client A observes client B leaving the project.
2137// client_b.wait_for_current_user(cx_b).await;
2138// server.disconnect_client(client_b.current_user_id(cx_b));
2139// cx_a.foreground().advance_clock(Duration::from_secs(3));
2140// project_a
2141// .condition(cx_a, |p, _| p.collaborators().len() == 0)
2142// .await;
2143// }
2144
2145// #[gpui::test(iterations = 10)]
2146// async fn test_collaborating_with_diagnostics(
2147// cx_a: &mut TestAppContext,
2148// cx_b: &mut TestAppContext,
2149// ) {
2150// cx_a.foreground().forbid_parking();
2151// let lang_registry = Arc::new(LanguageRegistry::test());
2152// let fs = FakeFs::new(cx_a.background());
2153
2154// // Set up a fake language server.
2155// let mut language = Language::new(
2156// LanguageConfig {
2157// name: "Rust".into(),
2158// path_suffixes: vec!["rs".to_string()],
2159// ..Default::default()
2160// },
2161// Some(tree_sitter_rust::language()),
2162// );
2163// let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
2164// lang_registry.add(Arc::new(language));
2165
2166// // Connect to a server as 2 clients.
2167// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
2168// let client_a = server.create_client(cx_a, "user_a").await;
2169// let client_b = server.create_client(cx_b, "user_b").await;
2170
2171// // Share a project as client A
2172// fs.insert_tree(
2173// "/a",
2174// json!({
2175// ".zed.toml": r#"collaborators = ["user_b"]"#,
2176// "a.rs": "let one = two",
2177// "other.rs": "",
2178// }),
2179// )
2180// .await;
2181// let project_a = cx_a.update(|cx| {
2182// Project::local(
2183// client_a.clone(),
2184// client_a.user_store.clone(),
2185// lang_registry.clone(),
2186// fs.clone(),
2187// cx,
2188// )
2189// });
2190// let (worktree_a, _) = project_a
2191// .update(cx_a, |p, cx| {
2192// p.find_or_create_local_worktree("/a", true, cx)
2193// })
2194// .await
2195// .unwrap();
2196// worktree_a
2197// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
2198// .await;
2199// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
2200// let worktree_id = worktree_a.read_with(cx_a, |tree, _| tree.id());
2201// project_a.update(cx_a, |p, cx| p.share(cx)).await.unwrap();
2202
2203// // Cause the language server to start.
2204// let _ = cx_a
2205// .background()
2206// .spawn(project_a.update(cx_a, |project, cx| {
2207// project.open_buffer(
2208// ProjectPath {
2209// worktree_id,
2210// path: Path::new("other.rs").into(),
2211// },
2212// cx,
2213// )
2214// }))
2215// .await
2216// .unwrap();
2217
2218// // Simulate a language server reporting errors for a file.
2219// let mut fake_language_server = fake_language_servers.next().await.unwrap();
2220// fake_language_server
2221// .receive_notification::<lsp::notification::DidOpenTextDocument>()
2222// .await;
2223// fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
2224// lsp::PublishDiagnosticsParams {
2225// uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
2226// version: None,
2227// diagnostics: vec![lsp::Diagnostic {
2228// severity: Some(lsp::DiagnosticSeverity::ERROR),
2229// range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
2230// message: "message 1".to_string(),
2231// ..Default::default()
2232// }],
2233// },
2234// );
2235
2236// // Wait for server to see the diagnostics update.
2237// server
2238// .condition(|store| {
2239// let worktree = store
2240// .project(project_id)
2241// .unwrap()
2242// .share
2243// .as_ref()
2244// .unwrap()
2245// .worktrees
2246// .get(&worktree_id.to_proto())
2247// .unwrap();
2248
2249// !worktree.diagnostic_summaries.is_empty()
2250// })
2251// .await;
2252
2253// // Join the worktree as client B.
2254// let project_b = Project::remote(
2255// project_id,
2256// client_b.clone(),
2257// client_b.user_store.clone(),
2258// lang_registry.clone(),
2259// fs.clone(),
2260// &mut cx_b.to_async(),
2261// )
2262// .await
2263// .unwrap();
2264
2265// project_b.read_with(cx_b, |project, cx| {
2266// assert_eq!(
2267// project.diagnostic_summaries(cx).collect::<Vec<_>>(),
2268// &[(
2269// ProjectPath {
2270// worktree_id,
2271// path: Arc::from(Path::new("a.rs")),
2272// },
2273// DiagnosticSummary {
2274// error_count: 1,
2275// warning_count: 0,
2276// ..Default::default()
2277// },
2278// )]
2279// )
2280// });
2281
2282// // Simulate a language server reporting more errors for a file.
2283// fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
2284// lsp::PublishDiagnosticsParams {
2285// uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
2286// version: None,
2287// diagnostics: vec![
2288// lsp::Diagnostic {
2289// severity: Some(lsp::DiagnosticSeverity::ERROR),
2290// range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)),
2291// message: "message 1".to_string(),
2292// ..Default::default()
2293// },
2294// lsp::Diagnostic {
2295// severity: Some(lsp::DiagnosticSeverity::WARNING),
2296// range: lsp::Range::new(
2297// lsp::Position::new(0, 10),
2298// lsp::Position::new(0, 13),
2299// ),
2300// message: "message 2".to_string(),
2301// ..Default::default()
2302// },
2303// ],
2304// },
2305// );
2306
2307// // Client b gets the updated summaries
2308// project_b
2309// .condition(&cx_b, |project, cx| {
2310// project.diagnostic_summaries(cx).collect::<Vec<_>>()
2311// == &[(
2312// ProjectPath {
2313// worktree_id,
2314// path: Arc::from(Path::new("a.rs")),
2315// },
2316// DiagnosticSummary {
2317// error_count: 1,
2318// warning_count: 1,
2319// ..Default::default()
2320// },
2321// )]
2322// })
2323// .await;
2324
2325// // Open the file with the errors on client B. They should be present.
2326// let buffer_b = cx_b
2327// .background()
2328// .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
2329// .await
2330// .unwrap();
2331
2332// buffer_b.read_with(cx_b, |buffer, _| {
2333// assert_eq!(
2334// buffer
2335// .snapshot()
2336// .diagnostics_in_range::<_, Point>(0..buffer.len(), false)
2337// .map(|entry| entry)
2338// .collect::<Vec<_>>(),
2339// &[
2340// DiagnosticEntry {
2341// range: Point::new(0, 4)..Point::new(0, 7),
2342// diagnostic: Diagnostic {
2343// group_id: 0,
2344// message: "message 1".to_string(),
2345// severity: lsp::DiagnosticSeverity::ERROR,
2346// is_primary: true,
2347// ..Default::default()
2348// }
2349// },
2350// DiagnosticEntry {
2351// range: Point::new(0, 10)..Point::new(0, 13),
2352// diagnostic: Diagnostic {
2353// group_id: 1,
2354// severity: lsp::DiagnosticSeverity::WARNING,
2355// message: "message 2".to_string(),
2356// is_primary: true,
2357// ..Default::default()
2358// }
2359// }
2360// ]
2361// );
2362// });
2363
2364// // Simulate a language server reporting no errors for a file.
2365// fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
2366// lsp::PublishDiagnosticsParams {
2367// uri: lsp::Url::from_file_path("/a/a.rs").unwrap(),
2368// version: None,
2369// diagnostics: vec![],
2370// },
2371// );
2372// project_a
2373// .condition(cx_a, |project, cx| {
2374// project.diagnostic_summaries(cx).collect::<Vec<_>>() == &[]
2375// })
2376// .await;
2377// project_b
2378// .condition(cx_b, |project, cx| {
2379// project.diagnostic_summaries(cx).collect::<Vec<_>>() == &[]
2380// })
2381// .await;
2382// }
2383
2384// #[gpui::test(iterations = 10)]
2385// async fn test_collaborating_with_completion(
2386// cx_a: &mut TestAppContext,
2387// cx_b: &mut TestAppContext,
2388// ) {
2389// cx_a.foreground().forbid_parking();
2390// let lang_registry = Arc::new(LanguageRegistry::test());
2391// let fs = FakeFs::new(cx_a.background());
2392
2393// // Set up a fake language server.
2394// let mut language = Language::new(
2395// LanguageConfig {
2396// name: "Rust".into(),
2397// path_suffixes: vec!["rs".to_string()],
2398// ..Default::default()
2399// },
2400// Some(tree_sitter_rust::language()),
2401// );
2402// let mut fake_language_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
2403// capabilities: lsp::ServerCapabilities {
2404// completion_provider: Some(lsp::CompletionOptions {
2405// trigger_characters: Some(vec![".".to_string()]),
2406// ..Default::default()
2407// }),
2408// ..Default::default()
2409// },
2410// ..Default::default()
2411// });
2412// lang_registry.add(Arc::new(language));
2413
2414// // Connect to a server as 2 clients.
2415// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
2416// let client_a = server.create_client(cx_a, "user_a").await;
2417// let client_b = server.create_client(cx_b, "user_b").await;
2418
2419// // Share a project as client A
2420// fs.insert_tree(
2421// "/a",
2422// json!({
2423// ".zed.toml": r#"collaborators = ["user_b"]"#,
2424// "main.rs": "fn main() { a }",
2425// "other.rs": "",
2426// }),
2427// )
2428// .await;
2429// let project_a = cx_a.update(|cx| {
2430// Project::local(
2431// client_a.clone(),
2432// client_a.user_store.clone(),
2433// lang_registry.clone(),
2434// fs.clone(),
2435// cx,
2436// )
2437// });
2438// let (worktree_a, _) = project_a
2439// .update(cx_a, |p, cx| {
2440// p.find_or_create_local_worktree("/a", true, cx)
2441// })
2442// .await
2443// .unwrap();
2444// worktree_a
2445// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
2446// .await;
2447// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
2448// let worktree_id = worktree_a.read_with(cx_a, |tree, _| tree.id());
2449// project_a.update(cx_a, |p, cx| p.share(cx)).await.unwrap();
2450
2451// // Join the worktree as client B.
2452// let project_b = Project::remote(
2453// project_id,
2454// client_b.clone(),
2455// client_b.user_store.clone(),
2456// lang_registry.clone(),
2457// fs.clone(),
2458// &mut cx_b.to_async(),
2459// )
2460// .await
2461// .unwrap();
2462
2463// // Open a file in an editor as the guest.
2464// let buffer_b = project_b
2465// .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
2466// .await
2467// .unwrap();
2468// let (window_b, _) = cx_b.add_window(|_| EmptyView);
2469// let editor_b = cx_b.add_view(window_b, |cx| {
2470// Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
2471// });
2472
2473// let fake_language_server = fake_language_servers.next().await.unwrap();
2474// buffer_b
2475// .condition(&cx_b, |buffer, _| !buffer.completion_triggers().is_empty())
2476// .await;
2477
2478// // Type a completion trigger character as the guest.
2479// editor_b.update(cx_b, |editor, cx| {
2480// editor.select_ranges([13..13], None, cx);
2481// editor.handle_input(&Input(".".into()), cx);
2482// cx.focus(&editor_b);
2483// });
2484
2485// // Receive a completion request as the host's language server.
2486// // Return some completions from the host's language server.
2487// cx_a.foreground().start_waiting();
2488// fake_language_server
2489// .handle_request::<lsp::request::Completion, _, _>(|params, _| async move {
2490// assert_eq!(
2491// params.text_document_position.text_document.uri,
2492// lsp::Url::from_file_path("/a/main.rs").unwrap(),
2493// );
2494// assert_eq!(
2495// params.text_document_position.position,
2496// lsp::Position::new(0, 14),
2497// );
2498
2499// Ok(Some(lsp::CompletionResponse::Array(vec![
2500// lsp::CompletionItem {
2501// label: "first_method(…)".into(),
2502// detail: Some("fn(&mut self, B) -> C".into()),
2503// text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
2504// new_text: "first_method($1)".to_string(),
2505// range: lsp::Range::new(
2506// lsp::Position::new(0, 14),
2507// lsp::Position::new(0, 14),
2508// ),
2509// })),
2510// insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
2511// ..Default::default()
2512// },
2513// lsp::CompletionItem {
2514// label: "second_method(…)".into(),
2515// detail: Some("fn(&mut self, C) -> D<E>".into()),
2516// text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
2517// new_text: "second_method()".to_string(),
2518// range: lsp::Range::new(
2519// lsp::Position::new(0, 14),
2520// lsp::Position::new(0, 14),
2521// ),
2522// })),
2523// insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
2524// ..Default::default()
2525// },
2526// ])))
2527// })
2528// .next()
2529// .await
2530// .unwrap();
2531// cx_a.foreground().finish_waiting();
2532
2533// // Open the buffer on the host.
2534// let buffer_a = project_a
2535// .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
2536// .await
2537// .unwrap();
2538// buffer_a
2539// .condition(&cx_a, |buffer, _| buffer.text() == "fn main() { a. }")
2540// .await;
2541
2542// // Confirm a completion on the guest.
2543// editor_b
2544// .condition(&cx_b, |editor, _| editor.context_menu_visible())
2545// .await;
2546// editor_b.update(cx_b, |editor, cx| {
2547// editor.confirm_completion(&ConfirmCompletion { item_ix: Some(0) }, cx);
2548// assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
2549// });
2550
2551// // Return a resolved completion from the host's language server.
2552// // The resolved completion has an additional text edit.
2553// fake_language_server.handle_request::<lsp::request::ResolveCompletionItem, _, _>(
2554// |params, _| async move {
2555// assert_eq!(params.label, "first_method(…)");
2556// Ok(lsp::CompletionItem {
2557// label: "first_method(…)".into(),
2558// detail: Some("fn(&mut self, B) -> C".into()),
2559// text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
2560// new_text: "first_method($1)".to_string(),
2561// range: lsp::Range::new(
2562// lsp::Position::new(0, 14),
2563// lsp::Position::new(0, 14),
2564// ),
2565// })),
2566// additional_text_edits: Some(vec![lsp::TextEdit {
2567// new_text: "use d::SomeTrait;\n".to_string(),
2568// range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
2569// }]),
2570// insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
2571// ..Default::default()
2572// })
2573// },
2574// );
2575
2576// // The additional edit is applied.
2577// buffer_a
2578// .condition(&cx_a, |buffer, _| {
2579// buffer.text() == "use d::SomeTrait;\nfn main() { a.first_method() }"
2580// })
2581// .await;
2582// buffer_b
2583// .condition(&cx_b, |buffer, _| {
2584// buffer.text() == "use d::SomeTrait;\nfn main() { a.first_method() }"
2585// })
2586// .await;
2587// }
2588
2589// #[gpui::test(iterations = 10)]
2590// async fn test_reloading_buffer_manually(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
2591// cx_a.foreground().forbid_parking();
2592// let lang_registry = Arc::new(LanguageRegistry::test());
2593// let fs = FakeFs::new(cx_a.background());
2594
2595// // Connect to a server as 2 clients.
2596// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
2597// let client_a = server.create_client(cx_a, "user_a").await;
2598// let client_b = server.create_client(cx_b, "user_b").await;
2599
2600// // Share a project as client A
2601// fs.insert_tree(
2602// "/a",
2603// json!({
2604// ".zed.toml": r#"collaborators = ["user_b"]"#,
2605// "a.rs": "let one = 1;",
2606// }),
2607// )
2608// .await;
2609// let project_a = cx_a.update(|cx| {
2610// Project::local(
2611// client_a.clone(),
2612// client_a.user_store.clone(),
2613// lang_registry.clone(),
2614// fs.clone(),
2615// cx,
2616// )
2617// });
2618// let (worktree_a, _) = project_a
2619// .update(cx_a, |p, cx| {
2620// p.find_or_create_local_worktree("/a", true, cx)
2621// })
2622// .await
2623// .unwrap();
2624// worktree_a
2625// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
2626// .await;
2627// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
2628// let worktree_id = worktree_a.read_with(cx_a, |tree, _| tree.id());
2629// project_a.update(cx_a, |p, cx| p.share(cx)).await.unwrap();
2630// let buffer_a = project_a
2631// .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))
2632// .await
2633// .unwrap();
2634
2635// // Join the worktree as client B.
2636// let project_b = Project::remote(
2637// project_id,
2638// client_b.clone(),
2639// client_b.user_store.clone(),
2640// lang_registry.clone(),
2641// fs.clone(),
2642// &mut cx_b.to_async(),
2643// )
2644// .await
2645// .unwrap();
2646
2647// let buffer_b = cx_b
2648// .background()
2649// .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
2650// .await
2651// .unwrap();
2652// buffer_b.update(cx_b, |buffer, cx| {
2653// buffer.edit([4..7], "six", cx);
2654// buffer.edit([10..11], "6", cx);
2655// assert_eq!(buffer.text(), "let six = 6;");
2656// assert!(buffer.is_dirty());
2657// assert!(!buffer.has_conflict());
2658// });
2659// buffer_a
2660// .condition(cx_a, |buffer, _| buffer.text() == "let six = 6;")
2661// .await;
2662
2663// fs.save(Path::new("/a/a.rs"), &Rope::from("let seven = 7;"))
2664// .await
2665// .unwrap();
2666// buffer_a
2667// .condition(cx_a, |buffer, _| buffer.has_conflict())
2668// .await;
2669// buffer_b
2670// .condition(cx_b, |buffer, _| buffer.has_conflict())
2671// .await;
2672
2673// project_b
2674// .update(cx_b, |project, cx| {
2675// project.reload_buffers(HashSet::from_iter([buffer_b.clone()]), true, cx)
2676// })
2677// .await
2678// .unwrap();
2679// buffer_a.read_with(cx_a, |buffer, _| {
2680// assert_eq!(buffer.text(), "let seven = 7;");
2681// assert!(!buffer.is_dirty());
2682// assert!(!buffer.has_conflict());
2683// });
2684// buffer_b.read_with(cx_b, |buffer, _| {
2685// assert_eq!(buffer.text(), "let seven = 7;");
2686// assert!(!buffer.is_dirty());
2687// assert!(!buffer.has_conflict());
2688// });
2689
2690// buffer_a.update(cx_a, |buffer, cx| {
2691// // Undoing on the host is a no-op when the reload was initiated by the guest.
2692// buffer.undo(cx);
2693// assert_eq!(buffer.text(), "let seven = 7;");
2694// assert!(!buffer.is_dirty());
2695// assert!(!buffer.has_conflict());
2696// });
2697// buffer_b.update(cx_b, |buffer, cx| {
2698// // Undoing on the guest rolls back the buffer to before it was reloaded but the conflict gets cleared.
2699// buffer.undo(cx);
2700// assert_eq!(buffer.text(), "let six = 6;");
2701// assert!(buffer.is_dirty());
2702// assert!(!buffer.has_conflict());
2703// });
2704// }
2705
2706// #[gpui::test(iterations = 10)]
2707// async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
2708// cx_a.foreground().forbid_parking();
2709// let lang_registry = Arc::new(LanguageRegistry::test());
2710// let fs = FakeFs::new(cx_a.background());
2711
2712// // Set up a fake language server.
2713// let mut language = Language::new(
2714// LanguageConfig {
2715// name: "Rust".into(),
2716// path_suffixes: vec!["rs".to_string()],
2717// ..Default::default()
2718// },
2719// Some(tree_sitter_rust::language()),
2720// );
2721// let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
2722// lang_registry.add(Arc::new(language));
2723
2724// // Connect to a server as 2 clients.
2725// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
2726// let client_a = server.create_client(cx_a, "user_a").await;
2727// let client_b = server.create_client(cx_b, "user_b").await;
2728
2729// // Share a project as client A
2730// fs.insert_tree(
2731// "/a",
2732// json!({
2733// ".zed.toml": r#"collaborators = ["user_b"]"#,
2734// "a.rs": "let one = two",
2735// }),
2736// )
2737// .await;
2738// let project_a = cx_a.update(|cx| {
2739// Project::local(
2740// client_a.clone(),
2741// client_a.user_store.clone(),
2742// lang_registry.clone(),
2743// fs.clone(),
2744// cx,
2745// )
2746// });
2747// let (worktree_a, _) = project_a
2748// .update(cx_a, |p, cx| {
2749// p.find_or_create_local_worktree("/a", true, cx)
2750// })
2751// .await
2752// .unwrap();
2753// worktree_a
2754// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
2755// .await;
2756// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
2757// let worktree_id = worktree_a.read_with(cx_a, |tree, _| tree.id());
2758// project_a.update(cx_a, |p, cx| p.share(cx)).await.unwrap();
2759
2760// // Join the worktree as client B.
2761// let project_b = Project::remote(
2762// project_id,
2763// client_b.clone(),
2764// client_b.user_store.clone(),
2765// lang_registry.clone(),
2766// fs.clone(),
2767// &mut cx_b.to_async(),
2768// )
2769// .await
2770// .unwrap();
2771
2772// let buffer_b = cx_b
2773// .background()
2774// .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
2775// .await
2776// .unwrap();
2777
2778// let fake_language_server = fake_language_servers.next().await.unwrap();
2779// fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
2780// Ok(Some(vec![
2781// lsp::TextEdit {
2782// range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
2783// new_text: "h".to_string(),
2784// },
2785// lsp::TextEdit {
2786// range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
2787// new_text: "y".to_string(),
2788// },
2789// ]))
2790// });
2791
2792// project_b
2793// .update(cx_b, |project, cx| {
2794// project.format(HashSet::from_iter([buffer_b.clone()]), true, cx)
2795// })
2796// .await
2797// .unwrap();
2798// assert_eq!(
2799// buffer_b.read_with(cx_b, |buffer, _| buffer.text()),
2800// "let honey = two"
2801// );
2802// }
2803
2804// #[gpui::test(iterations = 10)]
2805// async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
2806// cx_a.foreground().forbid_parking();
2807// let lang_registry = Arc::new(LanguageRegistry::test());
2808// let fs = FakeFs::new(cx_a.background());
2809// fs.insert_tree(
2810// "/root-1",
2811// json!({
2812// ".zed.toml": r#"collaborators = ["user_b"]"#,
2813// "a.rs": "const ONE: usize = b::TWO + b::THREE;",
2814// }),
2815// )
2816// .await;
2817// fs.insert_tree(
2818// "/root-2",
2819// json!({
2820// "b.rs": "const TWO: usize = 2;\nconst THREE: usize = 3;",
2821// }),
2822// )
2823// .await;
2824
2825// // Set up a fake language server.
2826// let mut language = Language::new(
2827// LanguageConfig {
2828// name: "Rust".into(),
2829// path_suffixes: vec!["rs".to_string()],
2830// ..Default::default()
2831// },
2832// Some(tree_sitter_rust::language()),
2833// );
2834// let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
2835// lang_registry.add(Arc::new(language));
2836
2837// // Connect to a server as 2 clients.
2838// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
2839// let client_a = server.create_client(cx_a, "user_a").await;
2840// let client_b = server.create_client(cx_b, "user_b").await;
2841
2842// // Share a project as client A
2843// let project_a = cx_a.update(|cx| {
2844// Project::local(
2845// client_a.clone(),
2846// client_a.user_store.clone(),
2847// lang_registry.clone(),
2848// fs.clone(),
2849// cx,
2850// )
2851// });
2852// let (worktree_a, _) = project_a
2853// .update(cx_a, |p, cx| {
2854// p.find_or_create_local_worktree("/root-1", true, cx)
2855// })
2856// .await
2857// .unwrap();
2858// worktree_a
2859// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
2860// .await;
2861// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
2862// let worktree_id = worktree_a.read_with(cx_a, |tree, _| tree.id());
2863// project_a.update(cx_a, |p, cx| p.share(cx)).await.unwrap();
2864
2865// // Join the worktree as client B.
2866// let project_b = Project::remote(
2867// project_id,
2868// client_b.clone(),
2869// client_b.user_store.clone(),
2870// lang_registry.clone(),
2871// fs.clone(),
2872// &mut cx_b.to_async(),
2873// )
2874// .await
2875// .unwrap();
2876
2877// // Open the file on client B.
2878// let buffer_b = cx_b
2879// .background()
2880// .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
2881// .await
2882// .unwrap();
2883
2884// // Request the definition of a symbol as the guest.
2885// let fake_language_server = fake_language_servers.next().await.unwrap();
2886// fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(
2887// |_, _| async move {
2888// Ok(Some(lsp::GotoDefinitionResponse::Scalar(
2889// lsp::Location::new(
2890// lsp::Url::from_file_path("/root-2/b.rs").unwrap(),
2891// lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
2892// ),
2893// )))
2894// },
2895// );
2896
2897// let definitions_1 = project_b
2898// .update(cx_b, |p, cx| p.definition(&buffer_b, 23, cx))
2899// .await
2900// .unwrap();
2901// cx_b.read(|cx| {
2902// assert_eq!(definitions_1.len(), 1);
2903// assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
2904// let target_buffer = definitions_1[0].buffer.read(cx);
2905// assert_eq!(
2906// target_buffer.text(),
2907// "const TWO: usize = 2;\nconst THREE: usize = 3;"
2908// );
2909// assert_eq!(
2910// definitions_1[0].range.to_point(target_buffer),
2911// Point::new(0, 6)..Point::new(0, 9)
2912// );
2913// });
2914
2915// // Try getting more definitions for the same buffer, ensuring the buffer gets reused from
2916// // the previous call to `definition`.
2917// fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(
2918// |_, _| async move {
2919// Ok(Some(lsp::GotoDefinitionResponse::Scalar(
2920// lsp::Location::new(
2921// lsp::Url::from_file_path("/root-2/b.rs").unwrap(),
2922// lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
2923// ),
2924// )))
2925// },
2926// );
2927
2928// let definitions_2 = project_b
2929// .update(cx_b, |p, cx| p.definition(&buffer_b, 33, cx))
2930// .await
2931// .unwrap();
2932// cx_b.read(|cx| {
2933// assert_eq!(definitions_2.len(), 1);
2934// assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
2935// let target_buffer = definitions_2[0].buffer.read(cx);
2936// assert_eq!(
2937// target_buffer.text(),
2938// "const TWO: usize = 2;\nconst THREE: usize = 3;"
2939// );
2940// assert_eq!(
2941// definitions_2[0].range.to_point(target_buffer),
2942// Point::new(1, 6)..Point::new(1, 11)
2943// );
2944// });
2945// assert_eq!(definitions_1[0].buffer, definitions_2[0].buffer);
2946// }
2947
2948// #[gpui::test(iterations = 10)]
2949// async fn test_references(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
2950// cx_a.foreground().forbid_parking();
2951// let lang_registry = Arc::new(LanguageRegistry::test());
2952// let fs = FakeFs::new(cx_a.background());
2953// fs.insert_tree(
2954// "/root-1",
2955// json!({
2956// ".zed.toml": r#"collaborators = ["user_b"]"#,
2957// "one.rs": "const ONE: usize = 1;",
2958// "two.rs": "const TWO: usize = one::ONE + one::ONE;",
2959// }),
2960// )
2961// .await;
2962// fs.insert_tree(
2963// "/root-2",
2964// json!({
2965// "three.rs": "const THREE: usize = two::TWO + one::ONE;",
2966// }),
2967// )
2968// .await;
2969
2970// // Set up a fake language server.
2971// let mut language = Language::new(
2972// LanguageConfig {
2973// name: "Rust".into(),
2974// path_suffixes: vec!["rs".to_string()],
2975// ..Default::default()
2976// },
2977// Some(tree_sitter_rust::language()),
2978// );
2979// let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
2980// lang_registry.add(Arc::new(language));
2981
2982// // Connect to a server as 2 clients.
2983// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
2984// let client_a = server.create_client(cx_a, "user_a").await;
2985// let client_b = server.create_client(cx_b, "user_b").await;
2986
2987// // Share a project as client A
2988// let project_a = cx_a.update(|cx| {
2989// Project::local(
2990// client_a.clone(),
2991// client_a.user_store.clone(),
2992// lang_registry.clone(),
2993// fs.clone(),
2994// cx,
2995// )
2996// });
2997// let (worktree_a, _) = project_a
2998// .update(cx_a, |p, cx| {
2999// p.find_or_create_local_worktree("/root-1", true, cx)
3000// })
3001// .await
3002// .unwrap();
3003// worktree_a
3004// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
3005// .await;
3006// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
3007// let worktree_id = worktree_a.read_with(cx_a, |tree, _| tree.id());
3008// project_a.update(cx_a, |p, cx| p.share(cx)).await.unwrap();
3009
3010// // Join the worktree as client B.
3011// let project_b = Project::remote(
3012// project_id,
3013// client_b.clone(),
3014// client_b.user_store.clone(),
3015// lang_registry.clone(),
3016// fs.clone(),
3017// &mut cx_b.to_async(),
3018// )
3019// .await
3020// .unwrap();
3021
3022// // Open the file on client B.
3023// let buffer_b = cx_b
3024// .background()
3025// .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
3026// .await
3027// .unwrap();
3028
3029// // Request references to a symbol as the guest.
3030// let fake_language_server = fake_language_servers.next().await.unwrap();
3031// fake_language_server.handle_request::<lsp::request::References, _, _>(
3032// |params, _| async move {
3033// assert_eq!(
3034// params.text_document_position.text_document.uri.as_str(),
3035// "file:///root-1/one.rs"
3036// );
3037// Ok(Some(vec![
3038// lsp::Location {
3039// uri: lsp::Url::from_file_path("/root-1/two.rs").unwrap(),
3040// range: lsp::Range::new(
3041// lsp::Position::new(0, 24),
3042// lsp::Position::new(0, 27),
3043// ),
3044// },
3045// lsp::Location {
3046// uri: lsp::Url::from_file_path("/root-1/two.rs").unwrap(),
3047// range: lsp::Range::new(
3048// lsp::Position::new(0, 35),
3049// lsp::Position::new(0, 38),
3050// ),
3051// },
3052// lsp::Location {
3053// uri: lsp::Url::from_file_path("/root-2/three.rs").unwrap(),
3054// range: lsp::Range::new(
3055// lsp::Position::new(0, 37),
3056// lsp::Position::new(0, 40),
3057// ),
3058// },
3059// ]))
3060// },
3061// );
3062
3063// let references = project_b
3064// .update(cx_b, |p, cx| p.references(&buffer_b, 7, cx))
3065// .await
3066// .unwrap();
3067// cx_b.read(|cx| {
3068// assert_eq!(references.len(), 3);
3069// assert_eq!(project_b.read(cx).worktrees(cx).count(), 2);
3070
3071// let two_buffer = references[0].buffer.read(cx);
3072// let three_buffer = references[2].buffer.read(cx);
3073// assert_eq!(
3074// two_buffer.file().unwrap().path().as_ref(),
3075// Path::new("two.rs")
3076// );
3077// assert_eq!(references[1].buffer, references[0].buffer);
3078// assert_eq!(
3079// three_buffer.file().unwrap().full_path(cx),
3080// Path::new("three.rs")
3081// );
3082
3083// assert_eq!(references[0].range.to_offset(&two_buffer), 24..27);
3084// assert_eq!(references[1].range.to_offset(&two_buffer), 35..38);
3085// assert_eq!(references[2].range.to_offset(&three_buffer), 37..40);
3086// });
3087// }
3088
3089// #[gpui::test(iterations = 10)]
3090// async fn test_project_search(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
3091// cx_a.foreground().forbid_parking();
3092// let lang_registry = Arc::new(LanguageRegistry::test());
3093// let fs = FakeFs::new(cx_a.background());
3094// fs.insert_tree(
3095// "/root-1",
3096// json!({
3097// ".zed.toml": r#"collaborators = ["user_b"]"#,
3098// "a": "hello world",
3099// "b": "goodnight moon",
3100// "c": "a world of goo",
3101// "d": "world champion of clown world",
3102// }),
3103// )
3104// .await;
3105// fs.insert_tree(
3106// "/root-2",
3107// json!({
3108// "e": "disney world is fun",
3109// }),
3110// )
3111// .await;
3112
3113// // Connect to a server as 2 clients.
3114// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
3115// let client_a = server.create_client(cx_a, "user_a").await;
3116// let client_b = server.create_client(cx_b, "user_b").await;
3117
3118// // Share a project as client A
3119// let project_a = cx_a.update(|cx| {
3120// Project::local(
3121// client_a.clone(),
3122// client_a.user_store.clone(),
3123// lang_registry.clone(),
3124// fs.clone(),
3125// cx,
3126// )
3127// });
3128// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
3129
3130// let (worktree_1, _) = project_a
3131// .update(cx_a, |p, cx| {
3132// p.find_or_create_local_worktree("/root-1", true, cx)
3133// })
3134// .await
3135// .unwrap();
3136// worktree_1
3137// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
3138// .await;
3139// let (worktree_2, _) = project_a
3140// .update(cx_a, |p, cx| {
3141// p.find_or_create_local_worktree("/root-2", true, cx)
3142// })
3143// .await
3144// .unwrap();
3145// worktree_2
3146// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
3147// .await;
3148
3149// project_a.update(cx_a, |p, cx| p.share(cx)).await.unwrap();
3150
3151// // Join the worktree as client B.
3152// let project_b = Project::remote(
3153// project_id,
3154// client_b.clone(),
3155// client_b.user_store.clone(),
3156// lang_registry.clone(),
3157// fs.clone(),
3158// &mut cx_b.to_async(),
3159// )
3160// .await
3161// .unwrap();
3162
3163// let results = project_b
3164// .update(cx_b, |project, cx| {
3165// project.search(SearchQuery::text("world", false, false), cx)
3166// })
3167// .await
3168// .unwrap();
3169
3170// let mut ranges_by_path = results
3171// .into_iter()
3172// .map(|(buffer, ranges)| {
3173// buffer.read_with(cx_b, |buffer, cx| {
3174// let path = buffer.file().unwrap().full_path(cx);
3175// let offset_ranges = ranges
3176// .into_iter()
3177// .map(|range| range.to_offset(buffer))
3178// .collect::<Vec<_>>();
3179// (path, offset_ranges)
3180// })
3181// })
3182// .collect::<Vec<_>>();
3183// ranges_by_path.sort_by_key(|(path, _)| path.clone());
3184
3185// assert_eq!(
3186// ranges_by_path,
3187// &[
3188// (PathBuf::from("root-1/a"), vec![6..11]),
3189// (PathBuf::from("root-1/c"), vec![2..7]),
3190// (PathBuf::from("root-1/d"), vec![0..5, 24..29]),
3191// (PathBuf::from("root-2/e"), vec![7..12]),
3192// ]
3193// );
3194// }
3195
3196// #[gpui::test(iterations = 10)]
3197// async fn test_document_highlights(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
3198// cx_a.foreground().forbid_parking();
3199// let lang_registry = Arc::new(LanguageRegistry::test());
3200// let fs = FakeFs::new(cx_a.background());
3201// fs.insert_tree(
3202// "/root-1",
3203// json!({
3204// ".zed.toml": r#"collaborators = ["user_b"]"#,
3205// "main.rs": "fn double(number: i32) -> i32 { number + number }",
3206// }),
3207// )
3208// .await;
3209
3210// // Set up a fake language server.
3211// let mut language = Language::new(
3212// LanguageConfig {
3213// name: "Rust".into(),
3214// path_suffixes: vec!["rs".to_string()],
3215// ..Default::default()
3216// },
3217// Some(tree_sitter_rust::language()),
3218// );
3219// let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
3220// lang_registry.add(Arc::new(language));
3221
3222// // Connect to a server as 2 clients.
3223// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
3224// let client_a = server.create_client(cx_a, "user_a").await;
3225// let client_b = server.create_client(cx_b, "user_b").await;
3226
3227// // Share a project as client A
3228// let project_a = cx_a.update(|cx| {
3229// Project::local(
3230// client_a.clone(),
3231// client_a.user_store.clone(),
3232// lang_registry.clone(),
3233// fs.clone(),
3234// cx,
3235// )
3236// });
3237// let (worktree_a, _) = project_a
3238// .update(cx_a, |p, cx| {
3239// p.find_or_create_local_worktree("/root-1", true, cx)
3240// })
3241// .await
3242// .unwrap();
3243// worktree_a
3244// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
3245// .await;
3246// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
3247// let worktree_id = worktree_a.read_with(cx_a, |tree, _| tree.id());
3248// project_a.update(cx_a, |p, cx| p.share(cx)).await.unwrap();
3249
3250// // Join the worktree as client B.
3251// let project_b = Project::remote(
3252// project_id,
3253// client_b.clone(),
3254// client_b.user_store.clone(),
3255// lang_registry.clone(),
3256// fs.clone(),
3257// &mut cx_b.to_async(),
3258// )
3259// .await
3260// .unwrap();
3261
3262// // Open the file on client B.
3263// let buffer_b = cx_b
3264// .background()
3265// .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)))
3266// .await
3267// .unwrap();
3268
3269// // Request document highlights as the guest.
3270// let fake_language_server = fake_language_servers.next().await.unwrap();
3271// fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
3272// |params, _| async move {
3273// assert_eq!(
3274// params
3275// .text_document_position_params
3276// .text_document
3277// .uri
3278// .as_str(),
3279// "file:///root-1/main.rs"
3280// );
3281// assert_eq!(
3282// params.text_document_position_params.position,
3283// lsp::Position::new(0, 34)
3284// );
3285// Ok(Some(vec![
3286// lsp::DocumentHighlight {
3287// kind: Some(lsp::DocumentHighlightKind::WRITE),
3288// range: lsp::Range::new(
3289// lsp::Position::new(0, 10),
3290// lsp::Position::new(0, 16),
3291// ),
3292// },
3293// lsp::DocumentHighlight {
3294// kind: Some(lsp::DocumentHighlightKind::READ),
3295// range: lsp::Range::new(
3296// lsp::Position::new(0, 32),
3297// lsp::Position::new(0, 38),
3298// ),
3299// },
3300// lsp::DocumentHighlight {
3301// kind: Some(lsp::DocumentHighlightKind::READ),
3302// range: lsp::Range::new(
3303// lsp::Position::new(0, 41),
3304// lsp::Position::new(0, 47),
3305// ),
3306// },
3307// ]))
3308// },
3309// );
3310
3311// let highlights = project_b
3312// .update(cx_b, |p, cx| p.document_highlights(&buffer_b, 34, cx))
3313// .await
3314// .unwrap();
3315// buffer_b.read_with(cx_b, |buffer, _| {
3316// let snapshot = buffer.snapshot();
3317
3318// let highlights = highlights
3319// .into_iter()
3320// .map(|highlight| (highlight.kind, highlight.range.to_offset(&snapshot)))
3321// .collect::<Vec<_>>();
3322// assert_eq!(
3323// highlights,
3324// &[
3325// (lsp::DocumentHighlightKind::WRITE, 10..16),
3326// (lsp::DocumentHighlightKind::READ, 32..38),
3327// (lsp::DocumentHighlightKind::READ, 41..47)
3328// ]
3329// )
3330// });
3331// }
3332
3333// #[gpui::test(iterations = 10)]
3334// async fn test_project_symbols(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
3335// cx_a.foreground().forbid_parking();
3336// let lang_registry = Arc::new(LanguageRegistry::test());
3337// let fs = FakeFs::new(cx_a.background());
3338// fs.insert_tree(
3339// "/code",
3340// json!({
3341// "crate-1": {
3342// ".zed.toml": r#"collaborators = ["user_b"]"#,
3343// "one.rs": "const ONE: usize = 1;",
3344// },
3345// "crate-2": {
3346// "two.rs": "const TWO: usize = 2; const THREE: usize = 3;",
3347// },
3348// "private": {
3349// "passwords.txt": "the-password",
3350// }
3351// }),
3352// )
3353// .await;
3354
3355// // Set up a fake language server.
3356// let mut language = Language::new(
3357// LanguageConfig {
3358// name: "Rust".into(),
3359// path_suffixes: vec!["rs".to_string()],
3360// ..Default::default()
3361// },
3362// Some(tree_sitter_rust::language()),
3363// );
3364// let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
3365// lang_registry.add(Arc::new(language));
3366
3367// // Connect to a server as 2 clients.
3368// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
3369// let client_a = server.create_client(cx_a, "user_a").await;
3370// let client_b = server.create_client(cx_b, "user_b").await;
3371
3372// // Share a project as client A
3373// let project_a = cx_a.update(|cx| {
3374// Project::local(
3375// client_a.clone(),
3376// client_a.user_store.clone(),
3377// lang_registry.clone(),
3378// fs.clone(),
3379// cx,
3380// )
3381// });
3382// let (worktree_a, _) = project_a
3383// .update(cx_a, |p, cx| {
3384// p.find_or_create_local_worktree("/code/crate-1", true, cx)
3385// })
3386// .await
3387// .unwrap();
3388// worktree_a
3389// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
3390// .await;
3391// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
3392// let worktree_id = worktree_a.read_with(cx_a, |tree, _| tree.id());
3393// project_a.update(cx_a, |p, cx| p.share(cx)).await.unwrap();
3394
3395// // Join the worktree as client B.
3396// let project_b = Project::remote(
3397// project_id,
3398// client_b.clone(),
3399// client_b.user_store.clone(),
3400// lang_registry.clone(),
3401// fs.clone(),
3402// &mut cx_b.to_async(),
3403// )
3404// .await
3405// .unwrap();
3406
3407// // Cause the language server to start.
3408// let _buffer = cx_b
3409// .background()
3410// .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "one.rs"), cx)))
3411// .await
3412// .unwrap();
3413
3414// let fake_language_server = fake_language_servers.next().await.unwrap();
3415// fake_language_server.handle_request::<lsp::request::WorkspaceSymbol, _, _>(
3416// |_, _| async move {
3417// #[allow(deprecated)]
3418// Ok(Some(vec![lsp::SymbolInformation {
3419// name: "TWO".into(),
3420// location: lsp::Location {
3421// uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
3422// range: lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
3423// },
3424// kind: lsp::SymbolKind::CONSTANT,
3425// tags: None,
3426// container_name: None,
3427// deprecated: None,
3428// }]))
3429// },
3430// );
3431
3432// // Request the definition of a symbol as the guest.
3433// let symbols = project_b
3434// .update(cx_b, |p, cx| p.symbols("two", cx))
3435// .await
3436// .unwrap();
3437// assert_eq!(symbols.len(), 1);
3438// assert_eq!(symbols[0].name, "TWO");
3439
3440// // Open one of the returned symbols.
3441// let buffer_b_2 = project_b
3442// .update(cx_b, |project, cx| {
3443// project.open_buffer_for_symbol(&symbols[0], cx)
3444// })
3445// .await
3446// .unwrap();
3447// buffer_b_2.read_with(cx_b, |buffer, _| {
3448// assert_eq!(
3449// buffer.file().unwrap().path().as_ref(),
3450// Path::new("../crate-2/two.rs")
3451// );
3452// });
3453
3454// // Attempt to craft a symbol and violate host's privacy by opening an arbitrary file.
3455// let mut fake_symbol = symbols[0].clone();
3456// fake_symbol.path = Path::new("/code/secrets").into();
3457// let error = project_b
3458// .update(cx_b, |project, cx| {
3459// project.open_buffer_for_symbol(&fake_symbol, cx)
3460// })
3461// .await
3462// .unwrap_err();
3463// assert!(error.to_string().contains("invalid symbol signature"));
3464// }
3465
3466// #[gpui::test(iterations = 10)]
3467// async fn test_open_buffer_while_getting_definition_pointing_to_it(
3468// cx_a: &mut TestAppContext,
3469// cx_b: &mut TestAppContext,
3470// mut rng: StdRng,
3471// ) {
3472// cx_a.foreground().forbid_parking();
3473// let lang_registry = Arc::new(LanguageRegistry::test());
3474// let fs = FakeFs::new(cx_a.background());
3475// fs.insert_tree(
3476// "/root",
3477// json!({
3478// ".zed.toml": r#"collaborators = ["user_b"]"#,
3479// "a.rs": "const ONE: usize = b::TWO;",
3480// "b.rs": "const TWO: usize = 2",
3481// }),
3482// )
3483// .await;
3484
3485// // Set up a fake language server.
3486// let mut language = Language::new(
3487// LanguageConfig {
3488// name: "Rust".into(),
3489// path_suffixes: vec!["rs".to_string()],
3490// ..Default::default()
3491// },
3492// Some(tree_sitter_rust::language()),
3493// );
3494// let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
3495// lang_registry.add(Arc::new(language));
3496
3497// // Connect to a server as 2 clients.
3498// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
3499// let client_a = server.create_client(cx_a, "user_a").await;
3500// let client_b = server.create_client(cx_b, "user_b").await;
3501
3502// // Share a project as client A
3503// let project_a = cx_a.update(|cx| {
3504// Project::local(
3505// client_a.clone(),
3506// client_a.user_store.clone(),
3507// lang_registry.clone(),
3508// fs.clone(),
3509// cx,
3510// )
3511// });
3512
3513// let (worktree_a, _) = project_a
3514// .update(cx_a, |p, cx| {
3515// p.find_or_create_local_worktree("/root", true, cx)
3516// })
3517// .await
3518// .unwrap();
3519// worktree_a
3520// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
3521// .await;
3522// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
3523// let worktree_id = worktree_a.read_with(cx_a, |tree, _| tree.id());
3524// project_a.update(cx_a, |p, cx| p.share(cx)).await.unwrap();
3525
3526// // Join the worktree as client B.
3527// let project_b = Project::remote(
3528// project_id,
3529// client_b.clone(),
3530// client_b.user_store.clone(),
3531// lang_registry.clone(),
3532// fs.clone(),
3533// &mut cx_b.to_async(),
3534// )
3535// .await
3536// .unwrap();
3537
3538// let buffer_b1 = cx_b
3539// .background()
3540// .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)))
3541// .await
3542// .unwrap();
3543
3544// let fake_language_server = fake_language_servers.next().await.unwrap();
3545// fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(
3546// |_, _| async move {
3547// Ok(Some(lsp::GotoDefinitionResponse::Scalar(
3548// lsp::Location::new(
3549// lsp::Url::from_file_path("/root/b.rs").unwrap(),
3550// lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
3551// ),
3552// )))
3553// },
3554// );
3555
3556// let definitions;
3557// let buffer_b2;
3558// if rng.gen() {
3559// definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
3560// buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
3561// } else {
3562// buffer_b2 = project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.rs"), cx));
3563// definitions = project_b.update(cx_b, |p, cx| p.definition(&buffer_b1, 23, cx));
3564// }
3565
3566// let buffer_b2 = buffer_b2.await.unwrap();
3567// let definitions = definitions.await.unwrap();
3568// assert_eq!(definitions.len(), 1);
3569// assert_eq!(definitions[0].buffer, buffer_b2);
3570// }
3571
3572// #[gpui::test(iterations = 10)]
3573// async fn test_collaborating_with_code_actions(
3574// cx_a: &mut TestAppContext,
3575// cx_b: &mut TestAppContext,
3576// ) {
3577// cx_a.foreground().forbid_parking();
3578// let lang_registry = Arc::new(LanguageRegistry::test());
3579// let fs = FakeFs::new(cx_a.background());
3580// cx_b.update(|cx| editor::init(cx));
3581
3582// // Set up a fake language server.
3583// let mut language = Language::new(
3584// LanguageConfig {
3585// name: "Rust".into(),
3586// path_suffixes: vec!["rs".to_string()],
3587// ..Default::default()
3588// },
3589// Some(tree_sitter_rust::language()),
3590// );
3591// let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default());
3592// lang_registry.add(Arc::new(language));
3593
3594// // Connect to a server as 2 clients.
3595// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
3596// let client_a = server.create_client(cx_a, "user_a").await;
3597// let client_b = server.create_client(cx_b, "user_b").await;
3598
3599// // Share a project as client A
3600// fs.insert_tree(
3601// "/a",
3602// json!({
3603// ".zed.toml": r#"collaborators = ["user_b"]"#,
3604// "main.rs": "mod other;\nfn main() { let foo = other::foo(); }",
3605// "other.rs": "pub fn foo() -> usize { 4 }",
3606// }),
3607// )
3608// .await;
3609// let project_a = cx_a.update(|cx| {
3610// Project::local(
3611// client_a.clone(),
3612// client_a.user_store.clone(),
3613// lang_registry.clone(),
3614// fs.clone(),
3615// cx,
3616// )
3617// });
3618// let (worktree_a, _) = project_a
3619// .update(cx_a, |p, cx| {
3620// p.find_or_create_local_worktree("/a", true, cx)
3621// })
3622// .await
3623// .unwrap();
3624// worktree_a
3625// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
3626// .await;
3627// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
3628// let worktree_id = worktree_a.read_with(cx_a, |tree, _| tree.id());
3629// project_a.update(cx_a, |p, cx| p.share(cx)).await.unwrap();
3630
3631// // Join the worktree as client B.
3632// let project_b = Project::remote(
3633// project_id,
3634// client_b.clone(),
3635// client_b.user_store.clone(),
3636// lang_registry.clone(),
3637// fs.clone(),
3638// &mut cx_b.to_async(),
3639// )
3640// .await
3641// .unwrap();
3642// let mut params = cx_b.update(WorkspaceParams::test);
3643// params.languages = lang_registry.clone();
3644// params.client = client_b.client.clone();
3645// params.user_store = client_b.user_store.clone();
3646// params.project = project_b;
3647
3648// let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(¶ms, cx));
3649// let editor_b = workspace_b
3650// .update(cx_b, |workspace, cx| {
3651// workspace.open_path((worktree_id, "main.rs"), cx)
3652// })
3653// .await
3654// .unwrap()
3655// .downcast::<Editor>()
3656// .unwrap();
3657
3658// let mut fake_language_server = fake_language_servers.next().await.unwrap();
3659// fake_language_server
3660// .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
3661// assert_eq!(
3662// params.text_document.uri,
3663// lsp::Url::from_file_path("/a/main.rs").unwrap(),
3664// );
3665// assert_eq!(params.range.start, lsp::Position::new(0, 0));
3666// assert_eq!(params.range.end, lsp::Position::new(0, 0));
3667// Ok(None)
3668// })
3669// .next()
3670// .await;
3671
3672// // Move cursor to a location that contains code actions.
3673// editor_b.update(cx_b, |editor, cx| {
3674// editor.select_ranges([Point::new(1, 31)..Point::new(1, 31)], None, cx);
3675// cx.focus(&editor_b);
3676// });
3677
3678// fake_language_server
3679// .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
3680// assert_eq!(
3681// params.text_document.uri,
3682// lsp::Url::from_file_path("/a/main.rs").unwrap(),
3683// );
3684// assert_eq!(params.range.start, lsp::Position::new(1, 31));
3685// assert_eq!(params.range.end, lsp::Position::new(1, 31));
3686
3687// Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
3688// lsp::CodeAction {
3689// title: "Inline into all callers".to_string(),
3690// edit: Some(lsp::WorkspaceEdit {
3691// changes: Some(
3692// [
3693// (
3694// lsp::Url::from_file_path("/a/main.rs").unwrap(),
3695// vec![lsp::TextEdit::new(
3696// lsp::Range::new(
3697// lsp::Position::new(1, 22),
3698// lsp::Position::new(1, 34),
3699// ),
3700// "4".to_string(),
3701// )],
3702// ),
3703// (
3704// lsp::Url::from_file_path("/a/other.rs").unwrap(),
3705// vec![lsp::TextEdit::new(
3706// lsp::Range::new(
3707// lsp::Position::new(0, 0),
3708// lsp::Position::new(0, 27),
3709// ),
3710// "".to_string(),
3711// )],
3712// ),
3713// ]
3714// .into_iter()
3715// .collect(),
3716// ),
3717// ..Default::default()
3718// }),
3719// data: Some(json!({
3720// "codeActionParams": {
3721// "range": {
3722// "start": {"line": 1, "column": 31},
3723// "end": {"line": 1, "column": 31},
3724// }
3725// }
3726// })),
3727// ..Default::default()
3728// },
3729// )]))
3730// })
3731// .next()
3732// .await;
3733
3734// // Toggle code actions and wait for them to display.
3735// editor_b.update(cx_b, |editor, cx| {
3736// editor.toggle_code_actions(
3737// &ToggleCodeActions {
3738// deployed_from_indicator: false,
3739// },
3740// cx,
3741// );
3742// });
3743// editor_b
3744// .condition(&cx_b, |editor, _| editor.context_menu_visible())
3745// .await;
3746
3747// fake_language_server.remove_request_handler::<lsp::request::CodeActionRequest>();
3748
3749// // Confirming the code action will trigger a resolve request.
3750// let confirm_action = workspace_b
3751// .update(cx_b, |workspace, cx| {
3752// Editor::confirm_code_action(workspace, &ConfirmCodeAction { item_ix: Some(0) }, cx)
3753// })
3754// .unwrap();
3755// fake_language_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
3756// |_, _| async move {
3757// Ok(lsp::CodeAction {
3758// title: "Inline into all callers".to_string(),
3759// edit: Some(lsp::WorkspaceEdit {
3760// changes: Some(
3761// [
3762// (
3763// lsp::Url::from_file_path("/a/main.rs").unwrap(),
3764// vec![lsp::TextEdit::new(
3765// lsp::Range::new(
3766// lsp::Position::new(1, 22),
3767// lsp::Position::new(1, 34),
3768// ),
3769// "4".to_string(),
3770// )],
3771// ),
3772// (
3773// lsp::Url::from_file_path("/a/other.rs").unwrap(),
3774// vec![lsp::TextEdit::new(
3775// lsp::Range::new(
3776// lsp::Position::new(0, 0),
3777// lsp::Position::new(0, 27),
3778// ),
3779// "".to_string(),
3780// )],
3781// ),
3782// ]
3783// .into_iter()
3784// .collect(),
3785// ),
3786// ..Default::default()
3787// }),
3788// ..Default::default()
3789// })
3790// },
3791// );
3792
3793// // After the action is confirmed, an editor containing both modified files is opened.
3794// confirm_action.await.unwrap();
3795// let code_action_editor = workspace_b.read_with(cx_b, |workspace, cx| {
3796// workspace
3797// .active_item(cx)
3798// .unwrap()
3799// .downcast::<Editor>()
3800// .unwrap()
3801// });
3802// code_action_editor.update(cx_b, |editor, cx| {
3803// assert_eq!(editor.text(cx), "\nmod other;\nfn main() { let foo = 4; }");
3804// editor.undo(&Undo, cx);
3805// assert_eq!(
3806// editor.text(cx),
3807// "pub fn foo() -> usize { 4 }\nmod other;\nfn main() { let foo = other::foo(); }"
3808// );
3809// editor.redo(&Redo, cx);
3810// assert_eq!(editor.text(cx), "\nmod other;\nfn main() { let foo = 4; }");
3811// });
3812// }
3813
3814// #[gpui::test(iterations = 10)]
3815// async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
3816// cx_a.foreground().forbid_parking();
3817// let lang_registry = Arc::new(LanguageRegistry::test());
3818// let fs = FakeFs::new(cx_a.background());
3819// cx_b.update(|cx| editor::init(cx));
3820
3821// // Set up a fake language server.
3822// let mut language = Language::new(
3823// LanguageConfig {
3824// name: "Rust".into(),
3825// path_suffixes: vec!["rs".to_string()],
3826// ..Default::default()
3827// },
3828// Some(tree_sitter_rust::language()),
3829// );
3830// let mut fake_language_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
3831// capabilities: lsp::ServerCapabilities {
3832// rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
3833// prepare_provider: Some(true),
3834// work_done_progress_options: Default::default(),
3835// })),
3836// ..Default::default()
3837// },
3838// ..Default::default()
3839// });
3840// lang_registry.add(Arc::new(language));
3841
3842// // Connect to a server as 2 clients.
3843// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
3844// let client_a = server.create_client(cx_a, "user_a").await;
3845// let client_b = server.create_client(cx_b, "user_b").await;
3846
3847// // Share a project as client A
3848// fs.insert_tree(
3849// "/dir",
3850// json!({
3851// ".zed.toml": r#"collaborators = ["user_b"]"#,
3852// "one.rs": "const ONE: usize = 1;",
3853// "two.rs": "const TWO: usize = one::ONE + one::ONE;"
3854// }),
3855// )
3856// .await;
3857// let project_a = cx_a.update(|cx| {
3858// Project::local(
3859// client_a.clone(),
3860// client_a.user_store.clone(),
3861// lang_registry.clone(),
3862// fs.clone(),
3863// cx,
3864// )
3865// });
3866// let (worktree_a, _) = project_a
3867// .update(cx_a, |p, cx| {
3868// p.find_or_create_local_worktree("/dir", true, cx)
3869// })
3870// .await
3871// .unwrap();
3872// worktree_a
3873// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
3874// .await;
3875// let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await;
3876// let worktree_id = worktree_a.read_with(cx_a, |tree, _| tree.id());
3877// project_a.update(cx_a, |p, cx| p.share(cx)).await.unwrap();
3878
3879// // Join the worktree as client B.
3880// let project_b = Project::remote(
3881// project_id,
3882// client_b.clone(),
3883// client_b.user_store.clone(),
3884// lang_registry.clone(),
3885// fs.clone(),
3886// &mut cx_b.to_async(),
3887// )
3888// .await
3889// .unwrap();
3890// let mut params = cx_b.update(WorkspaceParams::test);
3891// params.languages = lang_registry.clone();
3892// params.client = client_b.client.clone();
3893// params.user_store = client_b.user_store.clone();
3894// params.project = project_b;
3895
3896// let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(¶ms, cx));
3897// let editor_b = workspace_b
3898// .update(cx_b, |workspace, cx| {
3899// workspace.open_path((worktree_id, "one.rs"), cx)
3900// })
3901// .await
3902// .unwrap()
3903// .downcast::<Editor>()
3904// .unwrap();
3905// let fake_language_server = fake_language_servers.next().await.unwrap();
3906
3907// // Move cursor to a location that can be renamed.
3908// let prepare_rename = editor_b.update(cx_b, |editor, cx| {
3909// editor.select_ranges([7..7], None, cx);
3910// editor.rename(&Rename, cx).unwrap()
3911// });
3912
3913// fake_language_server
3914// .handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
3915// assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
3916// assert_eq!(params.position, lsp::Position::new(0, 7));
3917// Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
3918// lsp::Position::new(0, 6),
3919// lsp::Position::new(0, 9),
3920// ))))
3921// })
3922// .next()
3923// .await
3924// .unwrap();
3925// prepare_rename.await.unwrap();
3926// editor_b.update(cx_b, |editor, cx| {
3927// let rename = editor.pending_rename().unwrap();
3928// let buffer = editor.buffer().read(cx).snapshot(cx);
3929// assert_eq!(
3930// rename.range.start.to_offset(&buffer)..rename.range.end.to_offset(&buffer),
3931// 6..9
3932// );
3933// rename.editor.update(cx, |rename_editor, cx| {
3934// rename_editor.buffer().update(cx, |rename_buffer, cx| {
3935// rename_buffer.edit([0..3], "THREE", cx);
3936// });
3937// });
3938// });
3939
3940// let confirm_rename = workspace_b.update(cx_b, |workspace, cx| {
3941// Editor::confirm_rename(workspace, &ConfirmRename, cx).unwrap()
3942// });
3943// fake_language_server
3944// .handle_request::<lsp::request::Rename, _, _>(|params, _| async move {
3945// assert_eq!(
3946// params.text_document_position.text_document.uri.as_str(),
3947// "file:///dir/one.rs"
3948// );
3949// assert_eq!(
3950// params.text_document_position.position,
3951// lsp::Position::new(0, 6)
3952// );
3953// assert_eq!(params.new_name, "THREE");
3954// Ok(Some(lsp::WorkspaceEdit {
3955// changes: Some(
3956// [
3957// (
3958// lsp::Url::from_file_path("/dir/one.rs").unwrap(),
3959// vec![lsp::TextEdit::new(
3960// lsp::Range::new(
3961// lsp::Position::new(0, 6),
3962// lsp::Position::new(0, 9),
3963// ),
3964// "THREE".to_string(),
3965// )],
3966// ),
3967// (
3968// lsp::Url::from_file_path("/dir/two.rs").unwrap(),
3969// vec![
3970// lsp::TextEdit::new(
3971// lsp::Range::new(
3972// lsp::Position::new(0, 24),
3973// lsp::Position::new(0, 27),
3974// ),
3975// "THREE".to_string(),
3976// ),
3977// lsp::TextEdit::new(
3978// lsp::Range::new(
3979// lsp::Position::new(0, 35),
3980// lsp::Position::new(0, 38),
3981// ),
3982// "THREE".to_string(),
3983// ),
3984// ],
3985// ),
3986// ]
3987// .into_iter()
3988// .collect(),
3989// ),
3990// ..Default::default()
3991// }))
3992// })
3993// .next()
3994// .await
3995// .unwrap();
3996// confirm_rename.await.unwrap();
3997
3998// let rename_editor = workspace_b.read_with(cx_b, |workspace, cx| {
3999// workspace
4000// .active_item(cx)
4001// .unwrap()
4002// .downcast::<Editor>()
4003// .unwrap()
4004// });
4005// rename_editor.update(cx_b, |editor, cx| {
4006// assert_eq!(
4007// editor.text(cx),
4008// "const TWO: usize = one::THREE + one::THREE;\nconst THREE: usize = 1;"
4009// );
4010// editor.undo(&Undo, cx);
4011// assert_eq!(
4012// editor.text(cx),
4013// "const TWO: usize = one::ONE + one::ONE;\nconst ONE: usize = 1;"
4014// );
4015// editor.redo(&Redo, cx);
4016// assert_eq!(
4017// editor.text(cx),
4018// "const TWO: usize = one::THREE + one::THREE;\nconst THREE: usize = 1;"
4019// );
4020// });
4021
4022// // Ensure temporary rename edits cannot be undone/redone.
4023// editor_b.update(cx_b, |editor, cx| {
4024// editor.undo(&Undo, cx);
4025// assert_eq!(editor.text(cx), "const ONE: usize = 1;");
4026// editor.undo(&Undo, cx);
4027// assert_eq!(editor.text(cx), "const ONE: usize = 1;");
4028// editor.redo(&Redo, cx);
4029// assert_eq!(editor.text(cx), "const THREE: usize = 1;");
4030// })
4031// }
4032
4033// #[gpui::test(iterations = 10)]
4034// async fn test_basic_chat(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
4035// cx_a.foreground().forbid_parking();
4036
4037// // Connect to a server as 2 clients.
4038// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
4039// let client_a = server.create_client(cx_a, "user_a").await;
4040// let client_b = server.create_client(cx_b, "user_b").await;
4041
4042// // Create an org that includes these 2 users.
4043// let db = &server.app_state.db;
4044// let org_id = db.create_org("Test Org", "test-org").await.unwrap();
4045// db.add_org_member(org_id, client_a.current_user_id(&cx_a), false)
4046// .await
4047// .unwrap();
4048// db.add_org_member(org_id, client_b.current_user_id(&cx_b), false)
4049// .await
4050// .unwrap();
4051
4052// // Create a channel that includes all the users.
4053// let channel_id = db.create_org_channel(org_id, "test-channel").await.unwrap();
4054// db.add_channel_member(channel_id, client_a.current_user_id(&cx_a), false)
4055// .await
4056// .unwrap();
4057// db.add_channel_member(channel_id, client_b.current_user_id(&cx_b), false)
4058// .await
4059// .unwrap();
4060// db.create_channel_message(
4061// channel_id,
4062// client_b.current_user_id(&cx_b),
4063// "hello A, it's B.",
4064// OffsetDateTime::now_utc(),
4065// 1,
4066// )
4067// .await
4068// .unwrap();
4069
4070// let channels_a = cx_a
4071// .add_model(|cx| ChannelList::new(client_a.user_store.clone(), client_a.clone(), cx));
4072// channels_a
4073// .condition(cx_a, |list, _| list.available_channels().is_some())
4074// .await;
4075// channels_a.read_with(cx_a, |list, _| {
4076// assert_eq!(
4077// list.available_channels().unwrap(),
4078// &[ChannelDetails {
4079// id: channel_id.to_proto(),
4080// name: "test-channel".to_string()
4081// }]
4082// )
4083// });
4084// let channel_a = channels_a.update(cx_a, |this, cx| {
4085// this.get_channel(channel_id.to_proto(), cx).unwrap()
4086// });
4087// channel_a.read_with(cx_a, |channel, _| assert!(channel.messages().is_empty()));
4088// channel_a
4089// .condition(&cx_a, |channel, _| {
4090// channel_messages(channel)
4091// == [("user_b".to_string(), "hello A, it's B.".to_string(), false)]
4092// })
4093// .await;
4094
4095// let channels_b = cx_b
4096// .add_model(|cx| ChannelList::new(client_b.user_store.clone(), client_b.clone(), cx));
4097// channels_b
4098// .condition(cx_b, |list, _| list.available_channels().is_some())
4099// .await;
4100// channels_b.read_with(cx_b, |list, _| {
4101// assert_eq!(
4102// list.available_channels().unwrap(),
4103// &[ChannelDetails {
4104// id: channel_id.to_proto(),
4105// name: "test-channel".to_string()
4106// }]
4107// )
4108// });
4109
4110// let channel_b = channels_b.update(cx_b, |this, cx| {
4111// this.get_channel(channel_id.to_proto(), cx).unwrap()
4112// });
4113// channel_b.read_with(cx_b, |channel, _| assert!(channel.messages().is_empty()));
4114// channel_b
4115// .condition(&cx_b, |channel, _| {
4116// channel_messages(channel)
4117// == [("user_b".to_string(), "hello A, it's B.".to_string(), false)]
4118// })
4119// .await;
4120
4121// channel_a
4122// .update(cx_a, |channel, cx| {
4123// channel
4124// .send_message("oh, hi B.".to_string(), cx)
4125// .unwrap()
4126// .detach();
4127// let task = channel.send_message("sup".to_string(), cx).unwrap();
4128// assert_eq!(
4129// channel_messages(channel),
4130// &[
4131// ("user_b".to_string(), "hello A, it's B.".to_string(), false),
4132// ("user_a".to_string(), "oh, hi B.".to_string(), true),
4133// ("user_a".to_string(), "sup".to_string(), true)
4134// ]
4135// );
4136// task
4137// })
4138// .await
4139// .unwrap();
4140
4141// channel_b
4142// .condition(&cx_b, |channel, _| {
4143// channel_messages(channel)
4144// == [
4145// ("user_b".to_string(), "hello A, it's B.".to_string(), false),
4146// ("user_a".to_string(), "oh, hi B.".to_string(), false),
4147// ("user_a".to_string(), "sup".to_string(), false),
4148// ]
4149// })
4150// .await;
4151
4152// assert_eq!(
4153// server
4154// .state()
4155// .await
4156// .channel(channel_id)
4157// .unwrap()
4158// .connection_ids
4159// .len(),
4160// 2
4161// );
4162// cx_b.update(|_| drop(channel_b));
4163// server
4164// .condition(|state| state.channel(channel_id).unwrap().connection_ids.len() == 1)
4165// .await;
4166
4167// cx_a.update(|_| drop(channel_a));
4168// server
4169// .condition(|state| state.channel(channel_id).is_none())
4170// .await;
4171// }
4172
4173// #[gpui::test(iterations = 10)]
4174// async fn test_chat_message_validation(cx_a: &mut TestAppContext) {
4175// cx_a.foreground().forbid_parking();
4176
4177// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
4178// let client_a = server.create_client(cx_a, "user_a").await;
4179
4180// let db = &server.app_state.db;
4181// let org_id = db.create_org("Test Org", "test-org").await.unwrap();
4182// let channel_id = db.create_org_channel(org_id, "test-channel").await.unwrap();
4183// db.add_org_member(org_id, client_a.current_user_id(&cx_a), false)
4184// .await
4185// .unwrap();
4186// db.add_channel_member(channel_id, client_a.current_user_id(&cx_a), false)
4187// .await
4188// .unwrap();
4189
4190// let channels_a = cx_a
4191// .add_model(|cx| ChannelList::new(client_a.user_store.clone(), client_a.clone(), cx));
4192// channels_a
4193// .condition(cx_a, |list, _| list.available_channels().is_some())
4194// .await;
4195// let channel_a = channels_a.update(cx_a, |this, cx| {
4196// this.get_channel(channel_id.to_proto(), cx).unwrap()
4197// });
4198
4199// // Messages aren't allowed to be too long.
4200// channel_a
4201// .update(cx_a, |channel, cx| {
4202// let long_body = "this is long.\n".repeat(1024);
4203// channel.send_message(long_body, cx).unwrap()
4204// })
4205// .await
4206// .unwrap_err();
4207
4208// // Messages aren't allowed to be blank.
4209// channel_a.update(cx_a, |channel, cx| {
4210// channel.send_message(String::new(), cx).unwrap_err()
4211// });
4212
4213// // Leading and trailing whitespace are trimmed.
4214// channel_a
4215// .update(cx_a, |channel, cx| {
4216// channel
4217// .send_message("\n surrounded by whitespace \n".to_string(), cx)
4218// .unwrap()
4219// })
4220// .await
4221// .unwrap();
4222// assert_eq!(
4223// db.get_channel_messages(channel_id, 10, None)
4224// .await
4225// .unwrap()
4226// .iter()
4227// .map(|m| &m.body)
4228// .collect::<Vec<_>>(),
4229// &["surrounded by whitespace"]
4230// );
4231// }
4232
4233// #[gpui::test(iterations = 10)]
4234// async fn test_chat_reconnection(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
4235// cx_a.foreground().forbid_parking();
4236
4237// // Connect to a server as 2 clients.
4238// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
4239// let client_a = server.create_client(cx_a, "user_a").await;
4240// let client_b = server.create_client(cx_b, "user_b").await;
4241// let mut status_b = client_b.status();
4242
4243// // Create an org that includes these 2 users.
4244// let db = &server.app_state.db;
4245// let org_id = db.create_org("Test Org", "test-org").await.unwrap();
4246// db.add_org_member(org_id, client_a.current_user_id(&cx_a), false)
4247// .await
4248// .unwrap();
4249// db.add_org_member(org_id, client_b.current_user_id(&cx_b), false)
4250// .await
4251// .unwrap();
4252
4253// // Create a channel that includes all the users.
4254// let channel_id = db.create_org_channel(org_id, "test-channel").await.unwrap();
4255// db.add_channel_member(channel_id, client_a.current_user_id(&cx_a), false)
4256// .await
4257// .unwrap();
4258// db.add_channel_member(channel_id, client_b.current_user_id(&cx_b), false)
4259// .await
4260// .unwrap();
4261// db.create_channel_message(
4262// channel_id,
4263// client_b.current_user_id(&cx_b),
4264// "hello A, it's B.",
4265// OffsetDateTime::now_utc(),
4266// 2,
4267// )
4268// .await
4269// .unwrap();
4270
4271// let channels_a = cx_a
4272// .add_model(|cx| ChannelList::new(client_a.user_store.clone(), client_a.clone(), cx));
4273// channels_a
4274// .condition(cx_a, |list, _| list.available_channels().is_some())
4275// .await;
4276
4277// channels_a.read_with(cx_a, |list, _| {
4278// assert_eq!(
4279// list.available_channels().unwrap(),
4280// &[ChannelDetails {
4281// id: channel_id.to_proto(),
4282// name: "test-channel".to_string()
4283// }]
4284// )
4285// });
4286// let channel_a = channels_a.update(cx_a, |this, cx| {
4287// this.get_channel(channel_id.to_proto(), cx).unwrap()
4288// });
4289// channel_a.read_with(cx_a, |channel, _| assert!(channel.messages().is_empty()));
4290// channel_a
4291// .condition(&cx_a, |channel, _| {
4292// channel_messages(channel)
4293// == [("user_b".to_string(), "hello A, it's B.".to_string(), false)]
4294// })
4295// .await;
4296
4297// let channels_b = cx_b
4298// .add_model(|cx| ChannelList::new(client_b.user_store.clone(), client_b.clone(), cx));
4299// channels_b
4300// .condition(cx_b, |list, _| list.available_channels().is_some())
4301// .await;
4302// channels_b.read_with(cx_b, |list, _| {
4303// assert_eq!(
4304// list.available_channels().unwrap(),
4305// &[ChannelDetails {
4306// id: channel_id.to_proto(),
4307// name: "test-channel".to_string()
4308// }]
4309// )
4310// });
4311
4312// let channel_b = channels_b.update(cx_b, |this, cx| {
4313// this.get_channel(channel_id.to_proto(), cx).unwrap()
4314// });
4315// channel_b.read_with(cx_b, |channel, _| assert!(channel.messages().is_empty()));
4316// channel_b
4317// .condition(&cx_b, |channel, _| {
4318// channel_messages(channel)
4319// == [("user_b".to_string(), "hello A, it's B.".to_string(), false)]
4320// })
4321// .await;
4322
4323// // Disconnect client B, ensuring we can still access its cached channel data.
4324// server.forbid_connections();
4325// server.disconnect_client(client_b.current_user_id(&cx_b));
4326// cx_b.foreground().advance_clock(Duration::from_secs(3));
4327// while !matches!(
4328// status_b.next().await,
4329// Some(client::Status::ReconnectionError { .. })
4330// ) {}
4331
4332// channels_b.read_with(cx_b, |channels, _| {
4333// assert_eq!(
4334// channels.available_channels().unwrap(),
4335// [ChannelDetails {
4336// id: channel_id.to_proto(),
4337// name: "test-channel".to_string()
4338// }]
4339// )
4340// });
4341// channel_b.read_with(cx_b, |channel, _| {
4342// assert_eq!(
4343// channel_messages(channel),
4344// [("user_b".to_string(), "hello A, it's B.".to_string(), false)]
4345// )
4346// });
4347
4348// // Send a message from client B while it is disconnected.
4349// channel_b
4350// .update(cx_b, |channel, cx| {
4351// let task = channel
4352// .send_message("can you see this?".to_string(), cx)
4353// .unwrap();
4354// assert_eq!(
4355// channel_messages(channel),
4356// &[
4357// ("user_b".to_string(), "hello A, it's B.".to_string(), false),
4358// ("user_b".to_string(), "can you see this?".to_string(), true)
4359// ]
4360// );
4361// task
4362// })
4363// .await
4364// .unwrap_err();
4365
4366// // Send a message from client A while B is disconnected.
4367// channel_a
4368// .update(cx_a, |channel, cx| {
4369// channel
4370// .send_message("oh, hi B.".to_string(), cx)
4371// .unwrap()
4372// .detach();
4373// let task = channel.send_message("sup".to_string(), cx).unwrap();
4374// assert_eq!(
4375// channel_messages(channel),
4376// &[
4377// ("user_b".to_string(), "hello A, it's B.".to_string(), false),
4378// ("user_a".to_string(), "oh, hi B.".to_string(), true),
4379// ("user_a".to_string(), "sup".to_string(), true)
4380// ]
4381// );
4382// task
4383// })
4384// .await
4385// .unwrap();
4386
4387// // Give client B a chance to reconnect.
4388// server.allow_connections();
4389// cx_b.foreground().advance_clock(Duration::from_secs(10));
4390
4391// // Verify that B sees the new messages upon reconnection, as well as the message client B
4392// // sent while offline.
4393// channel_b
4394// .condition(&cx_b, |channel, _| {
4395// channel_messages(channel)
4396// == [
4397// ("user_b".to_string(), "hello A, it's B.".to_string(), false),
4398// ("user_a".to_string(), "oh, hi B.".to_string(), false),
4399// ("user_a".to_string(), "sup".to_string(), false),
4400// ("user_b".to_string(), "can you see this?".to_string(), false),
4401// ]
4402// })
4403// .await;
4404
4405// // Ensure client A and B can communicate normally after reconnection.
4406// channel_a
4407// .update(cx_a, |channel, cx| {
4408// channel.send_message("you online?".to_string(), cx).unwrap()
4409// })
4410// .await
4411// .unwrap();
4412// channel_b
4413// .condition(&cx_b, |channel, _| {
4414// channel_messages(channel)
4415// == [
4416// ("user_b".to_string(), "hello A, it's B.".to_string(), false),
4417// ("user_a".to_string(), "oh, hi B.".to_string(), false),
4418// ("user_a".to_string(), "sup".to_string(), false),
4419// ("user_b".to_string(), "can you see this?".to_string(), false),
4420// ("user_a".to_string(), "you online?".to_string(), false),
4421// ]
4422// })
4423// .await;
4424
4425// channel_b
4426// .update(cx_b, |channel, cx| {
4427// channel.send_message("yep".to_string(), cx).unwrap()
4428// })
4429// .await
4430// .unwrap();
4431// channel_a
4432// .condition(&cx_a, |channel, _| {
4433// channel_messages(channel)
4434// == [
4435// ("user_b".to_string(), "hello A, it's B.".to_string(), false),
4436// ("user_a".to_string(), "oh, hi B.".to_string(), false),
4437// ("user_a".to_string(), "sup".to_string(), false),
4438// ("user_b".to_string(), "can you see this?".to_string(), false),
4439// ("user_a".to_string(), "you online?".to_string(), false),
4440// ("user_b".to_string(), "yep".to_string(), false),
4441// ]
4442// })
4443// .await;
4444// }
4445
4446// #[gpui::test(iterations = 10)]
4447// async fn test_contacts(
4448// cx_a: &mut TestAppContext,
4449// cx_b: &mut TestAppContext,
4450// cx_c: &mut TestAppContext,
4451// ) {
4452// cx_a.foreground().forbid_parking();
4453// let lang_registry = Arc::new(LanguageRegistry::test());
4454// let fs = FakeFs::new(cx_a.background());
4455
4456// // Connect to a server as 3 clients.
4457// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
4458// let client_a = server.create_client(cx_a, "user_a").await;
4459// let client_b = server.create_client(cx_b, "user_b").await;
4460// let client_c = server.create_client(cx_c, "user_c").await;
4461
4462// // Share a worktree as client A.
4463// fs.insert_tree(
4464// "/a",
4465// json!({
4466// ".zed.toml": r#"collaborators = ["user_b", "user_c"]"#,
4467// }),
4468// )
4469// .await;
4470
4471// let project_a = cx_a.update(|cx| {
4472// Project::local(
4473// client_a.clone(),
4474// client_a.user_store.clone(),
4475// lang_registry.clone(),
4476// fs.clone(),
4477// cx,
4478// )
4479// });
4480// let (worktree_a, _) = project_a
4481// .update(cx_a, |p, cx| {
4482// p.find_or_create_local_worktree("/a", true, cx)
4483// })
4484// .await
4485// .unwrap();
4486// worktree_a
4487// .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete())
4488// .await;
4489
4490// client_a
4491// .user_store
4492// .condition(&cx_a, |user_store, _| {
4493// contacts(user_store) == vec![("user_a", vec![("a", false, vec![])])]
4494// })
4495// .await;
4496// client_b
4497// .user_store
4498// .condition(&cx_b, |user_store, _| {
4499// contacts(user_store) == vec![("user_a", vec![("a", false, vec![])])]
4500// })
4501// .await;
4502// client_c
4503// .user_store
4504// .condition(&cx_c, |user_store, _| {
4505// contacts(user_store) == vec![("user_a", vec![("a", false, vec![])])]
4506// })
4507// .await;
4508
4509// let project_id = project_a
4510// .update(cx_a, |project, _| project.next_remote_id())
4511// .await;
4512// project_a
4513// .update(cx_a, |project, cx| project.share(cx))
4514// .await
4515// .unwrap();
4516// client_a
4517// .user_store
4518// .condition(&cx_a, |user_store, _| {
4519// contacts(user_store) == vec![("user_a", vec![("a", true, vec![])])]
4520// })
4521// .await;
4522// client_b
4523// .user_store
4524// .condition(&cx_b, |user_store, _| {
4525// contacts(user_store) == vec![("user_a", vec![("a", true, vec![])])]
4526// })
4527// .await;
4528// client_c
4529// .user_store
4530// .condition(&cx_c, |user_store, _| {
4531// contacts(user_store) == vec![("user_a", vec![("a", true, vec![])])]
4532// })
4533// .await;
4534
4535// let _project_b = Project::remote(
4536// project_id,
4537// client_b.clone(),
4538// client_b.user_store.clone(),
4539// lang_registry.clone(),
4540// fs.clone(),
4541// &mut cx_b.to_async(),
4542// )
4543// .await
4544// .unwrap();
4545
4546// client_a
4547// .user_store
4548// .condition(&cx_a, |user_store, _| {
4549// contacts(user_store) == vec![("user_a", vec![("a", true, vec!["user_b"])])]
4550// })
4551// .await;
4552// client_b
4553// .user_store
4554// .condition(&cx_b, |user_store, _| {
4555// contacts(user_store) == vec![("user_a", vec![("a", true, vec!["user_b"])])]
4556// })
4557// .await;
4558// client_c
4559// .user_store
4560// .condition(&cx_c, |user_store, _| {
4561// contacts(user_store) == vec![("user_a", vec![("a", true, vec!["user_b"])])]
4562// })
4563// .await;
4564
4565// project_a
4566// .condition(&cx_a, |project, _| {
4567// project.collaborators().contains_key(&client_b.peer_id)
4568// })
4569// .await;
4570
4571// cx_a.update(move |_| drop(project_a));
4572// client_a
4573// .user_store
4574// .condition(&cx_a, |user_store, _| contacts(user_store) == vec![])
4575// .await;
4576// client_b
4577// .user_store
4578// .condition(&cx_b, |user_store, _| contacts(user_store) == vec![])
4579// .await;
4580// client_c
4581// .user_store
4582// .condition(&cx_c, |user_store, _| contacts(user_store) == vec![])
4583// .await;
4584
4585// fn contacts(user_store: &UserStore) -> Vec<(&str, Vec<(&str, bool, Vec<&str>)>)> {
4586// user_store
4587// .contacts()
4588// .iter()
4589// .map(|contact| {
4590// let worktrees = contact
4591// .projects
4592// .iter()
4593// .map(|p| {
4594// (
4595// p.worktree_root_names[0].as_str(),
4596// p.is_shared,
4597// p.guests.iter().map(|p| p.github_login.as_str()).collect(),
4598// )
4599// })
4600// .collect();
4601// (contact.user.github_login.as_str(), worktrees)
4602// })
4603// .collect()
4604// }
4605// }
4606
4607// #[gpui::test(iterations = 10)]
4608// async fn test_following(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
4609// cx_a.foreground().forbid_parking();
4610// let fs = FakeFs::new(cx_a.background());
4611
4612// // 2 clients connect to a server.
4613// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
4614// let mut client_a = server.create_client(cx_a, "user_a").await;
4615// let mut client_b = server.create_client(cx_b, "user_b").await;
4616// cx_a.update(editor::init);
4617// cx_b.update(editor::init);
4618
4619// // Client A shares a project.
4620// fs.insert_tree(
4621// "/a",
4622// json!({
4623// ".zed.toml": r#"collaborators = ["user_b"]"#,
4624// "1.txt": "one",
4625// "2.txt": "two",
4626// "3.txt": "three",
4627// }),
4628// )
4629// .await;
4630// let (project_a, worktree_id) = client_a.build_local_project(fs.clone(), "/a", cx_a).await;
4631// project_a
4632// .update(cx_a, |project, cx| project.share(cx))
4633// .await
4634// .unwrap();
4635
4636// // Client B joins the project.
4637// let project_b = client_b
4638// .build_remote_project(
4639// project_a
4640// .read_with(cx_a, |project, _| project.remote_id())
4641// .unwrap(),
4642// cx_b,
4643// )
4644// .await;
4645
4646// // Client A opens some editors.
4647// let workspace_a = client_a.build_workspace(&project_a, cx_a);
4648// let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
4649// let editor_a1 = workspace_a
4650// .update(cx_a, |workspace, cx| {
4651// workspace.open_path((worktree_id, "1.txt"), cx)
4652// })
4653// .await
4654// .unwrap()
4655// .downcast::<Editor>()
4656// .unwrap();
4657// let editor_a2 = workspace_a
4658// .update(cx_a, |workspace, cx| {
4659// workspace.open_path((worktree_id, "2.txt"), cx)
4660// })
4661// .await
4662// .unwrap()
4663// .downcast::<Editor>()
4664// .unwrap();
4665
4666// // Client B opens an editor.
4667// let workspace_b = client_b.build_workspace(&project_b, cx_b);
4668// let editor_b1 = workspace_b
4669// .update(cx_b, |workspace, cx| {
4670// workspace.open_path((worktree_id, "1.txt"), cx)
4671// })
4672// .await
4673// .unwrap()
4674// .downcast::<Editor>()
4675// .unwrap();
4676
4677// let client_a_id = project_b.read_with(cx_b, |project, _| {
4678// project.collaborators().values().next().unwrap().peer_id
4679// });
4680// let client_b_id = project_a.read_with(cx_a, |project, _| {
4681// project.collaborators().values().next().unwrap().peer_id
4682// });
4683
4684// // When client B starts following client A, all visible view states are replicated to client B.
4685// editor_a1.update(cx_a, |editor, cx| editor.select_ranges([0..1], None, cx));
4686// editor_a2.update(cx_a, |editor, cx| editor.select_ranges([2..3], None, cx));
4687// workspace_b
4688// .update(cx_b, |workspace, cx| {
4689// workspace
4690// .toggle_follow(&ToggleFollow(client_a_id), cx)
4691// .unwrap()
4692// })
4693// .await
4694// .unwrap();
4695// let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
4696// workspace
4697// .active_item(cx)
4698// .unwrap()
4699// .downcast::<Editor>()
4700// .unwrap()
4701// });
4702// assert!(cx_b.read(|cx| editor_b2.is_focused(cx)));
4703// assert_eq!(
4704// editor_b2.read_with(cx_b, |editor, cx| editor.project_path(cx)),
4705// Some((worktree_id, "2.txt").into())
4706// );
4707// assert_eq!(
4708// editor_b2.read_with(cx_b, |editor, cx| editor.selected_ranges(cx)),
4709// vec![2..3]
4710// );
4711// assert_eq!(
4712// editor_b1.read_with(cx_b, |editor, cx| editor.selected_ranges(cx)),
4713// vec![0..1]
4714// );
4715
4716// // When client A activates a different editor, client B does so as well.
4717// workspace_a.update(cx_a, |workspace, cx| {
4718// workspace.activate_item(&editor_a1, cx)
4719// });
4720// workspace_b
4721// .condition(cx_b, |workspace, cx| {
4722// workspace.active_item(cx).unwrap().id() == editor_b1.id()
4723// })
4724// .await;
4725
4726// // When client A navigates back and forth, client B does so as well.
4727// workspace_a
4728// .update(cx_a, |workspace, cx| {
4729// workspace::Pane::go_back(workspace, None, cx)
4730// })
4731// .await;
4732// workspace_b
4733// .condition(cx_b, |workspace, cx| {
4734// workspace.active_item(cx).unwrap().id() == editor_b2.id()
4735// })
4736// .await;
4737
4738// workspace_a
4739// .update(cx_a, |workspace, cx| {
4740// workspace::Pane::go_forward(workspace, None, cx)
4741// })
4742// .await;
4743// workspace_b
4744// .condition(cx_b, |workspace, cx| {
4745// workspace.active_item(cx).unwrap().id() == editor_b1.id()
4746// })
4747// .await;
4748
4749// // Changes to client A's editor are reflected on client B.
4750// editor_a1.update(cx_a, |editor, cx| {
4751// editor.select_ranges([1..1, 2..2], None, cx);
4752// });
4753// editor_b1
4754// .condition(cx_b, |editor, cx| {
4755// editor.selected_ranges(cx) == vec![1..1, 2..2]
4756// })
4757// .await;
4758
4759// editor_a1.update(cx_a, |editor, cx| editor.set_text("TWO", cx));
4760// editor_b1
4761// .condition(cx_b, |editor, cx| editor.text(cx) == "TWO")
4762// .await;
4763
4764// editor_a1.update(cx_a, |editor, cx| {
4765// editor.select_ranges([3..3], None, cx);
4766// editor.set_scroll_position(vec2f(0., 100.), cx);
4767// });
4768// editor_b1
4769// .condition(cx_b, |editor, cx| editor.selected_ranges(cx) == vec![3..3])
4770// .await;
4771
4772// // After unfollowing, client B stops receiving updates from client A.
4773// workspace_b.update(cx_b, |workspace, cx| {
4774// workspace.unfollow(&workspace.active_pane().clone(), cx)
4775// });
4776// workspace_a.update(cx_a, |workspace, cx| {
4777// workspace.activate_item(&editor_a2, cx)
4778// });
4779// cx_a.foreground().run_until_parked();
4780// assert_eq!(
4781// workspace_b.read_with(cx_b, |workspace, cx| workspace
4782// .active_item(cx)
4783// .unwrap()
4784// .id()),
4785// editor_b1.id()
4786// );
4787
4788// // Client A starts following client B.
4789// workspace_a
4790// .update(cx_a, |workspace, cx| {
4791// workspace
4792// .toggle_follow(&ToggleFollow(client_b_id), cx)
4793// .unwrap()
4794// })
4795// .await
4796// .unwrap();
4797// assert_eq!(
4798// workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
4799// Some(client_b_id)
4800// );
4801// assert_eq!(
4802// workspace_a.read_with(cx_a, |workspace, cx| workspace
4803// .active_item(cx)
4804// .unwrap()
4805// .id()),
4806// editor_a1.id()
4807// );
4808
4809// // Following interrupts when client B disconnects.
4810// client_b.disconnect(&cx_b.to_async()).unwrap();
4811// cx_a.foreground().run_until_parked();
4812// assert_eq!(
4813// workspace_a.read_with(cx_a, |workspace, _| workspace.leader_for_pane(&pane_a)),
4814// None
4815// );
4816// }
4817
4818// #[gpui::test(iterations = 10)]
4819// async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
4820// cx_a.foreground().forbid_parking();
4821// let fs = FakeFs::new(cx_a.background());
4822
4823// // 2 clients connect to a server.
4824// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
4825// let mut client_a = server.create_client(cx_a, "user_a").await;
4826// let mut client_b = server.create_client(cx_b, "user_b").await;
4827// cx_a.update(editor::init);
4828// cx_b.update(editor::init);
4829
4830// // Client A shares a project.
4831// fs.insert_tree(
4832// "/a",
4833// json!({
4834// ".zed.toml": r#"collaborators = ["user_b"]"#,
4835// "1.txt": "one",
4836// "2.txt": "two",
4837// "3.txt": "three",
4838// "4.txt": "four",
4839// }),
4840// )
4841// .await;
4842// let (project_a, worktree_id) = client_a.build_local_project(fs.clone(), "/a", cx_a).await;
4843// project_a
4844// .update(cx_a, |project, cx| project.share(cx))
4845// .await
4846// .unwrap();
4847
4848// // Client B joins the project.
4849// let project_b = client_b
4850// .build_remote_project(
4851// project_a
4852// .read_with(cx_a, |project, _| project.remote_id())
4853// .unwrap(),
4854// cx_b,
4855// )
4856// .await;
4857
4858// // Client A opens some editors.
4859// let workspace_a = client_a.build_workspace(&project_a, cx_a);
4860// let pane_a1 = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
4861// let _editor_a1 = workspace_a
4862// .update(cx_a, |workspace, cx| {
4863// workspace.open_path((worktree_id, "1.txt"), cx)
4864// })
4865// .await
4866// .unwrap()
4867// .downcast::<Editor>()
4868// .unwrap();
4869
4870// // Client B opens an editor.
4871// let workspace_b = client_b.build_workspace(&project_b, cx_b);
4872// let pane_b1 = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
4873// let _editor_b1 = workspace_b
4874// .update(cx_b, |workspace, cx| {
4875// workspace.open_path((worktree_id, "2.txt"), cx)
4876// })
4877// .await
4878// .unwrap()
4879// .downcast::<Editor>()
4880// .unwrap();
4881
4882// // Clients A and B follow each other in split panes
4883// workspace_a
4884// .update(cx_a, |workspace, cx| {
4885// workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
4886// assert_ne!(*workspace.active_pane(), pane_a1);
4887// let leader_id = *project_a.read(cx).collaborators().keys().next().unwrap();
4888// workspace
4889// .toggle_follow(&workspace::ToggleFollow(leader_id), cx)
4890// .unwrap()
4891// })
4892// .await
4893// .unwrap();
4894// workspace_b
4895// .update(cx_b, |workspace, cx| {
4896// workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
4897// assert_ne!(*workspace.active_pane(), pane_b1);
4898// let leader_id = *project_b.read(cx).collaborators().keys().next().unwrap();
4899// workspace
4900// .toggle_follow(&workspace::ToggleFollow(leader_id), cx)
4901// .unwrap()
4902// })
4903// .await
4904// .unwrap();
4905
4906// workspace_a
4907// .update(cx_a, |workspace, cx| {
4908// workspace.activate_next_pane(cx);
4909// assert_eq!(*workspace.active_pane(), pane_a1);
4910// workspace.open_path((worktree_id, "3.txt"), cx)
4911// })
4912// .await
4913// .unwrap();
4914// workspace_b
4915// .update(cx_b, |workspace, cx| {
4916// workspace.activate_next_pane(cx);
4917// assert_eq!(*workspace.active_pane(), pane_b1);
4918// workspace.open_path((worktree_id, "4.txt"), cx)
4919// })
4920// .await
4921// .unwrap();
4922// cx_a.foreground().run_until_parked();
4923
4924// // Ensure leader updates don't change the active pane of followers
4925// workspace_a.read_with(cx_a, |workspace, _| {
4926// assert_eq!(*workspace.active_pane(), pane_a1);
4927// });
4928// workspace_b.read_with(cx_b, |workspace, _| {
4929// assert_eq!(*workspace.active_pane(), pane_b1);
4930// });
4931
4932// // Ensure peers following each other doesn't cause an infinite loop.
4933// assert_eq!(
4934// workspace_a.read_with(cx_a, |workspace, cx| workspace
4935// .active_item(cx)
4936// .unwrap()
4937// .project_path(cx)),
4938// Some((worktree_id, "3.txt").into())
4939// );
4940// workspace_a.update(cx_a, |workspace, cx| {
4941// assert_eq!(
4942// workspace.active_item(cx).unwrap().project_path(cx),
4943// Some((worktree_id, "3.txt").into())
4944// );
4945// workspace.activate_next_pane(cx);
4946// assert_eq!(
4947// workspace.active_item(cx).unwrap().project_path(cx),
4948// Some((worktree_id, "4.txt").into())
4949// );
4950// });
4951// workspace_b.update(cx_b, |workspace, cx| {
4952// assert_eq!(
4953// workspace.active_item(cx).unwrap().project_path(cx),
4954// Some((worktree_id, "4.txt").into())
4955// );
4956// workspace.activate_next_pane(cx);
4957// assert_eq!(
4958// workspace.active_item(cx).unwrap().project_path(cx),
4959// Some((worktree_id, "3.txt").into())
4960// );
4961// });
4962// }
4963
4964// #[gpui::test(iterations = 10)]
4965// async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
4966// cx_a.foreground().forbid_parking();
4967// let fs = FakeFs::new(cx_a.background());
4968
4969// // 2 clients connect to a server.
4970// let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
4971// let mut client_a = server.create_client(cx_a, "user_a").await;
4972// let mut client_b = server.create_client(cx_b, "user_b").await;
4973// cx_a.update(editor::init);
4974// cx_b.update(editor::init);
4975
4976// // Client A shares a project.
4977// fs.insert_tree(
4978// "/a",
4979// json!({
4980// ".zed.toml": r#"collaborators = ["user_b"]"#,
4981// "1.txt": "one",
4982// "2.txt": "two",
4983// "3.txt": "three",
4984// }),
4985// )
4986// .await;
4987// let (project_a, worktree_id) = client_a.build_local_project(fs.clone(), "/a", cx_a).await;
4988// project_a
4989// .update(cx_a, |project, cx| project.share(cx))
4990// .await
4991// .unwrap();
4992
4993// // Client B joins the project.
4994// let project_b = client_b
4995// .build_remote_project(
4996// project_a
4997// .read_with(cx_a, |project, _| project.remote_id())
4998// .unwrap(),
4999// cx_b,
5000// )
5001// .await;
5002
5003// // Client A opens some editors.
5004// let workspace_a = client_a.build_workspace(&project_a, cx_a);
5005// let _editor_a1 = workspace_a
5006// .update(cx_a, |workspace, cx| {
5007// workspace.open_path((worktree_id, "1.txt"), cx)
5008// })
5009// .await
5010// .unwrap()
5011// .downcast::<Editor>()
5012// .unwrap();
5013
5014// // Client B starts following client A.
5015// let workspace_b = client_b.build_workspace(&project_b, cx_b);
5016// let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
5017// let leader_id = project_b.read_with(cx_b, |project, _| {
5018// project.collaborators().values().next().unwrap().peer_id
5019// });
5020// workspace_b
5021// .update(cx_b, |workspace, cx| {
5022// workspace
5023// .toggle_follow(&ToggleFollow(leader_id), cx)
5024// .unwrap()
5025// })
5026// .await
5027// .unwrap();
5028// assert_eq!(
5029// workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5030// Some(leader_id)
5031// );
5032// let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| {
5033// workspace
5034// .active_item(cx)
5035// .unwrap()
5036// .downcast::<Editor>()
5037// .unwrap()
5038// });
5039
5040// // When client B moves, it automatically stops following client A.
5041// editor_b2.update(cx_b, |editor, cx| editor.move_right(&editor::MoveRight, cx));
5042// assert_eq!(
5043// workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5044// None
5045// );
5046
5047// workspace_b
5048// .update(cx_b, |workspace, cx| {
5049// workspace
5050// .toggle_follow(&ToggleFollow(leader_id), cx)
5051// .unwrap()
5052// })
5053// .await
5054// .unwrap();
5055// assert_eq!(
5056// workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5057// Some(leader_id)
5058// );
5059
5060// // When client B edits, it automatically stops following client A.
5061// editor_b2.update(cx_b, |editor, cx| editor.insert("X", cx));
5062// assert_eq!(
5063// workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5064// None
5065// );
5066
5067// workspace_b
5068// .update(cx_b, |workspace, cx| {
5069// workspace
5070// .toggle_follow(&ToggleFollow(leader_id), cx)
5071// .unwrap()
5072// })
5073// .await
5074// .unwrap();
5075// assert_eq!(
5076// workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5077// Some(leader_id)
5078// );
5079
5080// // When client B scrolls, it automatically stops following client A.
5081// editor_b2.update(cx_b, |editor, cx| {
5082// editor.set_scroll_position(vec2f(0., 3.), cx)
5083// });
5084// assert_eq!(
5085// workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5086// None
5087// );
5088
5089// workspace_b
5090// .update(cx_b, |workspace, cx| {
5091// workspace
5092// .toggle_follow(&ToggleFollow(leader_id), cx)
5093// .unwrap()
5094// })
5095// .await
5096// .unwrap();
5097// assert_eq!(
5098// workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5099// Some(leader_id)
5100// );
5101
5102// // When client B activates a different pane, it continues following client A in the original pane.
5103// workspace_b.update(cx_b, |workspace, cx| {
5104// workspace.split_pane(pane_b.clone(), SplitDirection::Right, cx)
5105// });
5106// assert_eq!(
5107// workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5108// Some(leader_id)
5109// );
5110
5111// workspace_b.update(cx_b, |workspace, cx| workspace.activate_next_pane(cx));
5112// assert_eq!(
5113// workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5114// Some(leader_id)
5115// );
5116
5117// // When client B activates a different item in the original pane, it automatically stops following client A.
5118// workspace_b
5119// .update(cx_b, |workspace, cx| {
5120// workspace.open_path((worktree_id, "2.txt"), cx)
5121// })
5122// .await
5123// .unwrap();
5124// assert_eq!(
5125// workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
5126// None
5127// );
5128// }
5129
5130// #[gpui::test(iterations = 100)]
5131// async fn test_random_collaboration(
5132// cx: &mut TestAppContext,
5133// deterministic: Arc<Deterministic>,
5134// rng: StdRng,
5135// ) {
5136// cx.foreground().forbid_parking();
5137// let max_peers = env::var("MAX_PEERS")
5138// .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
5139// .unwrap_or(5);
5140// assert!(max_peers <= 5);
5141
5142// let max_operations = env::var("OPERATIONS")
5143// .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
5144// .unwrap_or(10);
5145
5146// let rng = Arc::new(Mutex::new(rng));
5147
5148// let guest_lang_registry = Arc::new(LanguageRegistry::test());
5149// let host_language_registry = Arc::new(LanguageRegistry::test());
5150
5151// let fs = FakeFs::new(cx.background());
5152// fs.insert_tree(
5153// "/_collab",
5154// json!({
5155// ".zed.toml": r#"collaborators = ["guest-1", "guest-2", "guest-3", "guest-4"]"#
5156// }),
5157// )
5158// .await;
5159
5160// let mut server = TestServer::start(cx.foreground(), cx.background()).await;
5161// let mut clients = Vec::new();
5162// let mut user_ids = Vec::new();
5163// let mut op_start_signals = Vec::new();
5164// let files = Arc::new(Mutex::new(Vec::new()));
5165
5166// let mut next_entity_id = 100000;
5167// let mut host_cx = TestAppContext::new(
5168// cx.foreground_platform(),
5169// cx.platform(),
5170// deterministic.build_foreground(next_entity_id),
5171// deterministic.build_background(),
5172// cx.font_cache(),
5173// cx.leak_detector(),
5174// next_entity_id,
5175// );
5176// let host = server.create_client(&mut host_cx, "host").await;
5177// let host_project = host_cx.update(|cx| {
5178// Project::local(
5179// host.client.clone(),
5180// host.user_store.clone(),
5181// host_language_registry.clone(),
5182// fs.clone(),
5183// cx,
5184// )
5185// });
5186// let host_project_id = host_project
5187// .update(&mut host_cx, |p, _| p.next_remote_id())
5188// .await;
5189
5190// let (collab_worktree, _) = host_project
5191// .update(&mut host_cx, |project, cx| {
5192// project.find_or_create_local_worktree("/_collab", true, cx)
5193// })
5194// .await
5195// .unwrap();
5196// collab_worktree
5197// .read_with(&host_cx, |tree, _| tree.as_local().unwrap().scan_complete())
5198// .await;
5199// host_project
5200// .update(&mut host_cx, |project, cx| project.share(cx))
5201// .await
5202// .unwrap();
5203
5204// // Set up fake language servers.
5205// let mut language = Language::new(
5206// LanguageConfig {
5207// name: "Rust".into(),
5208// path_suffixes: vec!["rs".to_string()],
5209// ..Default::default()
5210// },
5211// None,
5212// );
5213// let _fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
5214// name: "the-fake-language-server",
5215// capabilities: lsp::LanguageServer::full_capabilities(),
5216// initializer: Some(Box::new({
5217// let rng = rng.clone();
5218// let files = files.clone();
5219// let project = host_project.downgrade();
5220// move |fake_server: &mut FakeLanguageServer| {
5221// fake_server.handle_request::<lsp::request::Completion, _, _>(
5222// |_, _| async move {
5223// Ok(Some(lsp::CompletionResponse::Array(vec![
5224// lsp::CompletionItem {
5225// text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
5226// range: lsp::Range::new(
5227// lsp::Position::new(0, 0),
5228// lsp::Position::new(0, 0),
5229// ),
5230// new_text: "the-new-text".to_string(),
5231// })),
5232// ..Default::default()
5233// },
5234// ])))
5235// },
5236// );
5237
5238// fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>(
5239// |_, _| async move {
5240// Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
5241// lsp::CodeAction {
5242// title: "the-code-action".to_string(),
5243// ..Default::default()
5244// },
5245// )]))
5246// },
5247// );
5248
5249// fake_server.handle_request::<lsp::request::PrepareRenameRequest, _, _>(
5250// |params, _| async move {
5251// Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
5252// params.position,
5253// params.position,
5254// ))))
5255// },
5256// );
5257
5258// fake_server.handle_request::<lsp::request::GotoDefinition, _, _>({
5259// let files = files.clone();
5260// let rng = rng.clone();
5261// move |_, _| {
5262// let files = files.clone();
5263// let rng = rng.clone();
5264// async move {
5265// let files = files.lock();
5266// let mut rng = rng.lock();
5267// let count = rng.gen_range::<usize, _>(1..3);
5268// let files = (0..count)
5269// .map(|_| files.choose(&mut *rng).unwrap())
5270// .collect::<Vec<_>>();
5271// log::info!("LSP: Returning definitions in files {:?}", &files);
5272// Ok(Some(lsp::GotoDefinitionResponse::Array(
5273// files
5274// .into_iter()
5275// .map(|file| lsp::Location {
5276// uri: lsp::Url::from_file_path(file).unwrap(),
5277// range: Default::default(),
5278// })
5279// .collect(),
5280// )))
5281// }
5282// }
5283// });
5284
5285// fake_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>({
5286// let rng = rng.clone();
5287// let project = project.clone();
5288// move |params, mut cx| {
5289// let highlights = if let Some(project) = project.upgrade(&cx) {
5290// project.update(&mut cx, |project, cx| {
5291// let path = params
5292// .text_document_position_params
5293// .text_document
5294// .uri
5295// .to_file_path()
5296// .unwrap();
5297// let (worktree, relative_path) =
5298// project.find_local_worktree(&path, cx)?;
5299// let project_path =
5300// ProjectPath::from((worktree.read(cx).id(), relative_path));
5301// let buffer =
5302// project.get_open_buffer(&project_path, cx)?.read(cx);
5303
5304// let mut highlights = Vec::new();
5305// let highlight_count = rng.lock().gen_range(1..=5);
5306// let mut prev_end = 0;
5307// for _ in 0..highlight_count {
5308// let range =
5309// buffer.random_byte_range(prev_end, &mut *rng.lock());
5310
5311// highlights.push(lsp::DocumentHighlight {
5312// range: range_to_lsp(range.to_point_utf16(buffer)),
5313// kind: Some(lsp::DocumentHighlightKind::READ),
5314// });
5315// prev_end = range.end;
5316// }
5317// Some(highlights)
5318// })
5319// } else {
5320// None
5321// };
5322// async move { Ok(highlights) }
5323// }
5324// });
5325// }
5326// })),
5327// ..Default::default()
5328// });
5329// host_language_registry.add(Arc::new(language));
5330
5331// let op_start_signal = futures::channel::mpsc::unbounded();
5332// user_ids.push(host.current_user_id(&host_cx));
5333// op_start_signals.push(op_start_signal.0);
5334// clients.push(host_cx.foreground().spawn(host.simulate_host(
5335// host_project,
5336// files,
5337// op_start_signal.1,
5338// rng.clone(),
5339// host_cx,
5340// )));
5341
5342// let disconnect_host_at = if rng.lock().gen_bool(0.2) {
5343// rng.lock().gen_range(0..max_operations)
5344// } else {
5345// max_operations
5346// };
5347// let mut available_guests = vec![
5348// "guest-1".to_string(),
5349// "guest-2".to_string(),
5350// "guest-3".to_string(),
5351// "guest-4".to_string(),
5352// ];
5353// let mut operations = 0;
5354// while operations < max_operations {
5355// if operations == disconnect_host_at {
5356// server.disconnect_client(user_ids[0]);
5357// cx.foreground().advance_clock(RECEIVE_TIMEOUT);
5358// drop(op_start_signals);
5359// let mut clients = futures::future::join_all(clients).await;
5360// cx.foreground().run_until_parked();
5361
5362// let (host, mut host_cx, host_err) = clients.remove(0);
5363// if let Some(host_err) = host_err {
5364// log::error!("host error - {}", host_err);
5365// }
5366// host.project
5367// .as_ref()
5368// .unwrap()
5369// .read_with(&host_cx, |project, _| assert!(!project.is_shared()));
5370// for (guest, mut guest_cx, guest_err) in clients {
5371// if let Some(guest_err) = guest_err {
5372// log::error!("{} error - {}", guest.username, guest_err);
5373// }
5374// let contacts = server
5375// .store
5376// .read()
5377// .await
5378// .contacts_for_user(guest.current_user_id(&guest_cx));
5379// assert!(!contacts
5380// .iter()
5381// .flat_map(|contact| &contact.projects)
5382// .any(|project| project.id == host_project_id));
5383// guest
5384// .project
5385// .as_ref()
5386// .unwrap()
5387// .read_with(&guest_cx, |project, _| assert!(project.is_read_only()));
5388// guest_cx.update(|_| drop(guest));
5389// }
5390// host_cx.update(|_| drop(host));
5391
5392// return;
5393// }
5394
5395// let distribution = rng.lock().gen_range(0..100);
5396// match distribution {
5397// 0..=19 if !available_guests.is_empty() => {
5398// let guest_ix = rng.lock().gen_range(0..available_guests.len());
5399// let guest_username = available_guests.remove(guest_ix);
5400// log::info!("Adding new connection for {}", guest_username);
5401// next_entity_id += 100000;
5402// let mut guest_cx = TestAppContext::new(
5403// cx.foreground_platform(),
5404// cx.platform(),
5405// deterministic.build_foreground(next_entity_id),
5406// deterministic.build_background(),
5407// cx.font_cache(),
5408// cx.leak_detector(),
5409// next_entity_id,
5410// );
5411// let guest = server.create_client(&mut guest_cx, &guest_username).await;
5412// let guest_project = Project::remote(
5413// host_project_id,
5414// guest.client.clone(),
5415// guest.user_store.clone(),
5416// guest_lang_registry.clone(),
5417// FakeFs::new(cx.background()),
5418// &mut guest_cx.to_async(),
5419// )
5420// .await
5421// .unwrap();
5422// let op_start_signal = futures::channel::mpsc::unbounded();
5423// user_ids.push(guest.current_user_id(&guest_cx));
5424// op_start_signals.push(op_start_signal.0);
5425// clients.push(guest_cx.foreground().spawn(guest.simulate_guest(
5426// guest_username.clone(),
5427// guest_project,
5428// op_start_signal.1,
5429// rng.clone(),
5430// guest_cx,
5431// )));
5432
5433// log::info!("Added connection for {}", guest_username);
5434// operations += 1;
5435// }
5436// 20..=29 if clients.len() > 1 => {
5437// log::info!("Removing guest");
5438// let guest_ix = rng.lock().gen_range(1..clients.len());
5439// let removed_guest_id = user_ids.remove(guest_ix);
5440// let guest = clients.remove(guest_ix);
5441// op_start_signals.remove(guest_ix);
5442// server.disconnect_client(removed_guest_id);
5443// cx.foreground().advance_clock(RECEIVE_TIMEOUT);
5444// let (guest, mut guest_cx, guest_err) = guest.await;
5445// if let Some(guest_err) = guest_err {
5446// log::error!("{} error - {}", guest.username, guest_err);
5447// }
5448// guest
5449// .project
5450// .as_ref()
5451// .unwrap()
5452// .read_with(&guest_cx, |project, _| assert!(project.is_read_only()));
5453// for user_id in &user_ids {
5454// for contact in server.store.read().await.contacts_for_user(*user_id) {
5455// assert_ne!(
5456// contact.user_id, removed_guest_id.0 as u64,
5457// "removed guest is still a contact of another peer"
5458// );
5459// for project in contact.projects {
5460// for project_guest_id in project.guests {
5461// assert_ne!(
5462// project_guest_id, removed_guest_id.0 as u64,
5463// "removed guest appears as still participating on a project"
5464// );
5465// }
5466// }
5467// }
5468// }
5469
5470// log::info!("{} removed", guest.username);
5471// available_guests.push(guest.username.clone());
5472// guest_cx.update(|_| drop(guest));
5473
5474// operations += 1;
5475// }
5476// _ => {
5477// while operations < max_operations && rng.lock().gen_bool(0.7) {
5478// op_start_signals
5479// .choose(&mut *rng.lock())
5480// .unwrap()
5481// .unbounded_send(())
5482// .unwrap();
5483// operations += 1;
5484// }
5485
5486// if rng.lock().gen_bool(0.8) {
5487// cx.foreground().run_until_parked();
5488// }
5489// }
5490// }
5491// }
5492
5493// drop(op_start_signals);
5494// let mut clients = futures::future::join_all(clients).await;
5495// cx.foreground().run_until_parked();
5496
5497// let (host_client, mut host_cx, host_err) = clients.remove(0);
5498// if let Some(host_err) = host_err {
5499// panic!("host error - {}", host_err);
5500// }
5501// let host_project = host_client.project.as_ref().unwrap();
5502// let host_worktree_snapshots = host_project.read_with(&host_cx, |project, cx| {
5503// project
5504// .worktrees(cx)
5505// .map(|worktree| {
5506// let snapshot = worktree.read(cx).snapshot();
5507// (snapshot.id(), snapshot)
5508// })
5509// .collect::<BTreeMap<_, _>>()
5510// });
5511
5512// host_client
5513// .project
5514// .as_ref()
5515// .unwrap()
5516// .read_with(&host_cx, |project, cx| project.check_invariants(cx));
5517
5518// for (guest_client, mut guest_cx, guest_err) in clients.into_iter() {
5519// if let Some(guest_err) = guest_err {
5520// panic!("{} error - {}", guest_client.username, guest_err);
5521// }
5522// let worktree_snapshots =
5523// guest_client
5524// .project
5525// .as_ref()
5526// .unwrap()
5527// .read_with(&guest_cx, |project, cx| {
5528// project
5529// .worktrees(cx)
5530// .map(|worktree| {
5531// let worktree = worktree.read(cx);
5532// (worktree.id(), worktree.snapshot())
5533// })
5534// .collect::<BTreeMap<_, _>>()
5535// });
5536
5537// assert_eq!(
5538// worktree_snapshots.keys().collect::<Vec<_>>(),
5539// host_worktree_snapshots.keys().collect::<Vec<_>>(),
5540// "{} has different worktrees than the host",
5541// guest_client.username
5542// );
5543// for (id, host_snapshot) in &host_worktree_snapshots {
5544// let guest_snapshot = &worktree_snapshots[id];
5545// assert_eq!(
5546// guest_snapshot.root_name(),
5547// host_snapshot.root_name(),
5548// "{} has different root name than the host for worktree {}",
5549// guest_client.username,
5550// id
5551// );
5552// assert_eq!(
5553// guest_snapshot.entries(false).collect::<Vec<_>>(),
5554// host_snapshot.entries(false).collect::<Vec<_>>(),
5555// "{} has different snapshot than the host for worktree {}",
5556// guest_client.username,
5557// id
5558// );
5559// }
5560
5561// guest_client
5562// .project
5563// .as_ref()
5564// .unwrap()
5565// .read_with(&guest_cx, |project, cx| project.check_invariants(cx));
5566
5567// for guest_buffer in &guest_client.buffers {
5568// let buffer_id = guest_buffer.read_with(&guest_cx, |buffer, _| buffer.remote_id());
5569// let host_buffer = host_project.read_with(&host_cx, |project, cx| {
5570// project.buffer_for_id(buffer_id, cx).expect(&format!(
5571// "host does not have buffer for guest:{}, peer:{}, id:{}",
5572// guest_client.username, guest_client.peer_id, buffer_id
5573// ))
5574// });
5575// let path = host_buffer
5576// .read_with(&host_cx, |buffer, cx| buffer.file().unwrap().full_path(cx));
5577
5578// assert_eq!(
5579// guest_buffer.read_with(&guest_cx, |buffer, _| buffer.deferred_ops_len()),
5580// 0,
5581// "{}, buffer {}, path {:?} has deferred operations",
5582// guest_client.username,
5583// buffer_id,
5584// path,
5585// );
5586// assert_eq!(
5587// guest_buffer.read_with(&guest_cx, |buffer, _| buffer.text()),
5588// host_buffer.read_with(&host_cx, |buffer, _| buffer.text()),
5589// "{}, buffer {}, path {:?}, differs from the host's buffer",
5590// guest_client.username,
5591// buffer_id,
5592// path
5593// );
5594// }
5595
5596// guest_cx.update(|_| drop(guest_client));
5597// }
5598
5599// host_cx.update(|_| drop(host_client));
5600// }
5601
5602// struct TestServer {
5603// peer: Arc<Peer>,
5604// app_state: Arc<AppState>,
5605// server: Arc<Server>,
5606// foreground: Rc<executor::Foreground>,
5607// notifications: mpsc::UnboundedReceiver<()>,
5608// connection_killers: Arc<Mutex<HashMap<UserId, Arc<AtomicBool>>>>,
5609// forbid_connections: Arc<AtomicBool>,
5610// _test_db: TestDb,
5611// }
5612
5613// impl TestServer {
5614// async fn start(
5615// foreground: Rc<executor::Foreground>,
5616// background: Arc<executor::Background>,
5617// ) -> Self {
5618// let test_db = TestDb::fake(background);
5619// let app_state = Self::build_app_state(&test_db).await;
5620// let peer = Peer::new();
5621// let notifications = mpsc::unbounded();
5622// let server = Server::new(app_state.clone(), peer.clone(), Some(notifications.0));
5623// Self {
5624// peer,
5625// app_state,
5626// server,
5627// foreground,
5628// notifications: notifications.1,
5629// connection_killers: Default::default(),
5630// forbid_connections: Default::default(),
5631// _test_db: test_db,
5632// }
5633// }
5634
5635// async fn create_client(&mut self, cx: &mut TestAppContext, name: &str) -> TestClient {
5636// cx.update(|cx| {
5637// let settings = Settings::test(cx);
5638// cx.set_global(settings);
5639// });
5640
5641// let http = FakeHttpClient::with_404_response();
5642// let user_id = self.app_state.db.create_user(name, false).await.unwrap();
5643// let client_name = name.to_string();
5644// let mut client = Client::new(http.clone());
5645// let server = self.server.clone();
5646// let connection_killers = self.connection_killers.clone();
5647// let forbid_connections = self.forbid_connections.clone();
5648// let (connection_id_tx, mut connection_id_rx) = mpsc::channel(16);
5649
5650// Arc::get_mut(&mut client)
5651// .unwrap()
5652// .override_authenticate(move |cx| {
5653// cx.spawn(|_| async move {
5654// let access_token = "the-token".to_string();
5655// Ok(Credentials {
5656// user_id: user_id.0 as u64,
5657// access_token,
5658// })
5659// })
5660// })
5661// .override_establish_connection(move |credentials, cx| {
5662// assert_eq!(credentials.user_id, user_id.0 as u64);
5663// assert_eq!(credentials.access_token, "the-token");
5664
5665// let server = server.clone();
5666// let connection_killers = connection_killers.clone();
5667// let forbid_connections = forbid_connections.clone();
5668// let client_name = client_name.clone();
5669// let connection_id_tx = connection_id_tx.clone();
5670// cx.spawn(move |cx| async move {
5671// if forbid_connections.load(SeqCst) {
5672// Err(EstablishConnectionError::other(anyhow!(
5673// "server is forbidding connections"
5674// )))
5675// } else {
5676// let (client_conn, server_conn, killed) =
5677// Connection::in_memory(cx.background());
5678// connection_killers.lock().insert(user_id, killed);
5679// cx.background()
5680// .spawn(server.handle_connection(
5681// server_conn,
5682// client_name,
5683// user_id,
5684// Some(connection_id_tx),
5685// cx.background(),
5686// ))
5687// .detach();
5688// Ok(client_conn)
5689// }
5690// })
5691// });
5692
5693// client
5694// .authenticate_and_connect(false, &cx.to_async())
5695// .await
5696// .unwrap();
5697
5698// Channel::init(&client);
5699// Project::init(&client);
5700// cx.update(|cx| {
5701// workspace::init(&client, cx);
5702// });
5703
5704// let peer_id = PeerId(connection_id_rx.next().await.unwrap().0);
5705// let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
5706
5707// let client = TestClient {
5708// client,
5709// peer_id,
5710// username: name.to_string(),
5711// user_store,
5712// language_registry: Arc::new(LanguageRegistry::test()),
5713// project: Default::default(),
5714// buffers: Default::default(),
5715// };
5716// client.wait_for_current_user(cx).await;
5717// client
5718// }
5719
5720// fn disconnect_client(&self, user_id: UserId) {
5721// self.connection_killers
5722// .lock()
5723// .remove(&user_id)
5724// .unwrap()
5725// .store(true, SeqCst);
5726// }
5727
5728// fn forbid_connections(&self) {
5729// self.forbid_connections.store(true, SeqCst);
5730// }
5731
5732// fn allow_connections(&self) {
5733// self.forbid_connections.store(false, SeqCst);
5734// }
5735
5736// async fn build_app_state(test_db: &TestDb) -> Arc<AppState> {
5737// let mut config = Config::default();
5738// config.database_url = test_db.url.clone();
5739// Arc::new(AppState {
5740// db: test_db.db().clone(),
5741// config,
5742// })
5743// }
5744
5745// async fn state<'a>(&'a self) -> RwLockReadGuard<'a, Store> {
5746// self.server.store.read().await
5747// }
5748
5749// async fn condition<F>(&mut self, mut predicate: F)
5750// where
5751// F: FnMut(&Store) -> bool,
5752// {
5753// async_std::future::timeout(Duration::from_millis(500), async {
5754// while !(predicate)(&*self.server.store.read().await) {
5755// self.foreground.start_waiting();
5756// self.notifications.next().await;
5757// self.foreground.finish_waiting();
5758// }
5759// })
5760// .await
5761// .expect("condition timed out");
5762// }
5763// }
5764
5765// impl Deref for TestServer {
5766// type Target = Server;
5767
5768// fn deref(&self) -> &Self::Target {
5769// &self.server
5770// }
5771// }
5772
5773// impl Drop for TestServer {
5774// fn drop(&mut self) {
5775// self.peer.reset();
5776// }
5777// }
5778
5779// struct TestClient {
5780// client: Arc<Client>,
5781// username: String,
5782// pub peer_id: PeerId,
5783// pub user_store: ModelHandle<UserStore>,
5784// language_registry: Arc<LanguageRegistry>,
5785// project: Option<ModelHandle<Project>>,
5786// buffers: HashSet<ModelHandle<language::Buffer>>,
5787// }
5788
5789// impl Deref for TestClient {
5790// type Target = Arc<Client>;
5791
5792// fn deref(&self) -> &Self::Target {
5793// &self.client
5794// }
5795// }
5796
5797// impl TestClient {
5798// pub fn current_user_id(&self, cx: &TestAppContext) -> UserId {
5799// UserId::from_proto(
5800// self.user_store
5801// .read_with(cx, |user_store, _| user_store.current_user().unwrap().id),
5802// )
5803// }
5804
5805// async fn wait_for_current_user(&self, cx: &TestAppContext) {
5806// let mut authed_user = self
5807// .user_store
5808// .read_with(cx, |user_store, _| user_store.watch_current_user());
5809// while authed_user.next().await.unwrap().is_none() {}
5810// }
5811
5812// async fn build_local_project(
5813// &mut self,
5814// fs: Arc<FakeFs>,
5815// root_path: impl AsRef<Path>,
5816// cx: &mut TestAppContext,
5817// ) -> (ModelHandle<Project>, WorktreeId) {
5818// let project = cx.update(|cx| {
5819// Project::local(
5820// self.client.clone(),
5821// self.user_store.clone(),
5822// self.language_registry.clone(),
5823// fs,
5824// cx,
5825// )
5826// });
5827// self.project = Some(project.clone());
5828// let (worktree, _) = project
5829// .update(cx, |p, cx| {
5830// p.find_or_create_local_worktree(root_path, true, cx)
5831// })
5832// .await
5833// .unwrap();
5834// worktree
5835// .read_with(cx, |tree, _| tree.as_local().unwrap().scan_complete())
5836// .await;
5837// project
5838// .update(cx, |project, _| project.next_remote_id())
5839// .await;
5840// (project, worktree.read_with(cx, |tree, _| tree.id()))
5841// }
5842
5843// async fn build_remote_project(
5844// &mut self,
5845// project_id: u64,
5846// cx: &mut TestAppContext,
5847// ) -> ModelHandle<Project> {
5848// let project = Project::remote(
5849// project_id,
5850// self.client.clone(),
5851// self.user_store.clone(),
5852// self.language_registry.clone(),
5853// FakeFs::new(cx.background()),
5854// &mut cx.to_async(),
5855// )
5856// .await
5857// .unwrap();
5858// self.project = Some(project.clone());
5859// project
5860// }
5861
5862// fn build_workspace(
5863// &self,
5864// project: &ModelHandle<Project>,
5865// cx: &mut TestAppContext,
5866// ) -> ViewHandle<Workspace> {
5867// let (window_id, _) = cx.add_window(|_| EmptyView);
5868// cx.add_view(window_id, |cx| {
5869// let fs = project.read(cx).fs().clone();
5870// Workspace::new(
5871// &WorkspaceParams {
5872// fs,
5873// project: project.clone(),
5874// user_store: self.user_store.clone(),
5875// languages: self.language_registry.clone(),
5876// themes: ThemeRegistry::new((), cx.font_cache().clone()),
5877// channel_list: cx.add_model(|cx| {
5878// ChannelList::new(self.user_store.clone(), self.client.clone(), cx)
5879// }),
5880// client: self.client.clone(),
5881// },
5882// cx,
5883// )
5884// })
5885// }
5886
5887// async fn simulate_host(
5888// mut self,
5889// project: ModelHandle<Project>,
5890// files: Arc<Mutex<Vec<PathBuf>>>,
5891// op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>,
5892// rng: Arc<Mutex<StdRng>>,
5893// mut cx: TestAppContext,
5894// ) -> (Self, TestAppContext, Option<anyhow::Error>) {
5895// async fn simulate_host_internal(
5896// client: &mut TestClient,
5897// project: ModelHandle<Project>,
5898// files: Arc<Mutex<Vec<PathBuf>>>,
5899// mut op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>,
5900// rng: Arc<Mutex<StdRng>>,
5901// cx: &mut TestAppContext,
5902// ) -> anyhow::Result<()> {
5903// let fs = project.read_with(cx, |project, _| project.fs().clone());
5904
5905// while op_start_signal.next().await.is_some() {
5906// let distribution = rng.lock().gen_range::<usize, _>(0..100);
5907// match distribution {
5908// 0..=20 if !files.lock().is_empty() => {
5909// let path = files.lock().choose(&mut *rng.lock()).unwrap().clone();
5910// let mut path = path.as_path();
5911// while let Some(parent_path) = path.parent() {
5912// path = parent_path;
5913// if rng.lock().gen() {
5914// break;
5915// }
5916// }
5917
5918// log::info!("Host: find/create local worktree {:?}", path);
5919// let find_or_create_worktree = project.update(cx, |project, cx| {
5920// project.find_or_create_local_worktree(path, true, cx)
5921// });
5922// if rng.lock().gen() {
5923// cx.background().spawn(find_or_create_worktree).detach();
5924// } else {
5925// find_or_create_worktree.await?;
5926// }
5927// }
5928// 10..=80 if !files.lock().is_empty() => {
5929// let buffer = if client.buffers.is_empty() || rng.lock().gen() {
5930// let file = files.lock().choose(&mut *rng.lock()).unwrap().clone();
5931// let (worktree, path) = project
5932// .update(cx, |project, cx| {
5933// project.find_or_create_local_worktree(
5934// file.clone(),
5935// true,
5936// cx,
5937// )
5938// })
5939// .await?;
5940// let project_path =
5941// worktree.read_with(cx, |worktree, _| (worktree.id(), path));
5942// log::info!(
5943// "Host: opening path {:?}, worktree {}, relative_path {:?}",
5944// file,
5945// project_path.0,
5946// project_path.1
5947// );
5948// let buffer = project
5949// .update(cx, |project, cx| project.open_buffer(project_path, cx))
5950// .await
5951// .unwrap();
5952// client.buffers.insert(buffer.clone());
5953// buffer
5954// } else {
5955// client
5956// .buffers
5957// .iter()
5958// .choose(&mut *rng.lock())
5959// .unwrap()
5960// .clone()
5961// };
5962
5963// if rng.lock().gen_bool(0.1) {
5964// cx.update(|cx| {
5965// log::info!(
5966// "Host: dropping buffer {:?}",
5967// buffer.read(cx).file().unwrap().full_path(cx)
5968// );
5969// client.buffers.remove(&buffer);
5970// drop(buffer);
5971// });
5972// } else {
5973// buffer.update(cx, |buffer, cx| {
5974// log::info!(
5975// "Host: updating buffer {:?} ({})",
5976// buffer.file().unwrap().full_path(cx),
5977// buffer.remote_id()
5978// );
5979
5980// if rng.lock().gen_bool(0.7) {
5981// buffer.randomly_edit(&mut *rng.lock(), 5, cx);
5982// } else {
5983// buffer.randomly_undo_redo(&mut *rng.lock(), cx);
5984// }
5985// });
5986// }
5987// }
5988// _ => loop {
5989// let path_component_count = rng.lock().gen_range::<usize, _>(1..=5);
5990// let mut path = PathBuf::new();
5991// path.push("/");
5992// for _ in 0..path_component_count {
5993// let letter = rng.lock().gen_range(b'a'..=b'z');
5994// path.push(std::str::from_utf8(&[letter]).unwrap());
5995// }
5996// path.set_extension("rs");
5997// let parent_path = path.parent().unwrap();
5998
5999// log::info!("Host: creating file {:?}", path,);
6000
6001// if fs.create_dir(&parent_path).await.is_ok()
6002// && fs.create_file(&path, Default::default()).await.is_ok()
6003// {
6004// files.lock().push(path);
6005// break;
6006// } else {
6007// log::info!("Host: cannot create file");
6008// }
6009// },
6010// }
6011
6012// cx.background().simulate_random_delay().await;
6013// }
6014
6015// Ok(())
6016// }
6017
6018// let result = simulate_host_internal(
6019// &mut self,
6020// project.clone(),
6021// files,
6022// op_start_signal,
6023// rng,
6024// &mut cx,
6025// )
6026// .await;
6027// log::info!("Host done");
6028// self.project = Some(project);
6029// (self, cx, result.err())
6030// }
6031
6032// pub async fn simulate_guest(
6033// mut self,
6034// guest_username: String,
6035// project: ModelHandle<Project>,
6036// op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>,
6037// rng: Arc<Mutex<StdRng>>,
6038// mut cx: TestAppContext,
6039// ) -> (Self, TestAppContext, Option<anyhow::Error>) {
6040// async fn simulate_guest_internal(
6041// client: &mut TestClient,
6042// guest_username: &str,
6043// project: ModelHandle<Project>,
6044// mut op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>,
6045// rng: Arc<Mutex<StdRng>>,
6046// cx: &mut TestAppContext,
6047// ) -> anyhow::Result<()> {
6048// while op_start_signal.next().await.is_some() {
6049// let buffer = if client.buffers.is_empty() || rng.lock().gen() {
6050// let worktree = if let Some(worktree) =
6051// project.read_with(cx, |project, cx| {
6052// project
6053// .worktrees(&cx)
6054// .filter(|worktree| {
6055// let worktree = worktree.read(cx);
6056// worktree.is_visible()
6057// && worktree.entries(false).any(|e| e.is_file())
6058// })
6059// .choose(&mut *rng.lock())
6060// }) {
6061// worktree
6062// } else {
6063// cx.background().simulate_random_delay().await;
6064// continue;
6065// };
6066
6067// let (worktree_root_name, project_path) =
6068// worktree.read_with(cx, |worktree, _| {
6069// let entry = worktree
6070// .entries(false)
6071// .filter(|e| e.is_file())
6072// .choose(&mut *rng.lock())
6073// .unwrap();
6074// (
6075// worktree.root_name().to_string(),
6076// (worktree.id(), entry.path.clone()),
6077// )
6078// });
6079// log::info!(
6080// "{}: opening path {:?} in worktree {} ({})",
6081// guest_username,
6082// project_path.1,
6083// project_path.0,
6084// worktree_root_name,
6085// );
6086// let buffer = project
6087// .update(cx, |project, cx| {
6088// project.open_buffer(project_path.clone(), cx)
6089// })
6090// .await?;
6091// log::info!(
6092// "{}: opened path {:?} in worktree {} ({}) with buffer id {}",
6093// guest_username,
6094// project_path.1,
6095// project_path.0,
6096// worktree_root_name,
6097// buffer.read_with(cx, |buffer, _| buffer.remote_id())
6098// );
6099// client.buffers.insert(buffer.clone());
6100// buffer
6101// } else {
6102// client
6103// .buffers
6104// .iter()
6105// .choose(&mut *rng.lock())
6106// .unwrap()
6107// .clone()
6108// };
6109
6110// let choice = rng.lock().gen_range(0..100);
6111// match choice {
6112// 0..=9 => {
6113// cx.update(|cx| {
6114// log::info!(
6115// "{}: dropping buffer {:?}",
6116// guest_username,
6117// buffer.read(cx).file().unwrap().full_path(cx)
6118// );
6119// client.buffers.remove(&buffer);
6120// drop(buffer);
6121// });
6122// }
6123// 10..=19 => {
6124// let completions = project.update(cx, |project, cx| {
6125// log::info!(
6126// "{}: requesting completions for buffer {} ({:?})",
6127// guest_username,
6128// buffer.read(cx).remote_id(),
6129// buffer.read(cx).file().unwrap().full_path(cx)
6130// );
6131// let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
6132// project.completions(&buffer, offset, cx)
6133// });
6134// let completions = cx.background().spawn(async move {
6135// completions
6136// .await
6137// .map_err(|err| anyhow!("completions request failed: {:?}", err))
6138// });
6139// if rng.lock().gen_bool(0.3) {
6140// log::info!("{}: detaching completions request", guest_username);
6141// cx.update(|cx| completions.detach_and_log_err(cx));
6142// } else {
6143// completions.await?;
6144// }
6145// }
6146// 20..=29 => {
6147// let code_actions = project.update(cx, |project, cx| {
6148// log::info!(
6149// "{}: requesting code actions for buffer {} ({:?})",
6150// guest_username,
6151// buffer.read(cx).remote_id(),
6152// buffer.read(cx).file().unwrap().full_path(cx)
6153// );
6154// let range = buffer.read(cx).random_byte_range(0, &mut *rng.lock());
6155// project.code_actions(&buffer, range, cx)
6156// });
6157// let code_actions = cx.background().spawn(async move {
6158// code_actions.await.map_err(|err| {
6159// anyhow!("code actions request failed: {:?}", err)
6160// })
6161// });
6162// if rng.lock().gen_bool(0.3) {
6163// log::info!("{}: detaching code actions request", guest_username);
6164// cx.update(|cx| code_actions.detach_and_log_err(cx));
6165// } else {
6166// code_actions.await?;
6167// }
6168// }
6169// 30..=39 if buffer.read_with(cx, |buffer, _| buffer.is_dirty()) => {
6170// let (requested_version, save) = buffer.update(cx, |buffer, cx| {
6171// log::info!(
6172// "{}: saving buffer {} ({:?})",
6173// guest_username,
6174// buffer.remote_id(),
6175// buffer.file().unwrap().full_path(cx)
6176// );
6177// (buffer.version(), buffer.save(cx))
6178// });
6179// let save = cx.background().spawn(async move {
6180// let (saved_version, _) = save
6181// .await
6182// .map_err(|err| anyhow!("save request failed: {:?}", err))?;
6183// assert!(saved_version.observed_all(&requested_version));
6184// Ok::<_, anyhow::Error>(())
6185// });
6186// if rng.lock().gen_bool(0.3) {
6187// log::info!("{}: detaching save request", guest_username);
6188// cx.update(|cx| save.detach_and_log_err(cx));
6189// } else {
6190// save.await?;
6191// }
6192// }
6193// 40..=44 => {
6194// let prepare_rename = project.update(cx, |project, cx| {
6195// log::info!(
6196// "{}: preparing rename for buffer {} ({:?})",
6197// guest_username,
6198// buffer.read(cx).remote_id(),
6199// buffer.read(cx).file().unwrap().full_path(cx)
6200// );
6201// let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
6202// project.prepare_rename(buffer, offset, cx)
6203// });
6204// let prepare_rename = cx.background().spawn(async move {
6205// prepare_rename.await.map_err(|err| {
6206// anyhow!("prepare rename request failed: {:?}", err)
6207// })
6208// });
6209// if rng.lock().gen_bool(0.3) {
6210// log::info!("{}: detaching prepare rename request", guest_username);
6211// cx.update(|cx| prepare_rename.detach_and_log_err(cx));
6212// } else {
6213// prepare_rename.await?;
6214// }
6215// }
6216// 45..=49 => {
6217// let definitions = project.update(cx, |project, cx| {
6218// log::info!(
6219// "{}: requesting definitions for buffer {} ({:?})",
6220// guest_username,
6221// buffer.read(cx).remote_id(),
6222// buffer.read(cx).file().unwrap().full_path(cx)
6223// );
6224// let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
6225// project.definition(&buffer, offset, cx)
6226// });
6227// let definitions = cx.background().spawn(async move {
6228// definitions
6229// .await
6230// .map_err(|err| anyhow!("definitions request failed: {:?}", err))
6231// });
6232// if rng.lock().gen_bool(0.3) {
6233// log::info!("{}: detaching definitions request", guest_username);
6234// cx.update(|cx| definitions.detach_and_log_err(cx));
6235// } else {
6236// client
6237// .buffers
6238// .extend(definitions.await?.into_iter().map(|loc| loc.buffer));
6239// }
6240// }
6241// 50..=54 => {
6242// let highlights = project.update(cx, |project, cx| {
6243// log::info!(
6244// "{}: requesting highlights for buffer {} ({:?})",
6245// guest_username,
6246// buffer.read(cx).remote_id(),
6247// buffer.read(cx).file().unwrap().full_path(cx)
6248// );
6249// let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
6250// project.document_highlights(&buffer, offset, cx)
6251// });
6252// let highlights = cx.background().spawn(async move {
6253// highlights
6254// .await
6255// .map_err(|err| anyhow!("highlights request failed: {:?}", err))
6256// });
6257// if rng.lock().gen_bool(0.3) {
6258// log::info!("{}: detaching highlights request", guest_username);
6259// cx.update(|cx| highlights.detach_and_log_err(cx));
6260// } else {
6261// highlights.await?;
6262// }
6263// }
6264// 55..=59 => {
6265// let search = project.update(cx, |project, cx| {
6266// let query = rng.lock().gen_range('a'..='z');
6267// log::info!("{}: project-wide search {:?}", guest_username, query);
6268// project.search(SearchQuery::text(query, false, false), cx)
6269// });
6270// let search = cx.background().spawn(async move {
6271// search
6272// .await
6273// .map_err(|err| anyhow!("search request failed: {:?}", err))
6274// });
6275// if rng.lock().gen_bool(0.3) {
6276// log::info!("{}: detaching search request", guest_username);
6277// cx.update(|cx| search.detach_and_log_err(cx));
6278// } else {
6279// client.buffers.extend(search.await?.into_keys());
6280// }
6281// }
6282// _ => {
6283// buffer.update(cx, |buffer, cx| {
6284// log::info!(
6285// "{}: updating buffer {} ({:?})",
6286// guest_username,
6287// buffer.remote_id(),
6288// buffer.file().unwrap().full_path(cx)
6289// );
6290// if rng.lock().gen_bool(0.7) {
6291// buffer.randomly_edit(&mut *rng.lock(), 5, cx);
6292// } else {
6293// buffer.randomly_undo_redo(&mut *rng.lock(), cx);
6294// }
6295// });
6296// }
6297// }
6298// cx.background().simulate_random_delay().await;
6299// }
6300// Ok(())
6301// }
6302
6303// let result = simulate_guest_internal(
6304// &mut self,
6305// &guest_username,
6306// project.clone(),
6307// op_start_signal,
6308// rng,
6309// &mut cx,
6310// )
6311// .await;
6312// log::info!("{}: done", guest_username);
6313
6314// self.project = Some(project);
6315// (self, cx, result.err())
6316// }
6317// }
6318
6319// impl Drop for TestClient {
6320// fn drop(&mut self) {
6321// self.client.tear_down();
6322// }
6323// }
6324
6325// impl Executor for Arc<gpui::executor::Background> {
6326// type Timer = gpui::executor::Timer;
6327
6328// fn spawn_detached<F: 'static + Send + Future<Output = ()>>(&self, future: F) {
6329// self.spawn(future).detach();
6330// }
6331
6332// fn timer(&self, duration: Duration) -> Self::Timer {
6333// self.as_ref().timer(duration)
6334// }
6335// }
6336
6337// fn channel_messages(channel: &Channel) -> Vec<(String, String, bool)> {
6338// channel
6339// .messages()
6340// .cursor::<()>()
6341// .map(|m| {
6342// (
6343// m.sender.github_login.clone(),
6344// m.body.clone(),
6345// m.is_pending(),
6346// )
6347// })
6348// .collect()
6349// }
6350
6351// struct EmptyView;
6352
6353// impl gpui::Entity for EmptyView {
6354// type Event = ();
6355// }
6356
6357// impl gpui::View for EmptyView {
6358// fn ui_name() -> &'static str {
6359// "empty view"
6360// }
6361
6362// fn render(&mut self, _: &mut gpui::RenderContext<Self>) -> gpui::ElementBox {
6363// gpui::Element::boxed(gpui::elements::Empty)
6364// }
6365// }
6366// }
6367
6368use axum::{body::Body, Router};
6369use client::Peer;