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