1// use editor::{
2// test::editor_test_context::EditorTestContext, ConfirmCodeAction, ConfirmCompletion,
3// ConfirmRename, Editor, Redo, Rename, ToggleCodeActions, Undo,
4// };
5
6//todo!(editor)
7// #[gpui::test(iterations = 10)]
8// async fn test_host_disconnect(
9// executor: BackgroundExecutor,
10// cx_a: &mut TestAppContext,
11// cx_b: &mut TestAppContext,
12// cx_c: &mut TestAppContext,
13// ) {
14// let mut server = TestServer::start(&executor).await;
15// let client_a = server.create_client(cx_a, "user_a").await;
16// let client_b = server.create_client(cx_b, "user_b").await;
17// let client_c = server.create_client(cx_c, "user_c").await;
18// server
19// .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
20// .await;
21
22// cx_b.update(editor::init);
23
24// client_a
25// .fs()
26// .insert_tree(
27// "/a",
28// json!({
29// "a.txt": "a-contents",
30// "b.txt": "b-contents",
31// }),
32// )
33// .await;
34
35// let active_call_a = cx_a.read(ActiveCall::global);
36// let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
37
38// let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
39// let project_id = active_call_a
40// .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
41// .await
42// .unwrap();
43
44// let project_b = client_b.build_remote_project(project_id, cx_b).await;
45// executor.run_until_parked();
46
47// assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
48
49// let window_b =
50// cx_b.add_window(|cx| Workspace::new(0, project_b.clone(), client_b.app_state.clone(), cx));
51// let workspace_b = window_b.root(cx_b);
52// let editor_b = workspace_b
53// .update(cx_b, |workspace, cx| {
54// workspace.open_path((worktree_id, "b.txt"), None, true, cx)
55// })
56// .await
57// .unwrap()
58// .downcast::<Editor>()
59// .unwrap();
60
61// assert!(window_b.read_with(cx_b, |cx| editor_b.is_focused(cx)));
62// editor_b.update(cx_b, |editor, cx| editor.insert("X", cx));
63// assert!(window_b.is_edited(cx_b));
64
65// // Drop client A's connection. Collaborators should disappear and the project should not be shown as shared.
66// server.forbid_connections();
67// server.disconnect_client(client_a.peer_id().unwrap());
68// executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
69
70// project_a.read_with(cx_a, |project, _| project.collaborators().is_empty());
71
72// project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
73
74// project_b.read_with(cx_b, |project, _| project.is_read_only());
75
76// assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared()));
77
78// // Ensure client B's edited state is reset and that the whole window is blurred.
79
80// window_b.read_with(cx_b, |cx| {
81// assert_eq!(cx.focused_view_id(), None);
82// });
83// assert!(!window_b.is_edited(cx_b));
84
85// // Ensure client B is not prompted to save edits when closing window after disconnecting.
86// let can_close = workspace_b
87// .update(cx_b, |workspace, cx| workspace.prepare_to_close(true, cx))
88// .await
89// .unwrap();
90// assert!(can_close);
91
92// // Allow client A to reconnect to the server.
93// server.allow_connections();
94// executor.advance_clock(RECEIVE_TIMEOUT);
95
96// // Client B calls client A again after they reconnected.
97// let active_call_b = cx_b.read(ActiveCall::global);
98// active_call_b
99// .update(cx_b, |call, cx| {
100// call.invite(client_a.user_id().unwrap(), None, cx)
101// })
102// .await
103// .unwrap();
104// executor.run_until_parked();
105// active_call_a
106// .update(cx_a, |call, cx| call.accept_incoming(cx))
107// .await
108// .unwrap();
109
110// active_call_a
111// .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
112// .await
113// .unwrap();
114
115// // Drop client A's connection again. We should still unshare it successfully.
116// server.forbid_connections();
117// server.disconnect_client(client_a.peer_id().unwrap());
118// executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
119
120// project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
121// }
122
123//todo!(editor)
124// #[gpui::test]
125// async fn test_newline_above_or_below_does_not_move_guest_cursor(
126// executor: BackgroundExecutor,
127// cx_a: &mut TestAppContext,
128// cx_b: &mut TestAppContext,
129// ) {
130// let mut server = TestServer::start(&executor).await;
131// let client_a = server.create_client(cx_a, "user_a").await;
132// let client_b = server.create_client(cx_b, "user_b").await;
133// server
134// .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
135// .await;
136// let active_call_a = cx_a.read(ActiveCall::global);
137
138// client_a
139// .fs()
140// .insert_tree("/dir", json!({ "a.txt": "Some text\n" }))
141// .await;
142// let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
143// let project_id = active_call_a
144// .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
145// .await
146// .unwrap();
147
148// let project_b = client_b.build_remote_project(project_id, cx_b).await;
149
150// // Open a buffer as client A
151// let buffer_a = project_a
152// .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
153// .await
154// .unwrap();
155// let window_a = cx_a.add_window(|_| EmptyView);
156// let editor_a = window_a.add_view(cx_a, |cx| Editor::for_buffer(buffer_a, Some(project_a), cx));
157// let mut editor_cx_a = EditorTestContext {
158// cx: cx_a,
159// window: window_a.into(),
160// editor: editor_a,
161// };
162
163// // Open a buffer as client B
164// let buffer_b = project_b
165// .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
166// .await
167// .unwrap();
168// let window_b = cx_b.add_window(|_| EmptyView);
169// let editor_b = window_b.add_view(cx_b, |cx| Editor::for_buffer(buffer_b, Some(project_b), cx));
170// let mut editor_cx_b = EditorTestContext {
171// cx: cx_b,
172// window: window_b.into(),
173// editor: editor_b,
174// };
175
176// // Test newline above
177// editor_cx_a.set_selections_state(indoc! {"
178// Some textˇ
179// "});
180// editor_cx_b.set_selections_state(indoc! {"
181// Some textˇ
182// "});
183// editor_cx_a.update_editor(|editor, cx| editor.newline_above(&editor::NewlineAbove, cx));
184// executor.run_until_parked();
185// editor_cx_a.assert_editor_state(indoc! {"
186// ˇ
187// Some text
188// "});
189// editor_cx_b.assert_editor_state(indoc! {"
190
191// Some textˇ
192// "});
193
194// // Test newline below
195// editor_cx_a.set_selections_state(indoc! {"
196
197// Some textˇ
198// "});
199// editor_cx_b.set_selections_state(indoc! {"
200
201// Some textˇ
202// "});
203// editor_cx_a.update_editor(|editor, cx| editor.newline_below(&editor::NewlineBelow, cx));
204// executor.run_until_parked();
205// editor_cx_a.assert_editor_state(indoc! {"
206
207// Some text
208// ˇ
209// "});
210// editor_cx_b.assert_editor_state(indoc! {"
211
212// Some textˇ
213
214// "});
215// }
216
217//todo!(editor)
218// #[gpui::test(iterations = 10)]
219// async fn test_collaborating_with_completion(
220// executor: BackgroundExecutor,
221// cx_a: &mut TestAppContext,
222// cx_b: &mut TestAppContext,
223// ) {
224// let mut server = TestServer::start(&executor).await;
225// let client_a = server.create_client(cx_a, "user_a").await;
226// let client_b = server.create_client(cx_b, "user_b").await;
227// server
228// .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
229// .await;
230// let active_call_a = cx_a.read(ActiveCall::global);
231
232// // Set up a fake language server.
233// let mut language = Language::new(
234// LanguageConfig {
235// name: "Rust".into(),
236// path_suffixes: vec!["rs".to_string()],
237// ..Default::default()
238// },
239// Some(tree_sitter_rust::language()),
240// );
241// let mut fake_language_servers = language
242// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
243// capabilities: lsp::ServerCapabilities {
244// completion_provider: Some(lsp::CompletionOptions {
245// trigger_characters: Some(vec![".".to_string()]),
246// resolve_provider: Some(true),
247// ..Default::default()
248// }),
249// ..Default::default()
250// },
251// ..Default::default()
252// }))
253// .await;
254// client_a.language_registry().add(Arc::new(language));
255
256// client_a
257// .fs()
258// .insert_tree(
259// "/a",
260// json!({
261// "main.rs": "fn main() { a }",
262// "other.rs": "",
263// }),
264// )
265// .await;
266// let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
267// let project_id = active_call_a
268// .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
269// .await
270// .unwrap();
271// let project_b = client_b.build_remote_project(project_id, cx_b).await;
272
273// // Open a file in an editor as the guest.
274// let buffer_b = project_b
275// .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
276// .await
277// .unwrap();
278// let window_b = cx_b.add_window(|_| EmptyView);
279// let editor_b = window_b.add_view(cx_b, |cx| {
280// Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
281// });
282
283// let fake_language_server = fake_language_servers.next().await.unwrap();
284// cx_a.foreground().run_until_parked();
285
286// buffer_b.read_with(cx_b, |buffer, _| {
287// assert!(!buffer.completion_triggers().is_empty())
288// });
289
290// // Type a completion trigger character as the guest.
291// editor_b.update(cx_b, |editor, cx| {
292// editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
293// editor.handle_input(".", cx);
294// cx.focus(&editor_b);
295// });
296
297// // Receive a completion request as the host's language server.
298// // Return some completions from the host's language server.
299// cx_a.foreground().start_waiting();
300// fake_language_server
301// .handle_request::<lsp::request::Completion, _, _>(|params, _| async move {
302// assert_eq!(
303// params.text_document_position.text_document.uri,
304// lsp::Url::from_file_path("/a/main.rs").unwrap(),
305// );
306// assert_eq!(
307// params.text_document_position.position,
308// lsp::Position::new(0, 14),
309// );
310
311// Ok(Some(lsp::CompletionResponse::Array(vec![
312// lsp::CompletionItem {
313// label: "first_method(…)".into(),
314// detail: Some("fn(&mut self, B) -> C".into()),
315// text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
316// new_text: "first_method($1)".to_string(),
317// range: lsp::Range::new(
318// lsp::Position::new(0, 14),
319// lsp::Position::new(0, 14),
320// ),
321// })),
322// insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
323// ..Default::default()
324// },
325// lsp::CompletionItem {
326// label: "second_method(…)".into(),
327// detail: Some("fn(&mut self, C) -> D<E>".into()),
328// text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
329// new_text: "second_method()".to_string(),
330// range: lsp::Range::new(
331// lsp::Position::new(0, 14),
332// lsp::Position::new(0, 14),
333// ),
334// })),
335// insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
336// ..Default::default()
337// },
338// ])))
339// })
340// .next()
341// .await
342// .unwrap();
343// cx_a.foreground().finish_waiting();
344
345// // Open the buffer on the host.
346// let buffer_a = project_a
347// .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
348// .await
349// .unwrap();
350// cx_a.foreground().run_until_parked();
351
352// buffer_a.read_with(cx_a, |buffer, _| {
353// assert_eq!(buffer.text(), "fn main() { a. }")
354// });
355
356// // Confirm a completion on the guest.
357
358// editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
359// editor_b.update(cx_b, |editor, cx| {
360// editor.confirm_completion(&ConfirmCompletion { item_ix: Some(0) }, cx);
361// assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
362// });
363
364// // Return a resolved completion from the host's language server.
365// // The resolved completion has an additional text edit.
366// fake_language_server.handle_request::<lsp::request::ResolveCompletionItem, _, _>(
367// |params, _| async move {
368// assert_eq!(params.label, "first_method(…)");
369// Ok(lsp::CompletionItem {
370// label: "first_method(…)".into(),
371// detail: Some("fn(&mut self, B) -> C".into()),
372// text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
373// new_text: "first_method($1)".to_string(),
374// range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
375// })),
376// additional_text_edits: Some(vec![lsp::TextEdit {
377// new_text: "use d::SomeTrait;\n".to_string(),
378// range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
379// }]),
380// insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
381// ..Default::default()
382// })
383// },
384// );
385
386// // The additional edit is applied.
387// cx_a.foreground().run_until_parked();
388
389// buffer_a.read_with(cx_a, |buffer, _| {
390// assert_eq!(
391// buffer.text(),
392// "use d::SomeTrait;\nfn main() { a.first_method() }"
393// );
394// });
395
396// buffer_b.read_with(cx_b, |buffer, _| {
397// assert_eq!(
398// buffer.text(),
399// "use d::SomeTrait;\nfn main() { a.first_method() }"
400// );
401// });
402// }
403//todo!(editor)
404// #[gpui::test(iterations = 10)]
405// async fn test_collaborating_with_code_actions(
406// executor: BackgroundExecutor,
407// cx_a: &mut TestAppContext,
408// cx_b: &mut TestAppContext,
409// ) {
410// let mut server = TestServer::start(&executor).await;
411// let client_a = server.create_client(cx_a, "user_a").await;
412// //
413// let client_b = server.create_client(cx_b, "user_b").await;
414// server
415// .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
416// .await;
417// let active_call_a = cx_a.read(ActiveCall::global);
418
419// cx_b.update(editor::init);
420
421// // Set up a fake language server.
422// let mut language = Language::new(
423// LanguageConfig {
424// name: "Rust".into(),
425// path_suffixes: vec!["rs".to_string()],
426// ..Default::default()
427// },
428// Some(tree_sitter_rust::language()),
429// );
430// let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
431// client_a.language_registry().add(Arc::new(language));
432
433// client_a
434// .fs()
435// .insert_tree(
436// "/a",
437// json!({
438// "main.rs": "mod other;\nfn main() { let foo = other::foo(); }",
439// "other.rs": "pub fn foo() -> usize { 4 }",
440// }),
441// )
442// .await;
443// let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
444// let project_id = active_call_a
445// .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
446// .await
447// .unwrap();
448
449// // Join the project as client B.
450// let project_b = client_b.build_remote_project(project_id, cx_b).await;
451// let window_b =
452// cx_b.add_window(|cx| Workspace::new(0, project_b.clone(), client_b.app_state.clone(), cx));
453// let workspace_b = window_b.root(cx_b);
454// let editor_b = workspace_b
455// .update(cx_b, |workspace, cx| {
456// workspace.open_path((worktree_id, "main.rs"), None, true, cx)
457// })
458// .await
459// .unwrap()
460// .downcast::<Editor>()
461// .unwrap();
462
463// let mut fake_language_server = fake_language_servers.next().await.unwrap();
464// let mut requests = fake_language_server
465// .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
466// assert_eq!(
467// params.text_document.uri,
468// lsp::Url::from_file_path("/a/main.rs").unwrap(),
469// );
470// assert_eq!(params.range.start, lsp::Position::new(0, 0));
471// assert_eq!(params.range.end, lsp::Position::new(0, 0));
472// Ok(None)
473// });
474// executor.advance_clock(editor::CODE_ACTIONS_DEBOUNCE_TIMEOUT * 2);
475// requests.next().await;
476
477// // Move cursor to a location that contains code actions.
478// editor_b.update(cx_b, |editor, cx| {
479// editor.change_selections(None, cx, |s| {
480// s.select_ranges([Point::new(1, 31)..Point::new(1, 31)])
481// });
482// cx.focus(&editor_b);
483// });
484
485// let mut requests = fake_language_server
486// .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
487// assert_eq!(
488// params.text_document.uri,
489// lsp::Url::from_file_path("/a/main.rs").unwrap(),
490// );
491// assert_eq!(params.range.start, lsp::Position::new(1, 31));
492// assert_eq!(params.range.end, lsp::Position::new(1, 31));
493
494// Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
495// lsp::CodeAction {
496// title: "Inline into all callers".to_string(),
497// edit: Some(lsp::WorkspaceEdit {
498// changes: Some(
499// [
500// (
501// lsp::Url::from_file_path("/a/main.rs").unwrap(),
502// vec![lsp::TextEdit::new(
503// lsp::Range::new(
504// lsp::Position::new(1, 22),
505// lsp::Position::new(1, 34),
506// ),
507// "4".to_string(),
508// )],
509// ),
510// (
511// lsp::Url::from_file_path("/a/other.rs").unwrap(),
512// vec![lsp::TextEdit::new(
513// lsp::Range::new(
514// lsp::Position::new(0, 0),
515// lsp::Position::new(0, 27),
516// ),
517// "".to_string(),
518// )],
519// ),
520// ]
521// .into_iter()
522// .collect(),
523// ),
524// ..Default::default()
525// }),
526// data: Some(json!({
527// "codeActionParams": {
528// "range": {
529// "start": {"line": 1, "column": 31},
530// "end": {"line": 1, "column": 31},
531// }
532// }
533// })),
534// ..Default::default()
535// },
536// )]))
537// });
538// executor.advance_clock(editor::CODE_ACTIONS_DEBOUNCE_TIMEOUT * 2);
539// requests.next().await;
540
541// // Toggle code actions and wait for them to display.
542// editor_b.update(cx_b, |editor, cx| {
543// editor.toggle_code_actions(
544// &ToggleCodeActions {
545// deployed_from_indicator: false,
546// },
547// cx,
548// );
549// });
550// cx_a.foreground().run_until_parked();
551
552// editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
553
554// fake_language_server.remove_request_handler::<lsp::request::CodeActionRequest>();
555
556// // Confirming the code action will trigger a resolve request.
557// let confirm_action = workspace_b
558// .update(cx_b, |workspace, cx| {
559// Editor::confirm_code_action(workspace, &ConfirmCodeAction { item_ix: Some(0) }, cx)
560// })
561// .unwrap();
562// fake_language_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
563// |_, _| async move {
564// Ok(lsp::CodeAction {
565// title: "Inline into all callers".to_string(),
566// edit: Some(lsp::WorkspaceEdit {
567// changes: Some(
568// [
569// (
570// lsp::Url::from_file_path("/a/main.rs").unwrap(),
571// vec![lsp::TextEdit::new(
572// lsp::Range::new(
573// lsp::Position::new(1, 22),
574// lsp::Position::new(1, 34),
575// ),
576// "4".to_string(),
577// )],
578// ),
579// (
580// lsp::Url::from_file_path("/a/other.rs").unwrap(),
581// vec![lsp::TextEdit::new(
582// lsp::Range::new(
583// lsp::Position::new(0, 0),
584// lsp::Position::new(0, 27),
585// ),
586// "".to_string(),
587// )],
588// ),
589// ]
590// .into_iter()
591// .collect(),
592// ),
593// ..Default::default()
594// }),
595// ..Default::default()
596// })
597// },
598// );
599
600// // After the action is confirmed, an editor containing both modified files is opened.
601// confirm_action.await.unwrap();
602
603// let code_action_editor = workspace_b.read_with(cx_b, |workspace, cx| {
604// workspace
605// .active_item(cx)
606// .unwrap()
607// .downcast::<Editor>()
608// .unwrap()
609// });
610// code_action_editor.update(cx_b, |editor, cx| {
611// assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
612// editor.undo(&Undo, cx);
613// assert_eq!(
614// editor.text(cx),
615// "mod other;\nfn main() { let foo = other::foo(); }\npub fn foo() -> usize { 4 }"
616// );
617// editor.redo(&Redo, cx);
618// assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
619// });
620// }
621
622//todo!(editor)
623// #[gpui::test(iterations = 10)]
624// async fn test_collaborating_with_renames(
625// executor: BackgroundExecutor,
626// cx_a: &mut TestAppContext,
627// cx_b: &mut TestAppContext,
628// ) {
629// let mut server = TestServer::start(&executor).await;
630// let client_a = server.create_client(cx_a, "user_a").await;
631// let client_b = server.create_client(cx_b, "user_b").await;
632// server
633// .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
634// .await;
635// let active_call_a = cx_a.read(ActiveCall::global);
636
637// cx_b.update(editor::init);
638
639// // Set up a fake language server.
640// let mut language = Language::new(
641// LanguageConfig {
642// name: "Rust".into(),
643// path_suffixes: vec!["rs".to_string()],
644// ..Default::default()
645// },
646// Some(tree_sitter_rust::language()),
647// );
648// let mut fake_language_servers = language
649// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
650// capabilities: lsp::ServerCapabilities {
651// rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
652// prepare_provider: Some(true),
653// work_done_progress_options: Default::default(),
654// })),
655// ..Default::default()
656// },
657// ..Default::default()
658// }))
659// .await;
660// client_a.language_registry().add(Arc::new(language));
661
662// client_a
663// .fs()
664// .insert_tree(
665// "/dir",
666// json!({
667// "one.rs": "const ONE: usize = 1;",
668// "two.rs": "const TWO: usize = one::ONE + one::ONE;"
669// }),
670// )
671// .await;
672// let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
673// let project_id = active_call_a
674// .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
675// .await
676// .unwrap();
677// let project_b = client_b.build_remote_project(project_id, cx_b).await;
678
679// let window_b =
680// cx_b.add_window(|cx| Workspace::new(0, project_b.clone(), client_b.app_state.clone(), cx));
681// let workspace_b = window_b.root(cx_b);
682// let editor_b = workspace_b
683// .update(cx_b, |workspace, cx| {
684// workspace.open_path((worktree_id, "one.rs"), None, true, cx)
685// })
686// .await
687// .unwrap()
688// .downcast::<Editor>()
689// .unwrap();
690// let fake_language_server = fake_language_servers.next().await.unwrap();
691
692// // Move cursor to a location that can be renamed.
693// let prepare_rename = editor_b.update(cx_b, |editor, cx| {
694// editor.change_selections(None, cx, |s| s.select_ranges([7..7]));
695// editor.rename(&Rename, cx).unwrap()
696// });
697
698// fake_language_server
699// .handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
700// assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
701// assert_eq!(params.position, lsp::Position::new(0, 7));
702// Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
703// lsp::Position::new(0, 6),
704// lsp::Position::new(0, 9),
705// ))))
706// })
707// .next()
708// .await
709// .unwrap();
710// prepare_rename.await.unwrap();
711// editor_b.update(cx_b, |editor, cx| {
712// use editor::ToOffset;
713// let rename = editor.pending_rename().unwrap();
714// let buffer = editor.buffer().read(cx).snapshot(cx);
715// assert_eq!(
716// rename.range.start.to_offset(&buffer)..rename.range.end.to_offset(&buffer),
717// 6..9
718// );
719// rename.editor.update(cx, |rename_editor, cx| {
720// rename_editor.buffer().update(cx, |rename_buffer, cx| {
721// rename_buffer.edit([(0..3, "THREE")], None, cx);
722// });
723// });
724// });
725
726// let confirm_rename = workspace_b.update(cx_b, |workspace, cx| {
727// Editor::confirm_rename(workspace, &ConfirmRename, cx).unwrap()
728// });
729// fake_language_server
730// .handle_request::<lsp::request::Rename, _, _>(|params, _| async move {
731// assert_eq!(
732// params.text_document_position.text_document.uri.as_str(),
733// "file:///dir/one.rs"
734// );
735// assert_eq!(
736// params.text_document_position.position,
737// lsp::Position::new(0, 6)
738// );
739// assert_eq!(params.new_name, "THREE");
740// Ok(Some(lsp::WorkspaceEdit {
741// changes: Some(
742// [
743// (
744// lsp::Url::from_file_path("/dir/one.rs").unwrap(),
745// vec![lsp::TextEdit::new(
746// lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
747// "THREE".to_string(),
748// )],
749// ),
750// (
751// lsp::Url::from_file_path("/dir/two.rs").unwrap(),
752// vec![
753// lsp::TextEdit::new(
754// lsp::Range::new(
755// lsp::Position::new(0, 24),
756// lsp::Position::new(0, 27),
757// ),
758// "THREE".to_string(),
759// ),
760// lsp::TextEdit::new(
761// lsp::Range::new(
762// lsp::Position::new(0, 35),
763// lsp::Position::new(0, 38),
764// ),
765// "THREE".to_string(),
766// ),
767// ],
768// ),
769// ]
770// .into_iter()
771// .collect(),
772// ),
773// ..Default::default()
774// }))
775// })
776// .next()
777// .await
778// .unwrap();
779// confirm_rename.await.unwrap();
780
781// let rename_editor = workspace_b.read_with(cx_b, |workspace, cx| {
782// workspace
783// .active_item(cx)
784// .unwrap()
785// .downcast::<Editor>()
786// .unwrap()
787// });
788// rename_editor.update(cx_b, |editor, cx| {
789// assert_eq!(
790// editor.text(cx),
791// "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
792// );
793// editor.undo(&Undo, cx);
794// assert_eq!(
795// editor.text(cx),
796// "const ONE: usize = 1;\nconst TWO: usize = one::ONE + one::ONE;"
797// );
798// editor.redo(&Redo, cx);
799// assert_eq!(
800// editor.text(cx),
801// "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
802// );
803// });
804
805// // Ensure temporary rename edits cannot be undone/redone.
806// editor_b.update(cx_b, |editor, cx| {
807// editor.undo(&Undo, cx);
808// assert_eq!(editor.text(cx), "const ONE: usize = 1;");
809// editor.undo(&Undo, cx);
810// assert_eq!(editor.text(cx), "const ONE: usize = 1;");
811// editor.redo(&Redo, cx);
812// assert_eq!(editor.text(cx), "const THREE: usize = 1;");
813// })
814// }
815
816//todo!(editor)
817// #[gpui::test(iterations = 10)]
818// async fn test_language_server_statuses(
819// executor: BackgroundExecutor,
820// cx_a: &mut TestAppContext,
821// cx_b: &mut TestAppContext,
822// ) {
823// let mut server = TestServer::start(&executor).await;
824// let client_a = server.create_client(cx_a, "user_a").await;
825// let client_b = server.create_client(cx_b, "user_b").await;
826// server
827// .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
828// .await;
829// let active_call_a = cx_a.read(ActiveCall::global);
830
831// cx_b.update(editor::init);
832
833// // Set up a fake language server.
834// let mut language = Language::new(
835// LanguageConfig {
836// name: "Rust".into(),
837// path_suffixes: vec!["rs".to_string()],
838// ..Default::default()
839// },
840// Some(tree_sitter_rust::language()),
841// );
842// let mut fake_language_servers = language
843// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
844// name: "the-language-server",
845// ..Default::default()
846// }))
847// .await;
848// client_a.language_registry().add(Arc::new(language));
849
850// client_a
851// .fs()
852// .insert_tree(
853// "/dir",
854// json!({
855// "main.rs": "const ONE: usize = 1;",
856// }),
857// )
858// .await;
859// let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
860
861// let _buffer_a = project_a
862// .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
863// .await
864// .unwrap();
865
866// let fake_language_server = fake_language_servers.next().await.unwrap();
867// fake_language_server.start_progress("the-token").await;
868// fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
869// token: lsp::NumberOrString::String("the-token".to_string()),
870// value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
871// lsp::WorkDoneProgressReport {
872// message: Some("the-message".to_string()),
873// ..Default::default()
874// },
875// )),
876// });
877// executor.run_until_parked();
878
879// project_a.read_with(cx_a, |project, _| {
880// let status = project.language_server_statuses().next().unwrap();
881// assert_eq!(status.name, "the-language-server");
882// assert_eq!(status.pending_work.len(), 1);
883// assert_eq!(
884// status.pending_work["the-token"].message.as_ref().unwrap(),
885// "the-message"
886// );
887// });
888
889// let project_id = active_call_a
890// .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
891// .await
892// .unwrap();
893// executor.run_until_parked();
894// let project_b = client_b.build_remote_project(project_id, cx_b).await;
895
896// project_b.read_with(cx_b, |project, _| {
897// let status = project.language_server_statuses().next().unwrap();
898// assert_eq!(status.name, "the-language-server");
899// });
900
901// fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
902// token: lsp::NumberOrString::String("the-token".to_string()),
903// value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
904// lsp::WorkDoneProgressReport {
905// message: Some("the-message-2".to_string()),
906// ..Default::default()
907// },
908// )),
909// });
910// executor.run_until_parked();
911
912// project_a.read_with(cx_a, |project, _| {
913// let status = project.language_server_statuses().next().unwrap();
914// assert_eq!(status.name, "the-language-server");
915// assert_eq!(status.pending_work.len(), 1);
916// assert_eq!(
917// status.pending_work["the-token"].message.as_ref().unwrap(),
918// "the-message-2"
919// );
920// });
921
922// project_b.read_with(cx_b, |project, _| {
923// let status = project.language_server_statuses().next().unwrap();
924// assert_eq!(status.name, "the-language-server");
925// assert_eq!(status.pending_work.len(), 1);
926// assert_eq!(
927// status.pending_work["the-token"].message.as_ref().unwrap(),
928// "the-message-2"
929// );
930// });
931// }
932
933// #[gpui::test(iterations = 10)]
934// async fn test_share_project(
935// executor: BackgroundExecutor,
936// cx_a: &mut TestAppContext,
937// cx_b: &mut TestAppContext,
938// cx_c: &mut TestAppContext,
939// ) {
940// let window_b = cx_b.add_window(|_| EmptyView);
941// let mut server = TestServer::start(&executor).await;
942// let client_a = server.create_client(cx_a, "user_a").await;
943// let client_b = server.create_client(cx_b, "user_b").await;
944// let client_c = server.create_client(cx_c, "user_c").await;
945// server
946// .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
947// .await;
948// let active_call_a = cx_a.read(ActiveCall::global);
949// let active_call_b = cx_b.read(ActiveCall::global);
950// let active_call_c = cx_c.read(ActiveCall::global);
951
952// client_a
953// .fs()
954// .insert_tree(
955// "/a",
956// json!({
957// ".gitignore": "ignored-dir",
958// "a.txt": "a-contents",
959// "b.txt": "b-contents",
960// "ignored-dir": {
961// "c.txt": "",
962// "d.txt": "",
963// }
964// }),
965// )
966// .await;
967
968// // Invite client B to collaborate on a project
969// let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
970// active_call_a
971// .update(cx_a, |call, cx| {
972// call.invite(client_b.user_id().unwrap(), Some(project_a.clone()), cx)
973// })
974// .await
975// .unwrap();
976
977// // Join that project as client B
978
979// let incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
980// executor.run_until_parked();
981// let call = incoming_call_b.borrow().clone().unwrap();
982// assert_eq!(call.calling_user.github_login, "user_a");
983// let initial_project = call.initial_project.unwrap();
984// active_call_b
985// .update(cx_b, |call, cx| call.accept_incoming(cx))
986// .await
987// .unwrap();
988// let client_b_peer_id = client_b.peer_id().unwrap();
989// let project_b = client_b
990// .build_remote_project(initial_project.id, cx_b)
991// .await;
992
993// let replica_id_b = project_b.read_with(cx_b, |project, _| project.replica_id());
994
995// executor.run_until_parked();
996
997// project_a.read_with(cx_a, |project, _| {
998// let client_b_collaborator = project.collaborators().get(&client_b_peer_id).unwrap();
999// assert_eq!(client_b_collaborator.replica_id, replica_id_b);
1000// });
1001
1002// project_b.read_with(cx_b, |project, cx| {
1003// let worktree = project.worktrees().next().unwrap().read(cx);
1004// assert_eq!(
1005// worktree.paths().map(AsRef::as_ref).collect::<Vec<_>>(),
1006// [
1007// Path::new(".gitignore"),
1008// Path::new("a.txt"),
1009// Path::new("b.txt"),
1010// Path::new("ignored-dir"),
1011// ]
1012// );
1013// });
1014
1015// project_b
1016// .update(cx_b, |project, cx| {
1017// let worktree = project.worktrees().next().unwrap();
1018// let entry = worktree.read(cx).entry_for_path("ignored-dir").unwrap();
1019// project.expand_entry(worktree_id, entry.id, cx).unwrap()
1020// })
1021// .await
1022// .unwrap();
1023
1024// project_b.read_with(cx_b, |project, cx| {
1025// let worktree = project.worktrees().next().unwrap().read(cx);
1026// assert_eq!(
1027// worktree.paths().map(AsRef::as_ref).collect::<Vec<_>>(),
1028// [
1029// Path::new(".gitignore"),
1030// Path::new("a.txt"),
1031// Path::new("b.txt"),
1032// Path::new("ignored-dir"),
1033// Path::new("ignored-dir/c.txt"),
1034// Path::new("ignored-dir/d.txt"),
1035// ]
1036// );
1037// });
1038
1039// // Open the same file as client B and client A.
1040// let buffer_b = project_b
1041// .update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.txt"), cx))
1042// .await
1043// .unwrap();
1044
1045// buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "b-contents"));
1046
1047// project_a.read_with(cx_a, |project, cx| {
1048// assert!(project.has_open_buffer((worktree_id, "b.txt"), cx))
1049// });
1050// let buffer_a = project_a
1051// .update(cx_a, |p, cx| p.open_buffer((worktree_id, "b.txt"), cx))
1052// .await
1053// .unwrap();
1054
1055// let editor_b = window_b.add_view(cx_b, |cx| Editor::for_buffer(buffer_b, None, cx));
1056
1057// // Client A sees client B's selection
1058// executor.run_until_parked();
1059
1060// buffer_a.read_with(cx_a, |buffer, _| {
1061// buffer
1062// .snapshot()
1063// .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1064// .count()
1065// == 1
1066// });
1067
1068// // Edit the buffer as client B and see that edit as client A.
1069// editor_b.update(cx_b, |editor, cx| editor.handle_input("ok, ", cx));
1070// executor.run_until_parked();
1071
1072// buffer_a.read_with(cx_a, |buffer, _| {
1073// assert_eq!(buffer.text(), "ok, b-contents")
1074// });
1075
1076// // Client B can invite client C on a project shared by client A.
1077// active_call_b
1078// .update(cx_b, |call, cx| {
1079// call.invite(client_c.user_id().unwrap(), Some(project_b.clone()), cx)
1080// })
1081// .await
1082// .unwrap();
1083
1084// let incoming_call_c = active_call_c.read_with(cx_c, |call, _| call.incoming());
1085// executor.run_until_parked();
1086// let call = incoming_call_c.borrow().clone().unwrap();
1087// assert_eq!(call.calling_user.github_login, "user_b");
1088// let initial_project = call.initial_project.unwrap();
1089// active_call_c
1090// .update(cx_c, |call, cx| call.accept_incoming(cx))
1091// .await
1092// .unwrap();
1093// let _project_c = client_c
1094// .build_remote_project(initial_project.id, cx_c)
1095// .await;
1096
1097// // Client B closes the editor, and client A sees client B's selections removed.
1098// cx_b.update(move |_| drop(editor_b));
1099// executor.run_until_parked();
1100
1101// buffer_a.read_with(cx_a, |buffer, _| {
1102// buffer
1103// .snapshot()
1104// .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1105// .count()
1106// == 0
1107// });
1108// }