Uncomment editor tests

Conrad Irwin and Julia created

Co-Authored-By: Julia <julia@zed.dev>

Change summary

crates/collab/src/tests/editor_tests.rs | 3773 +++++++++++++-------------
1 file changed, 1,884 insertions(+), 1,889 deletions(-)

Detailed changes

crates/collab/src/tests/editor_tests.rs 🔗

@@ -1,1889 +1,1884 @@
-//todo(partially ported)
-// use std::{
-//     path::Path,
-//     sync::{
-//         atomic::{self, AtomicBool, AtomicUsize},
-//         Arc,
-//     },
-// };
-
-// use call::ActiveCall;
-// use editor::{
-//     test::editor_test_context::{AssertionContextManager, EditorTestContext},
-//     Anchor, ConfirmCodeAction, ConfirmCompletion, ConfirmRename, Editor, Redo, Rename,
-//     ToggleCodeActions, Undo,
-// };
-// use gpui::{BackgroundExecutor, TestAppContext, VisualContext, VisualTestContext};
-// use indoc::indoc;
-// use language::{
-//     language_settings::{AllLanguageSettings, InlayHintSettings},
-//     tree_sitter_rust, FakeLspAdapter, Language, LanguageConfig,
-// };
-// use rpc::RECEIVE_TIMEOUT;
-// use serde_json::json;
-// use settings::SettingsStore;
-// use text::Point;
-// use workspace::Workspace;
-
-// use crate::{rpc::RECONNECT_TIMEOUT, tests::TestServer};
-
-// #[gpui::test(iterations = 10)]
-// async fn test_host_disconnect(
-//     executor: BackgroundExecutor,
-//     cx_a: &mut TestAppContext,
-//     cx_b: &mut TestAppContext,
-//     cx_c: &mut TestAppContext,
-// ) {
-//     let mut server = TestServer::start(executor).await;
-//     let client_a = server.create_client(cx_a, "user_a").await;
-//     let client_b = server.create_client(cx_b, "user_b").await;
-//     let client_c = server.create_client(cx_c, "user_c").await;
-//     server
-//         .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
-//         .await;
-
-//     cx_b.update(editor::init);
-
-//     client_a
-//         .fs()
-//         .insert_tree(
-//             "/a",
-//             serde_json::json!({
-//                 "a.txt": "a-contents",
-//                 "b.txt": "b-contents",
-//             }),
-//         )
-//         .await;
-
-//     let active_call_a = cx_a.read(ActiveCall::global);
-//     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
-
-//     let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees().next().unwrap());
-//     let project_id = active_call_a
-//         .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
-//         .await
-//         .unwrap();
-
-//     let project_b = client_b.build_remote_project(project_id, cx_b).await;
-//     executor.run_until_parked();
-
-//     assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
-
-//     let workspace_b =
-//         cx_b.add_window(|cx| Workspace::new(0, project_b.clone(), client_b.app_state.clone(), cx));
-//     let cx_b = &mut VisualTestContext::from_window(*workspace_b, cx_b);
-
-//     let editor_b = workspace_b
-//         .update(cx_b, |workspace, cx| {
-//             workspace.open_path((worktree_id, "b.txt"), None, true, cx)
-//         })
-//         .unwrap()
-//         .await
-//         .unwrap()
-//         .downcast::<Editor>()
-//         .unwrap();
-
-//     //TODO: focus
-//     assert!(cx_b.update_view(&editor_b, |editor, cx| editor.is_focused(cx)));
-//     editor_b.update(cx_b, |editor, cx| editor.insert("X", cx));
-//     //todo(is_edited)
-//     // assert!(workspace_b.is_edited(cx_b));
-
-//     // Drop client A's connection. Collaborators should disappear and the project should not be shown as shared.
-//     server.forbid_connections();
-//     server.disconnect_client(client_a.peer_id().unwrap());
-//     executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
-
-//     project_a.read_with(cx_a, |project, _| project.collaborators().is_empty());
-
-//     project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
-
-//     project_b.read_with(cx_b, |project, _| project.is_read_only());
-
-//     assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared()));
-
-//     // Ensure client B's edited state is reset and that the whole window is blurred.
-
-//     workspace_b.update(cx_b, |_, cx| {
-//         assert_eq!(cx.focused_view_id(), None);
-//     });
-//     // assert!(!workspace_b.is_edited(cx_b));
-
-//     // Ensure client B is not prompted to save edits when closing window after disconnecting.
-//     let can_close = workspace_b
-//         .update(cx_b, |workspace, cx| workspace.prepare_to_close(true, cx))
-//         .await
-//         .unwrap();
-//     assert!(can_close);
-
-//     // Allow client A to reconnect to the server.
-//     server.allow_connections();
-//     executor.advance_clock(RECEIVE_TIMEOUT);
-
-//     // Client B calls client A again after they reconnected.
-//     let active_call_b = cx_b.read(ActiveCall::global);
-//     active_call_b
-//         .update(cx_b, |call, cx| {
-//             call.invite(client_a.user_id().unwrap(), None, cx)
-//         })
-//         .await
-//         .unwrap();
-//     executor.run_until_parked();
-//     active_call_a
-//         .update(cx_a, |call, cx| call.accept_incoming(cx))
-//         .await
-//         .unwrap();
-
-//     active_call_a
-//         .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
-//         .await
-//         .unwrap();
-
-//     // Drop client A's connection again. We should still unshare it successfully.
-//     server.forbid_connections();
-//     server.disconnect_client(client_a.peer_id().unwrap());
-//     executor.advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
-
-//     project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
-// }
-
-// #[gpui::test]
-// async fn test_newline_above_or_below_does_not_move_guest_cursor(
-//     executor: BackgroundExecutor,
-//     cx_a: &mut TestAppContext,
-//     cx_b: &mut TestAppContext,
-// ) {
-//     let mut server = TestServer::start(&executor).await;
-//     let client_a = server.create_client(cx_a, "user_a").await;
-//     let client_b = server.create_client(cx_b, "user_b").await;
-//     server
-//         .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
-//         .await;
-//     let active_call_a = cx_a.read(ActiveCall::global);
-
-//     client_a
-//         .fs()
-//         .insert_tree("/dir", json!({ "a.txt": "Some text\n" }))
-//         .await;
-//     let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
-//     let project_id = active_call_a
-//         .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
-//         .await
-//         .unwrap();
-
-//     let project_b = client_b.build_remote_project(project_id, cx_b).await;
-
-//     // Open a buffer as client A
-//     let buffer_a = project_a
-//         .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
-//         .await
-//         .unwrap();
-//     let window_a = cx_a.add_empty_window();
-//     let editor_a =
-//         window_a.build_view(cx_a, |cx| Editor::for_buffer(buffer_a, Some(project_a), cx));
-//     let mut editor_cx_a = EditorTestContext {
-//         cx: cx_a,
-//         window: window_a.into(),
-//         editor: editor_a,
-//         assertion_cx: AssertionContextManager::new(),
-//     };
-
-//     // Open a buffer as client B
-//     let buffer_b = project_b
-//         .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
-//         .await
-//         .unwrap();
-//     let window_b = cx_b.add_empty_window();
-//     let editor_b =
-//         window_b.build_view(cx_b, |cx| Editor::for_buffer(buffer_b, Some(project_b), cx));
-//     let mut editor_cx_b = EditorTestContext {
-//         cx: cx_b,
-//         window: window_b.into(),
-//         editor: editor_b,
-//         assertion_cx: AssertionContextManager::new(),
-//     };
-
-//     // Test newline above
-//     editor_cx_a.set_selections_state(indoc! {"
-//         Some textˇ
-//     "});
-//     editor_cx_b.set_selections_state(indoc! {"
-//         Some textˇ
-//     "});
-//     editor_cx_a.update_editor(|editor, cx| editor.newline_above(&editor::NewlineAbove, cx));
-//     executor.run_until_parked();
-//     editor_cx_a.assert_editor_state(indoc! {"
-//         ˇ
-//         Some text
-//     "});
-//     editor_cx_b.assert_editor_state(indoc! {"
-
-//         Some textˇ
-//     "});
-
-//     // Test newline below
-//     editor_cx_a.set_selections_state(indoc! {"
-
-//         Some textˇ
-//     "});
-//     editor_cx_b.set_selections_state(indoc! {"
-
-//         Some textˇ
-//     "});
-//     editor_cx_a.update_editor(|editor, cx| editor.newline_below(&editor::NewlineBelow, cx));
-//     executor.run_until_parked();
-//     editor_cx_a.assert_editor_state(indoc! {"
-
-//         Some text
-//         ˇ
-//     "});
-//     editor_cx_b.assert_editor_state(indoc! {"
-
-//         Some textˇ
-
-//     "});
-// }
-
-// #[gpui::test(iterations = 10)]
-// async fn test_collaborating_with_completion(
-//     executor: BackgroundExecutor,
-//     cx_a: &mut TestAppContext,
-//     cx_b: &mut TestAppContext,
-// ) {
-//     let mut server = TestServer::start(&executor).await;
-//     let client_a = server.create_client(cx_a, "user_a").await;
-//     let client_b = server.create_client(cx_b, "user_b").await;
-//     server
-//         .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
-//         .await;
-//     let active_call_a = cx_a.read(ActiveCall::global);
-
-//     // Set up a fake language server.
-//     let mut language = Language::new(
-//         LanguageConfig {
-//             name: "Rust".into(),
-//             path_suffixes: vec!["rs".to_string()],
-//             ..Default::default()
-//         },
-//         Some(tree_sitter_rust::language()),
-//     );
-//     let mut fake_language_servers = language
-//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-//             capabilities: lsp::ServerCapabilities {
-//                 completion_provider: Some(lsp::CompletionOptions {
-//                     trigger_characters: Some(vec![".".to_string()]),
-//                     resolve_provider: Some(true),
-//                     ..Default::default()
-//                 }),
-//                 ..Default::default()
-//             },
-//             ..Default::default()
-//         }))
-//         .await;
-//     client_a.language_registry().add(Arc::new(language));
-
-//     client_a
-//         .fs()
-//         .insert_tree(
-//             "/a",
-//             json!({
-//                 "main.rs": "fn main() { a }",
-//                 "other.rs": "",
-//             }),
-//         )
-//         .await;
-//     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
-//     let project_id = active_call_a
-//         .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
-//         .await
-//         .unwrap();
-//     let project_b = client_b.build_remote_project(project_id, cx_b).await;
-
-//     // Open a file in an editor as the guest.
-//     let buffer_b = project_b
-//         .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
-//         .await
-//         .unwrap();
-//     let window_b = cx_b.add_empty_window();
-//     let editor_b = window_b.build_view(cx_b, |cx| {
-//         Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
-//     });
-
-//     let fake_language_server = fake_language_servers.next().await.unwrap();
-//     cx_a.foreground().run_until_parked();
-
-//     buffer_b.read_with(cx_b, |buffer, _| {
-//         assert!(!buffer.completion_triggers().is_empty())
-//     });
-
-//     // Type a completion trigger character as the guest.
-//     editor_b.update(cx_b, |editor, cx| {
-//         editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-//         editor.handle_input(".", cx);
-//         cx.focus(&editor_b);
-//     });
-
-//     // Receive a completion request as the host's language server.
-//     // Return some completions from the host's language server.
-//     cx_a.foreground().start_waiting();
-//     fake_language_server
-//         .handle_request::<lsp::request::Completion, _, _>(|params, _| async move {
-//             assert_eq!(
-//                 params.text_document_position.text_document.uri,
-//                 lsp::Url::from_file_path("/a/main.rs").unwrap(),
-//             );
-//             assert_eq!(
-//                 params.text_document_position.position,
-//                 lsp::Position::new(0, 14),
-//             );
-
-//             Ok(Some(lsp::CompletionResponse::Array(vec![
-//                 lsp::CompletionItem {
-//                     label: "first_method(…)".into(),
-//                     detail: Some("fn(&mut self, B) -> C".into()),
-//                     text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
-//                         new_text: "first_method($1)".to_string(),
-//                         range: lsp::Range::new(
-//                             lsp::Position::new(0, 14),
-//                             lsp::Position::new(0, 14),
-//                         ),
-//                     })),
-//                     insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
-//                     ..Default::default()
-//                 },
-//                 lsp::CompletionItem {
-//                     label: "second_method(…)".into(),
-//                     detail: Some("fn(&mut self, C) -> D<E>".into()),
-//                     text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
-//                         new_text: "second_method()".to_string(),
-//                         range: lsp::Range::new(
-//                             lsp::Position::new(0, 14),
-//                             lsp::Position::new(0, 14),
-//                         ),
-//                     })),
-//                     insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
-//                     ..Default::default()
-//                 },
-//             ])))
-//         })
-//         .next()
-//         .await
-//         .unwrap();
-//     cx_a.foreground().finish_waiting();
-
-//     // Open the buffer on the host.
-//     let buffer_a = project_a
-//         .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
-//         .await
-//         .unwrap();
-//     cx_a.foreground().run_until_parked();
-
-//     buffer_a.read_with(cx_a, |buffer, _| {
-//         assert_eq!(buffer.text(), "fn main() { a. }")
-//     });
-
-//     // Confirm a completion on the guest.
-
-//     editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
-//     editor_b.update(cx_b, |editor, cx| {
-//         editor.confirm_completion(&ConfirmCompletion { item_ix: Some(0) }, cx);
-//         assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
-//     });
-
-//     // Return a resolved completion from the host's language server.
-//     // The resolved completion has an additional text edit.
-//     fake_language_server.handle_request::<lsp::request::ResolveCompletionItem, _, _>(
-//         |params, _| async move {
-//             assert_eq!(params.label, "first_method(…)");
-//             Ok(lsp::CompletionItem {
-//                 label: "first_method(…)".into(),
-//                 detail: Some("fn(&mut self, B) -> C".into()),
-//                 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
-//                     new_text: "first_method($1)".to_string(),
-//                     range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
-//                 })),
-//                 additional_text_edits: Some(vec![lsp::TextEdit {
-//                     new_text: "use d::SomeTrait;\n".to_string(),
-//                     range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
-//                 }]),
-//                 insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
-//                 ..Default::default()
-//             })
-//         },
-//     );
-
-//     // The additional edit is applied.
-//     cx_a.executor().run_until_parked();
-
-//     buffer_a.read_with(cx_a, |buffer, _| {
-//         assert_eq!(
-//             buffer.text(),
-//             "use d::SomeTrait;\nfn main() { a.first_method() }"
-//         );
-//     });
-
-//     buffer_b.read_with(cx_b, |buffer, _| {
-//         assert_eq!(
-//             buffer.text(),
-//             "use d::SomeTrait;\nfn main() { a.first_method() }"
-//         );
-//     });
-// }
-
-// #[gpui::test(iterations = 10)]
-// async fn test_collaborating_with_code_actions(
-//     executor: BackgroundExecutor,
-//     cx_a: &mut TestAppContext,
-//     cx_b: &mut TestAppContext,
-// ) {
-//     let mut server = TestServer::start(&executor).await;
-//     let client_a = server.create_client(cx_a, "user_a").await;
-//     //
-//     let client_b = server.create_client(cx_b, "user_b").await;
-//     server
-//         .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
-//         .await;
-//     let active_call_a = cx_a.read(ActiveCall::global);
-
-//     cx_b.update(editor::init);
-
-//     // Set up a fake language server.
-//     let mut language = Language::new(
-//         LanguageConfig {
-//             name: "Rust".into(),
-//             path_suffixes: vec!["rs".to_string()],
-//             ..Default::default()
-//         },
-//         Some(tree_sitter_rust::language()),
-//     );
-//     let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
-//     client_a.language_registry().add(Arc::new(language));
-
-//     client_a
-//         .fs()
-//         .insert_tree(
-//             "/a",
-//             json!({
-//                 "main.rs": "mod other;\nfn main() { let foo = other::foo(); }",
-//                 "other.rs": "pub fn foo() -> usize { 4 }",
-//             }),
-//         )
-//         .await;
-//     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
-//     let project_id = active_call_a
-//         .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
-//         .await
-//         .unwrap();
-
-//     // Join the project as client B.
-//     let project_b = client_b.build_remote_project(project_id, cx_b).await;
-//     let window_b =
-//         cx_b.add_window(|cx| Workspace::new(0, project_b.clone(), client_b.app_state.clone(), cx));
-//     let workspace_b = window_b.root(cx_b);
-//     let editor_b = workspace_b
-//         .update(cx_b, |workspace, cx| {
-//             workspace.open_path((worktree_id, "main.rs"), None, true, cx)
-//         })
-//         .await
-//         .unwrap()
-//         .downcast::<Editor>()
-//         .unwrap();
-
-//     let mut fake_language_server = fake_language_servers.next().await.unwrap();
-//     let mut requests = fake_language_server
-//         .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
-//             assert_eq!(
-//                 params.text_document.uri,
-//                 lsp::Url::from_file_path("/a/main.rs").unwrap(),
-//             );
-//             assert_eq!(params.range.start, lsp::Position::new(0, 0));
-//             assert_eq!(params.range.end, lsp::Position::new(0, 0));
-//             Ok(None)
-//         });
-//     executor.advance_clock(editor::CODE_ACTIONS_DEBOUNCE_TIMEOUT * 2);
-//     requests.next().await;
-
-//     // Move cursor to a location that contains code actions.
-//     editor_b.update(cx_b, |editor, cx| {
-//         editor.change_selections(None, cx, |s| {
-//             s.select_ranges([Point::new(1, 31)..Point::new(1, 31)])
-//         });
-//         cx.focus(&editor_b);
-//     });
-
-//     let mut requests = fake_language_server
-//         .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
-//             assert_eq!(
-//                 params.text_document.uri,
-//                 lsp::Url::from_file_path("/a/main.rs").unwrap(),
-//             );
-//             assert_eq!(params.range.start, lsp::Position::new(1, 31));
-//             assert_eq!(params.range.end, lsp::Position::new(1, 31));
-
-//             Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
-//                 lsp::CodeAction {
-//                     title: "Inline into all callers".to_string(),
-//                     edit: Some(lsp::WorkspaceEdit {
-//                         changes: Some(
-//                             [
-//                                 (
-//                                     lsp::Url::from_file_path("/a/main.rs").unwrap(),
-//                                     vec![lsp::TextEdit::new(
-//                                         lsp::Range::new(
-//                                             lsp::Position::new(1, 22),
-//                                             lsp::Position::new(1, 34),
-//                                         ),
-//                                         "4".to_string(),
-//                                     )],
-//                                 ),
-//                                 (
-//                                     lsp::Url::from_file_path("/a/other.rs").unwrap(),
-//                                     vec![lsp::TextEdit::new(
-//                                         lsp::Range::new(
-//                                             lsp::Position::new(0, 0),
-//                                             lsp::Position::new(0, 27),
-//                                         ),
-//                                         "".to_string(),
-//                                     )],
-//                                 ),
-//                             ]
-//                             .into_iter()
-//                             .collect(),
-//                         ),
-//                         ..Default::default()
-//                     }),
-//                     data: Some(json!({
-//                         "codeActionParams": {
-//                             "range": {
-//                                 "start": {"line": 1, "column": 31},
-//                                 "end": {"line": 1, "column": 31},
-//                             }
-//                         }
-//                     })),
-//                     ..Default::default()
-//                 },
-//             )]))
-//         });
-//     executor.advance_clock(editor::CODE_ACTIONS_DEBOUNCE_TIMEOUT * 2);
-//     requests.next().await;
-
-//     // Toggle code actions and wait for them to display.
-//     editor_b.update(cx_b, |editor, cx| {
-//         editor.toggle_code_actions(
-//             &ToggleCodeActions {
-//                 deployed_from_indicator: false,
-//             },
-//             cx,
-//         );
-//     });
-//     cx_a.foreground().run_until_parked();
-
-//     editor_b.read_with(cx_b, |editor, _| assert!(editor.context_menu_visible()));
-
-//     fake_language_server.remove_request_handler::<lsp::request::CodeActionRequest>();
-
-//     // Confirming the code action will trigger a resolve request.
-//     let confirm_action = workspace_b
-//         .update(cx_b, |workspace, cx| {
-//             Editor::confirm_code_action(workspace, &ConfirmCodeAction { item_ix: Some(0) }, cx)
-//         })
-//         .unwrap();
-//     fake_language_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
-//         |_, _| async move {
-//             Ok(lsp::CodeAction {
-//                 title: "Inline into all callers".to_string(),
-//                 edit: Some(lsp::WorkspaceEdit {
-//                     changes: Some(
-//                         [
-//                             (
-//                                 lsp::Url::from_file_path("/a/main.rs").unwrap(),
-//                                 vec![lsp::TextEdit::new(
-//                                     lsp::Range::new(
-//                                         lsp::Position::new(1, 22),
-//                                         lsp::Position::new(1, 34),
-//                                     ),
-//                                     "4".to_string(),
-//                                 )],
-//                             ),
-//                             (
-//                                 lsp::Url::from_file_path("/a/other.rs").unwrap(),
-//                                 vec![lsp::TextEdit::new(
-//                                     lsp::Range::new(
-//                                         lsp::Position::new(0, 0),
-//                                         lsp::Position::new(0, 27),
-//                                     ),
-//                                     "".to_string(),
-//                                 )],
-//                             ),
-//                         ]
-//                         .into_iter()
-//                         .collect(),
-//                     ),
-//                     ..Default::default()
-//                 }),
-//                 ..Default::default()
-//             })
-//         },
-//     );
-
-//     // After the action is confirmed, an editor containing both modified files is opened.
-//     confirm_action.await.unwrap();
-
-//     let code_action_editor = workspace_b.read_with(cx_b, |workspace, cx| {
-//         workspace
-//             .active_item(cx)
-//             .unwrap()
-//             .downcast::<Editor>()
-//             .unwrap()
-//     });
-//     code_action_editor.update(cx_b, |editor, cx| {
-//         assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
-//         editor.undo(&Undo, cx);
-//         assert_eq!(
-//             editor.text(cx),
-//             "mod other;\nfn main() { let foo = other::foo(); }\npub fn foo() -> usize { 4 }"
-//         );
-//         editor.redo(&Redo, cx);
-//         assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
-//     });
-// }
-
-// #[gpui::test(iterations = 10)]
-// async fn test_collaborating_with_renames(
-//     executor: BackgroundExecutor,
-//     cx_a: &mut TestAppContext,
-//     cx_b: &mut TestAppContext,
-// ) {
-//     let mut server = TestServer::start(&executor).await;
-//     let client_a = server.create_client(cx_a, "user_a").await;
-//     let client_b = server.create_client(cx_b, "user_b").await;
-//     server
-//         .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
-//         .await;
-//     let active_call_a = cx_a.read(ActiveCall::global);
-
-//     cx_b.update(editor::init);
-
-//     // Set up a fake language server.
-//     let mut language = Language::new(
-//         LanguageConfig {
-//             name: "Rust".into(),
-//             path_suffixes: vec!["rs".to_string()],
-//             ..Default::default()
-//         },
-//         Some(tree_sitter_rust::language()),
-//     );
-//     let mut fake_language_servers = language
-//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-//             capabilities: lsp::ServerCapabilities {
-//                 rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
-//                     prepare_provider: Some(true),
-//                     work_done_progress_options: Default::default(),
-//                 })),
-//                 ..Default::default()
-//             },
-//             ..Default::default()
-//         }))
-//         .await;
-//     client_a.language_registry().add(Arc::new(language));
-
-//     client_a
-//         .fs()
-//         .insert_tree(
-//             "/dir",
-//             json!({
-//                 "one.rs": "const ONE: usize = 1;",
-//                 "two.rs": "const TWO: usize = one::ONE + one::ONE;"
-//             }),
-//         )
-//         .await;
-//     let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
-//     let project_id = active_call_a
-//         .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
-//         .await
-//         .unwrap();
-//     let project_b = client_b.build_remote_project(project_id, cx_b).await;
-
-//     let window_b =
-//         cx_b.add_window(|cx| Workspace::new(0, project_b.clone(), client_b.app_state.clone(), cx));
-//     let workspace_b = window_b.root(cx_b);
-//     let editor_b = workspace_b
-//         .update(cx_b, |workspace, cx| {
-//             workspace.open_path((worktree_id, "one.rs"), None, true, cx)
-//         })
-//         .await
-//         .unwrap()
-//         .downcast::<Editor>()
-//         .unwrap();
-//     let fake_language_server = fake_language_servers.next().await.unwrap();
-
-//     // Move cursor to a location that can be renamed.
-//     let prepare_rename = editor_b.update(cx_b, |editor, cx| {
-//         editor.change_selections(None, cx, |s| s.select_ranges([7..7]));
-//         editor.rename(&Rename, cx).unwrap()
-//     });
-
-//     fake_language_server
-//         .handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
-//             assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
-//             assert_eq!(params.position, lsp::Position::new(0, 7));
-//             Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
-//                 lsp::Position::new(0, 6),
-//                 lsp::Position::new(0, 9),
-//             ))))
-//         })
-//         .next()
-//         .await
-//         .unwrap();
-//     prepare_rename.await.unwrap();
-//     editor_b.update(cx_b, |editor, cx| {
-//         use editor::ToOffset;
-//         let rename = editor.pending_rename().unwrap();
-//         let buffer = editor.buffer().read(cx).snapshot(cx);
-//         assert_eq!(
-//             rename.range.start.to_offset(&buffer)..rename.range.end.to_offset(&buffer),
-//             6..9
-//         );
-//         rename.editor.update(cx, |rename_editor, cx| {
-//             rename_editor.buffer().update(cx, |rename_buffer, cx| {
-//                 rename_buffer.edit([(0..3, "THREE")], None, cx);
-//             });
-//         });
-//     });
-
-//     let confirm_rename = workspace_b.update(cx_b, |workspace, cx| {
-//         Editor::confirm_rename(workspace, &ConfirmRename, cx).unwrap()
-//     });
-//     fake_language_server
-//         .handle_request::<lsp::request::Rename, _, _>(|params, _| async move {
-//             assert_eq!(
-//                 params.text_document_position.text_document.uri.as_str(),
-//                 "file:///dir/one.rs"
-//             );
-//             assert_eq!(
-//                 params.text_document_position.position,
-//                 lsp::Position::new(0, 6)
-//             );
-//             assert_eq!(params.new_name, "THREE");
-//             Ok(Some(lsp::WorkspaceEdit {
-//                 changes: Some(
-//                     [
-//                         (
-//                             lsp::Url::from_file_path("/dir/one.rs").unwrap(),
-//                             vec![lsp::TextEdit::new(
-//                                 lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
-//                                 "THREE".to_string(),
-//                             )],
-//                         ),
-//                         (
-//                             lsp::Url::from_file_path("/dir/two.rs").unwrap(),
-//                             vec![
-//                                 lsp::TextEdit::new(
-//                                     lsp::Range::new(
-//                                         lsp::Position::new(0, 24),
-//                                         lsp::Position::new(0, 27),
-//                                     ),
-//                                     "THREE".to_string(),
-//                                 ),
-//                                 lsp::TextEdit::new(
-//                                     lsp::Range::new(
-//                                         lsp::Position::new(0, 35),
-//                                         lsp::Position::new(0, 38),
-//                                     ),
-//                                     "THREE".to_string(),
-//                                 ),
-//                             ],
-//                         ),
-//                     ]
-//                     .into_iter()
-//                     .collect(),
-//                 ),
-//                 ..Default::default()
-//             }))
-//         })
-//         .next()
-//         .await
-//         .unwrap();
-//     confirm_rename.await.unwrap();
-
-//     let rename_editor = workspace_b.read_with(cx_b, |workspace, cx| {
-//         workspace
-//             .active_item(cx)
-//             .unwrap()
-//             .downcast::<Editor>()
-//             .unwrap()
-//     });
-//     rename_editor.update(cx_b, |editor, cx| {
-//         assert_eq!(
-//             editor.text(cx),
-//             "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
-//         );
-//         editor.undo(&Undo, cx);
-//         assert_eq!(
-//             editor.text(cx),
-//             "const ONE: usize = 1;\nconst TWO: usize = one::ONE + one::ONE;"
-//         );
-//         editor.redo(&Redo, cx);
-//         assert_eq!(
-//             editor.text(cx),
-//             "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
-//         );
-//     });
-
-//     // Ensure temporary rename edits cannot be undone/redone.
-//     editor_b.update(cx_b, |editor, cx| {
-//         editor.undo(&Undo, cx);
-//         assert_eq!(editor.text(cx), "const ONE: usize = 1;");
-//         editor.undo(&Undo, cx);
-//         assert_eq!(editor.text(cx), "const ONE: usize = 1;");
-//         editor.redo(&Redo, cx);
-//         assert_eq!(editor.text(cx), "const THREE: usize = 1;");
-//     })
-// }
-
-// #[gpui::test(iterations = 10)]
-// async fn test_language_server_statuses(
-//     executor: BackgroundExecutor,
-//     cx_a: &mut TestAppContext,
-//     cx_b: &mut TestAppContext,
-// ) {
-//     let mut server = TestServer::start(&executor).await;
-//     let client_a = server.create_client(cx_a, "user_a").await;
-//     let client_b = server.create_client(cx_b, "user_b").await;
-//     server
-//         .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
-//         .await;
-//     let active_call_a = cx_a.read(ActiveCall::global);
-
-//     cx_b.update(editor::init);
-
-//     // Set up a fake language server.
-//     let mut language = Language::new(
-//         LanguageConfig {
-//             name: "Rust".into(),
-//             path_suffixes: vec!["rs".to_string()],
-//             ..Default::default()
-//         },
-//         Some(tree_sitter_rust::language()),
-//     );
-//     let mut fake_language_servers = language
-//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-//             name: "the-language-server",
-//             ..Default::default()
-//         }))
-//         .await;
-//     client_a.language_registry().add(Arc::new(language));
-
-//     client_a
-//         .fs()
-//         .insert_tree(
-//             "/dir",
-//             json!({
-//                 "main.rs": "const ONE: usize = 1;",
-//             }),
-//         )
-//         .await;
-//     let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
-
-//     let _buffer_a = project_a
-//         .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
-//         .await
-//         .unwrap();
-
-//     let fake_language_server = fake_language_servers.next().await.unwrap();
-//     fake_language_server.start_progress("the-token").await;
-//     fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
-//         token: lsp::NumberOrString::String("the-token".to_string()),
-//         value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
-//             lsp::WorkDoneProgressReport {
-//                 message: Some("the-message".to_string()),
-//                 ..Default::default()
-//             },
-//         )),
-//     });
-//     executor.run_until_parked();
-
-//     project_a.read_with(cx_a, |project, _| {
-//         let status = project.language_server_statuses().next().unwrap();
-//         assert_eq!(status.name, "the-language-server");
-//         assert_eq!(status.pending_work.len(), 1);
-//         assert_eq!(
-//             status.pending_work["the-token"].message.as_ref().unwrap(),
-//             "the-message"
-//         );
-//     });
-
-//     let project_id = active_call_a
-//         .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
-//         .await
-//         .unwrap();
-//     executor.run_until_parked();
-//     let project_b = client_b.build_remote_project(project_id, cx_b).await;
-
-//     project_b.read_with(cx_b, |project, _| {
-//         let status = project.language_server_statuses().next().unwrap();
-//         assert_eq!(status.name, "the-language-server");
-//     });
-
-//     fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
-//         token: lsp::NumberOrString::String("the-token".to_string()),
-//         value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
-//             lsp::WorkDoneProgressReport {
-//                 message: Some("the-message-2".to_string()),
-//                 ..Default::default()
-//             },
-//         )),
-//     });
-//     executor.run_until_parked();
-
-//     project_a.read_with(cx_a, |project, _| {
-//         let status = project.language_server_statuses().next().unwrap();
-//         assert_eq!(status.name, "the-language-server");
-//         assert_eq!(status.pending_work.len(), 1);
-//         assert_eq!(
-//             status.pending_work["the-token"].message.as_ref().unwrap(),
-//             "the-message-2"
-//         );
-//     });
-
-//     project_b.read_with(cx_b, |project, _| {
-//         let status = project.language_server_statuses().next().unwrap();
-//         assert_eq!(status.name, "the-language-server");
-//         assert_eq!(status.pending_work.len(), 1);
-//         assert_eq!(
-//             status.pending_work["the-token"].message.as_ref().unwrap(),
-//             "the-message-2"
-//         );
-//     });
-// }
-
-// #[gpui::test(iterations = 10)]
-// async fn test_share_project(
-//     executor: BackgroundExecutor,
-//     cx_a: &mut TestAppContext,
-//     cx_b: &mut TestAppContext,
-//     cx_c: &mut TestAppContext,
-// ) {
-//     let window_b = cx_b.add_empty_window();
-//     let mut server = TestServer::start(executor).await;
-//     let client_a = server.create_client(cx_a, "user_a").await;
-//     let client_b = server.create_client(cx_b, "user_b").await;
-//     let client_c = server.create_client(cx_c, "user_c").await;
-//     server
-//         .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
-//         .await;
-//     let active_call_a = cx_a.read(ActiveCall::global);
-//     let active_call_b = cx_b.read(ActiveCall::global);
-//     let active_call_c = cx_c.read(ActiveCall::global);
-
-//     client_a
-//         .fs()
-//         .insert_tree(
-//             "/a",
-//             json!({
-//                 ".gitignore": "ignored-dir",
-//                 "a.txt": "a-contents",
-//                 "b.txt": "b-contents",
-//                 "ignored-dir": {
-//                     "c.txt": "",
-//                     "d.txt": "",
-//                 }
-//             }),
-//         )
-//         .await;
-
-//     // Invite client B to collaborate on a project
-//     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
-//     active_call_a
-//         .update(cx_a, |call, cx| {
-//             call.invite(client_b.user_id().unwrap(), Some(project_a.clone()), cx)
-//         })
-//         .await
-//         .unwrap();
-
-//     // Join that project as client B
-
-//     let incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
-//     executor.run_until_parked();
-//     let call = incoming_call_b.borrow().clone().unwrap();
-//     assert_eq!(call.calling_user.github_login, "user_a");
-//     let initial_project = call.initial_project.unwrap();
-//     active_call_b
-//         .update(cx_b, |call, cx| call.accept_incoming(cx))
-//         .await
-//         .unwrap();
-//     let client_b_peer_id = client_b.peer_id().unwrap();
-//     let project_b = client_b
-//         .build_remote_project(initial_project.id, cx_b)
-//         .await;
-
-//     let replica_id_b = project_b.read_with(cx_b, |project, _| project.replica_id());
-
-//     executor.run_until_parked();
-
-//     project_a.read_with(cx_a, |project, _| {
-//         let client_b_collaborator = project.collaborators().get(&client_b_peer_id).unwrap();
-//         assert_eq!(client_b_collaborator.replica_id, replica_id_b);
-//     });
-
-//     project_b.read_with(cx_b, |project, cx| {
-//         let worktree = project.worktrees().next().unwrap().read(cx);
-//         assert_eq!(
-//             worktree.paths().map(AsRef::as_ref).collect::<Vec<_>>(),
-//             [
-//                 Path::new(".gitignore"),
-//                 Path::new("a.txt"),
-//                 Path::new("b.txt"),
-//                 Path::new("ignored-dir"),
-//             ]
-//         );
-//     });
-
-//     project_b
-//         .update(cx_b, |project, cx| {
-//             let worktree = project.worktrees().next().unwrap();
-//             let entry = worktree.read(cx).entry_for_path("ignored-dir").unwrap();
-//             project.expand_entry(worktree_id, entry.id, cx).unwrap()
-//         })
-//         .await
-//         .unwrap();
-
-//     project_b.read_with(cx_b, |project, cx| {
-//         let worktree = project.worktrees().next().unwrap().read(cx);
-//         assert_eq!(
-//             worktree.paths().map(AsRef::as_ref).collect::<Vec<_>>(),
-//             [
-//                 Path::new(".gitignore"),
-//                 Path::new("a.txt"),
-//                 Path::new("b.txt"),
-//                 Path::new("ignored-dir"),
-//                 Path::new("ignored-dir/c.txt"),
-//                 Path::new("ignored-dir/d.txt"),
-//             ]
-//         );
-//     });
-
-//     // Open the same file as client B and client A.
-//     let buffer_b = project_b
-//         .update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.txt"), cx))
-//         .await
-//         .unwrap();
-
-//     buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "b-contents"));
-
-//     project_a.read_with(cx_a, |project, cx| {
-//         assert!(project.has_open_buffer((worktree_id, "b.txt"), cx))
-//     });
-//     let buffer_a = project_a
-//         .update(cx_a, |p, cx| p.open_buffer((worktree_id, "b.txt"), cx))
-//         .await
-//         .unwrap();
-
-//     let editor_b = window_b.build_view(cx_b, |cx| Editor::for_buffer(buffer_b, None, cx));
-
-//     // Client A sees client B's selection
-//     executor.run_until_parked();
-
-//     buffer_a.read_with(cx_a, |buffer, _| {
-//         buffer
-//             .snapshot()
-//             .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
-//             .count()
-//             == 1
-//     });
-
-//     // Edit the buffer as client B and see that edit as client A.
-//     editor_b.update(cx_b, |editor, cx| editor.handle_input("ok, ", cx));
-//     executor.run_until_parked();
-
-//     buffer_a.read_with(cx_a, |buffer, _| {
-//         assert_eq!(buffer.text(), "ok, b-contents")
-//     });
-
-//     // Client B can invite client C on a project shared by client A.
-//     active_call_b
-//         .update(cx_b, |call, cx| {
-//             call.invite(client_c.user_id().unwrap(), Some(project_b.clone()), cx)
-//         })
-//         .await
-//         .unwrap();
-
-//     let incoming_call_c = active_call_c.read_with(cx_c, |call, _| call.incoming());
-//     executor.run_until_parked();
-//     let call = incoming_call_c.borrow().clone().unwrap();
-//     assert_eq!(call.calling_user.github_login, "user_b");
-//     let initial_project = call.initial_project.unwrap();
-//     active_call_c
-//         .update(cx_c, |call, cx| call.accept_incoming(cx))
-//         .await
-//         .unwrap();
-//     let _project_c = client_c
-//         .build_remote_project(initial_project.id, cx_c)
-//         .await;
-
-//     // Client B closes the editor, and client A sees client B's selections removed.
-//     cx_b.update(move |_| drop(editor_b));
-//     executor.run_until_parked();
-
-//     buffer_a.read_with(cx_a, |buffer, _| {
-//         buffer
-//             .snapshot()
-//             .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
-//             .count()
-//             == 0
-//     });
-// }
-
-// #[gpui::test(iterations = 10)]
-// async fn test_on_input_format_from_host_to_guest(
-//     executor: BackgroundExecutor,
-//     cx_a: &mut TestAppContext,
-//     cx_b: &mut TestAppContext,
-// ) {
-//     let mut server = TestServer::start(&executor).await;
-//     let client_a = server.create_client(cx_a, "user_a").await;
-//     let client_b = server.create_client(cx_b, "user_b").await;
-//     server
-//         .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
-//         .await;
-//     let active_call_a = cx_a.read(ActiveCall::global);
-
-//     // Set up a fake language server.
-//     let mut language = Language::new(
-//         LanguageConfig {
-//             name: "Rust".into(),
-//             path_suffixes: vec!["rs".to_string()],
-//             ..Default::default()
-//         },
-//         Some(tree_sitter_rust::language()),
-//     );
-//     let mut fake_language_servers = language
-//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-//             capabilities: lsp::ServerCapabilities {
-//                 document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
-//                     first_trigger_character: ":".to_string(),
-//                     more_trigger_character: Some(vec![">".to_string()]),
-//                 }),
-//                 ..Default::default()
-//             },
-//             ..Default::default()
-//         }))
-//         .await;
-//     client_a.language_registry().add(Arc::new(language));
-
-//     client_a
-//         .fs()
-//         .insert_tree(
-//             "/a",
-//             json!({
-//                 "main.rs": "fn main() { a }",
-//                 "other.rs": "// Test file",
-//             }),
-//         )
-//         .await;
-//     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
-//     let project_id = active_call_a
-//         .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
-//         .await
-//         .unwrap();
-//     let project_b = client_b.build_remote_project(project_id, cx_b).await;
-
-//     // Open a file in an editor as the host.
-//     let buffer_a = project_a
-//         .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
-//         .await
-//         .unwrap();
-//     let window_a = cx_a.add_empty_window();
-//     let editor_a = window_a
-//         .update(cx_a, |_, cx| {
-//             cx.build_view(|cx| Editor::for_buffer(buffer_a, Some(project_a.clone()), cx))
-//         })
-//         .unwrap();
-
-//     let fake_language_server = fake_language_servers.next().await.unwrap();
-//     executor.run_until_parked();
-
-//     // Receive an OnTypeFormatting request as the host's language server.
-//     // Return some formattings from the host's language server.
-//     fake_language_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(
-//         |params, _| async move {
-//             assert_eq!(
-//                 params.text_document_position.text_document.uri,
-//                 lsp::Url::from_file_path("/a/main.rs").unwrap(),
-//             );
-//             assert_eq!(
-//                 params.text_document_position.position,
-//                 lsp::Position::new(0, 14),
-//             );
-
-//             Ok(Some(vec![lsp::TextEdit {
-//                 new_text: "~<".to_string(),
-//                 range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
-//             }]))
-//         },
-//     );
-
-//     // Open the buffer on the guest and see that the formattings worked
-//     let buffer_b = project_b
-//         .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
-//         .await
-//         .unwrap();
-
-//     // Type a on type formatting trigger character as the guest.
-//     editor_a.update(cx_a, |editor, cx| {
-//         cx.focus(&editor_a);
-//         editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-//         editor.handle_input(">", cx);
-//     });
-
-//     executor.run_until_parked();
-
-//     buffer_b.read_with(cx_b, |buffer, _| {
-//         assert_eq!(buffer.text(), "fn main() { a>~< }")
-//     });
-
-//     // Undo should remove LSP edits first
-//     editor_a.update(cx_a, |editor, cx| {
-//         assert_eq!(editor.text(cx), "fn main() { a>~< }");
-//         editor.undo(&Undo, cx);
-//         assert_eq!(editor.text(cx), "fn main() { a> }");
-//     });
-//     executor.run_until_parked();
-
-//     buffer_b.read_with(cx_b, |buffer, _| {
-//         assert_eq!(buffer.text(), "fn main() { a> }")
-//     });
-
-//     editor_a.update(cx_a, |editor, cx| {
-//         assert_eq!(editor.text(cx), "fn main() { a> }");
-//         editor.undo(&Undo, cx);
-//         assert_eq!(editor.text(cx), "fn main() { a }");
-//     });
-//     executor.run_until_parked();
-
-//     buffer_b.read_with(cx_b, |buffer, _| {
-//         assert_eq!(buffer.text(), "fn main() { a }")
-//     });
-// }
-
-// #[gpui::test(iterations = 10)]
-// async fn test_on_input_format_from_guest_to_host(
-//     executor: BackgroundExecutor,
-//     cx_a: &mut TestAppContext,
-//     cx_b: &mut TestAppContext,
-// ) {
-//     let mut server = TestServer::start(&executor).await;
-//     let client_a = server.create_client(cx_a, "user_a").await;
-//     let client_b = server.create_client(cx_b, "user_b").await;
-//     server
-//         .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
-//         .await;
-//     let active_call_a = cx_a.read(ActiveCall::global);
-
-//     // Set up a fake language server.
-//     let mut language = Language::new(
-//         LanguageConfig {
-//             name: "Rust".into(),
-//             path_suffixes: vec!["rs".to_string()],
-//             ..Default::default()
-//         },
-//         Some(tree_sitter_rust::language()),
-//     );
-//     let mut fake_language_servers = language
-//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-//             capabilities: lsp::ServerCapabilities {
-//                 document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
-//                     first_trigger_character: ":".to_string(),
-//                     more_trigger_character: Some(vec![">".to_string()]),
-//                 }),
-//                 ..Default::default()
-//             },
-//             ..Default::default()
-//         }))
-//         .await;
-//     client_a.language_registry().add(Arc::new(language));
-
-//     client_a
-//         .fs()
-//         .insert_tree(
-//             "/a",
-//             json!({
-//                 "main.rs": "fn main() { a }",
-//                 "other.rs": "// Test file",
-//             }),
-//         )
-//         .await;
-//     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
-//     let project_id = active_call_a
-//         .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
-//         .await
-//         .unwrap();
-//     let project_b = client_b.build_remote_project(project_id, cx_b).await;
-
-//     // Open a file in an editor as the guest.
-//     let buffer_b = project_b
-//         .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
-//         .await
-//         .unwrap();
-//     let window_b = cx_b.add_empty_window();
-//     let editor_b = window_b.build_view(cx_b, |cx| {
-//         Editor::for_buffer(buffer_b, Some(project_b.clone()), cx)
-//     });
-
-//     let fake_language_server = fake_language_servers.next().await.unwrap();
-//     executor.run_until_parked();
-//     // Type a on type formatting trigger character as the guest.
-//     editor_b.update(cx_b, |editor, cx| {
-//         editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-//         editor.handle_input(":", cx);
-//         cx.focus(&editor_b);
-//     });
-
-//     // Receive an OnTypeFormatting request as the host's language server.
-//     // Return some formattings from the host's language server.
-//     cx_a.foreground().start_waiting();
-//     fake_language_server
-//         .handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
-//             assert_eq!(
-//                 params.text_document_position.text_document.uri,
-//                 lsp::Url::from_file_path("/a/main.rs").unwrap(),
-//             );
-//             assert_eq!(
-//                 params.text_document_position.position,
-//                 lsp::Position::new(0, 14),
-//             );
-
-//             Ok(Some(vec![lsp::TextEdit {
-//                 new_text: "~:".to_string(),
-//                 range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
-//             }]))
-//         })
-//         .next()
-//         .await
-//         .unwrap();
-//     cx_a.foreground().finish_waiting();
-
-//     // Open the buffer on the host and see that the formattings worked
-//     let buffer_a = project_a
-//         .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
-//         .await
-//         .unwrap();
-//     executor.run_until_parked();
-
-//     buffer_a.read_with(cx_a, |buffer, _| {
-//         assert_eq!(buffer.text(), "fn main() { a:~: }")
-//     });
-
-//     // Undo should remove LSP edits first
-//     editor_b.update(cx_b, |editor, cx| {
-//         assert_eq!(editor.text(cx), "fn main() { a:~: }");
-//         editor.undo(&Undo, cx);
-//         assert_eq!(editor.text(cx), "fn main() { a: }");
-//     });
-//     executor.run_until_parked();
-
-//     buffer_a.read_with(cx_a, |buffer, _| {
-//         assert_eq!(buffer.text(), "fn main() { a: }")
-//     });
-
-//     editor_b.update(cx_b, |editor, cx| {
-//         assert_eq!(editor.text(cx), "fn main() { a: }");
-//         editor.undo(&Undo, cx);
-//         assert_eq!(editor.text(cx), "fn main() { a }");
-//     });
-//     executor.run_until_parked();
-
-//     buffer_a.read_with(cx_a, |buffer, _| {
-//         assert_eq!(buffer.text(), "fn main() { a }")
-//     });
-// }
-
-// #[gpui::test(iterations = 10)]
-// async fn test_mutual_editor_inlay_hint_cache_update(
-//     executor: BackgroundExecutor,
-//     cx_a: &mut TestAppContext,
-//     cx_b: &mut TestAppContext,
-// ) {
-//     let mut server = TestServer::start(&executor).await;
-//     let client_a = server.create_client(cx_a, "user_a").await;
-//     let client_b = server.create_client(cx_b, "user_b").await;
-//     server
-//         .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
-//         .await;
-//     let active_call_a = cx_a.read(ActiveCall::global);
-//     let active_call_b = cx_b.read(ActiveCall::global);
-
-//     cx_a.update(editor::init);
-//     cx_b.update(editor::init);
-
-//     cx_a.update(|cx| {
-//         cx.update_global(|store: &mut SettingsStore, cx| {
-//             store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
-//                 settings.defaults.inlay_hints = Some(InlayHintSettings {
-//                     enabled: true,
-//                     show_type_hints: true,
-//                     show_parameter_hints: false,
-//                     show_other_hints: true,
-//                 })
-//             });
-//         });
-//     });
-//     cx_b.update(|cx| {
-//         cx.update_global(|store: &mut SettingsStore, cx| {
-//             store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
-//                 settings.defaults.inlay_hints = Some(InlayHintSettings {
-//                     enabled: true,
-//                     show_type_hints: true,
-//                     show_parameter_hints: false,
-//                     show_other_hints: true,
-//                 })
-//             });
-//         });
-//     });
-
-//     let mut language = Language::new(
-//         LanguageConfig {
-//             name: "Rust".into(),
-//             path_suffixes: vec!["rs".to_string()],
-//             ..Default::default()
-//         },
-//         Some(tree_sitter_rust::language()),
-//     );
-//     let mut fake_language_servers = language
-//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-//             capabilities: lsp::ServerCapabilities {
-//                 inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-//                 ..Default::default()
-//             },
-//             ..Default::default()
-//         }))
-//         .await;
-//     let language = Arc::new(language);
-//     client_a.language_registry().add(Arc::clone(&language));
-//     client_b.language_registry().add(language);
-
-//     // Client A opens a project.
-//     client_a
-//         .fs()
-//         .insert_tree(
-//             "/a",
-//             json!({
-//                 "main.rs": "fn main() { a } // and some long comment to ensure inlay hints are not trimmed out",
-//                 "other.rs": "// Test file",
-//             }),
-//         )
-//         .await;
-//     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
-//     active_call_a
-//         .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
-//         .await
-//         .unwrap();
-//     let project_id = active_call_a
-//         .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
-//         .await
-//         .unwrap();
-
-//     // Client B joins the project
-//     let project_b = client_b.build_remote_project(project_id, cx_b).await;
-//     active_call_b
-//         .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
-//         .await
-//         .unwrap();
-
-//     let workspace_a = client_a.build_workspace(&project_a, cx_a).root_view(cx_a);
-//     cx_a.foreground().start_waiting();
-
-//     // The host opens a rust file.
-//     let _buffer_a = project_a
-//         .update(cx_a, |project, cx| {
-//             project.open_local_buffer("/a/main.rs", cx)
-//         })
-//         .await
-//         .unwrap();
-//     let fake_language_server = fake_language_servers.next().await.unwrap();
-//     let editor_a = workspace_a
-//         .update(cx_a, |workspace, cx| {
-//             workspace.open_path((worktree_id, "main.rs"), None, true, cx)
-//         })
-//         .await
-//         .unwrap()
-//         .downcast::<Editor>()
-//         .unwrap();
-
-//     // Set up the language server to return an additional inlay hint on each request.
-//     let edits_made = Arc::new(AtomicUsize::new(0));
-//     let closure_edits_made = Arc::clone(&edits_made);
-//     fake_language_server
-//         .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-//             let task_edits_made = Arc::clone(&closure_edits_made);
-//             async move {
-//                 assert_eq!(
-//                     params.text_document.uri,
-//                     lsp::Url::from_file_path("/a/main.rs").unwrap(),
-//                 );
-//                 let edits_made = task_edits_made.load(atomic::Ordering::Acquire);
-//                 Ok(Some(vec![lsp::InlayHint {
-//                     position: lsp::Position::new(0, edits_made as u32),
-//                     label: lsp::InlayHintLabel::String(edits_made.to_string()),
-//                     kind: None,
-//                     text_edits: None,
-//                     tooltip: None,
-//                     padding_left: None,
-//                     padding_right: None,
-//                     data: None,
-//                 }]))
-//             }
-//         })
-//         .next()
-//         .await
-//         .unwrap();
-
-//     executor.run_until_parked();
-
-//     let initial_edit = edits_made.load(atomic::Ordering::Acquire);
-//     editor_a.update(cx_a, |editor, _| {
-//         assert_eq!(
-//             vec![initial_edit.to_string()],
-//             extract_hint_labels(editor),
-//             "Host should get its first hints when opens an editor"
-//         );
-//         let inlay_cache = editor.inlay_hint_cache();
-//         assert_eq!(
-//             inlay_cache.version(),
-//             1,
-//             "Host editor update the cache version after every cache/view change",
-//         );
-//     });
-//     let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
-//     let editor_b = workspace_b
-//         .update(cx_b, |workspace, cx| {
-//             workspace.open_path((worktree_id, "main.rs"), None, true, cx)
-//         })
-//         .await
-//         .unwrap()
-//         .downcast::<Editor>()
-//         .unwrap();
-
-//     executor.run_until_parked();
-//     editor_b.update(cx_b, |editor, _| {
-//         assert_eq!(
-//             vec![initial_edit.to_string()],
-//             extract_hint_labels(editor),
-//             "Client should get its first hints when opens an editor"
-//         );
-//         let inlay_cache = editor.inlay_hint_cache();
-//         assert_eq!(
-//             inlay_cache.version(),
-//             1,
-//             "Guest editor update the cache version after every cache/view change"
-//         );
-//     });
-
-//     let after_client_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
-//     editor_b.update(cx_b, |editor, cx| {
-//         editor.change_selections(None, cx, |s| s.select_ranges([13..13].clone()));
-//         editor.handle_input(":", cx);
-//         cx.focus(&editor_b);
-//     });
-
-//     executor.run_until_parked();
-//     editor_a.update(cx_a, |editor, _| {
-//         assert_eq!(
-//             vec![after_client_edit.to_string()],
-//             extract_hint_labels(editor),
-//         );
-//         let inlay_cache = editor.inlay_hint_cache();
-//         assert_eq!(inlay_cache.version(), 2);
-//     });
-//     editor_b.update(cx_b, |editor, _| {
-//         assert_eq!(
-//             vec![after_client_edit.to_string()],
-//             extract_hint_labels(editor),
-//         );
-//         let inlay_cache = editor.inlay_hint_cache();
-//         assert_eq!(inlay_cache.version(), 2);
-//     });
-
-//     let after_host_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
-//     editor_a.update(cx_a, |editor, cx| {
-//         editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-//         editor.handle_input("a change to increment both buffers' versions", cx);
-//         cx.focus(&editor_a);
-//     });
-
-//     executor.run_until_parked();
-//     editor_a.update(cx_a, |editor, _| {
-//         assert_eq!(
-//             vec![after_host_edit.to_string()],
-//             extract_hint_labels(editor),
-//         );
-//         let inlay_cache = editor.inlay_hint_cache();
-//         assert_eq!(inlay_cache.version(), 3);
-//     });
-//     editor_b.update(cx_b, |editor, _| {
-//         assert_eq!(
-//             vec![after_host_edit.to_string()],
-//             extract_hint_labels(editor),
-//         );
-//         let inlay_cache = editor.inlay_hint_cache();
-//         assert_eq!(inlay_cache.version(), 3);
-//     });
-
-//     let after_special_edit_for_refresh = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
-//     fake_language_server
-//         .request::<lsp::request::InlayHintRefreshRequest>(())
-//         .await
-//         .expect("inlay refresh request failed");
-
-//     executor.run_until_parked();
-//     editor_a.update(cx_a, |editor, _| {
-//         assert_eq!(
-//             vec![after_special_edit_for_refresh.to_string()],
-//             extract_hint_labels(editor),
-//             "Host should react to /refresh LSP request"
-//         );
-//         let inlay_cache = editor.inlay_hint_cache();
-//         assert_eq!(
-//             inlay_cache.version(),
-//             4,
-//             "Host should accepted all edits and bump its cache version every time"
-//         );
-//     });
-//     editor_b.update(cx_b, |editor, _| {
-//         assert_eq!(
-//             vec![after_special_edit_for_refresh.to_string()],
-//             extract_hint_labels(editor),
-//             "Guest should get a /refresh LSP request propagated by host"
-//         );
-//         let inlay_cache = editor.inlay_hint_cache();
-//         assert_eq!(
-//             inlay_cache.version(),
-//             4,
-//             "Guest should accepted all edits and bump its cache version every time"
-//         );
-//     });
-// }
-
-// #[gpui::test(iterations = 10)]
-// async fn test_inlay_hint_refresh_is_forwarded(
-//     executor: BackgroundExecutor,
-//     cx_a: &mut TestAppContext,
-//     cx_b: &mut TestAppContext,
-// ) {
-//     let mut server = TestServer::start(&executor).await;
-//     let client_a = server.create_client(cx_a, "user_a").await;
-//     let client_b = server.create_client(cx_b, "user_b").await;
-//     server
-//         .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
-//         .await;
-//     let active_call_a = cx_a.read(ActiveCall::global);
-//     let active_call_b = cx_b.read(ActiveCall::global);
-
-//     cx_a.update(editor::init);
-//     cx_b.update(editor::init);
-
-//     cx_a.update(|cx| {
-//         cx.update_global(|store: &mut SettingsStore, cx| {
-//             store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
-//                 settings.defaults.inlay_hints = Some(InlayHintSettings {
-//                     enabled: false,
-//                     show_type_hints: false,
-//                     show_parameter_hints: false,
-//                     show_other_hints: false,
-//                 })
-//             });
-//         });
-//     });
-//     cx_b.update(|cx| {
-//         cx.update_global(|store: &mut SettingsStore, cx| {
-//             store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
-//                 settings.defaults.inlay_hints = Some(InlayHintSettings {
-//                     enabled: true,
-//                     show_type_hints: true,
-//                     show_parameter_hints: true,
-//                     show_other_hints: true,
-//                 })
-//             });
-//         });
-//     });
-
-//     let mut language = Language::new(
-//         LanguageConfig {
-//             name: "Rust".into(),
-//             path_suffixes: vec!["rs".to_string()],
-//             ..Default::default()
-//         },
-//         Some(tree_sitter_rust::language()),
-//     );
-//     let mut fake_language_servers = language
-//         .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-//             capabilities: lsp::ServerCapabilities {
-//                 inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-//                 ..Default::default()
-//             },
-//             ..Default::default()
-//         }))
-//         .await;
-//     let language = Arc::new(language);
-//     client_a.language_registry().add(Arc::clone(&language));
-//     client_b.language_registry().add(language);
-
-//     client_a
-//         .fs()
-//         .insert_tree(
-//             "/a",
-//             json!({
-//                 "main.rs": "fn main() { a } // and some long comment to ensure inlay hints are not trimmed out",
-//                 "other.rs": "// Test file",
-//             }),
-//         )
-//         .await;
-//     let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
-//     active_call_a
-//         .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
-//         .await
-//         .unwrap();
-//     let project_id = active_call_a
-//         .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
-//         .await
-//         .unwrap();
-
-//     let project_b = client_b.build_remote_project(project_id, cx_b).await;
-//     active_call_b
-//         .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
-//         .await
-//         .unwrap();
-
-//     let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
-//     let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
-//     cx_a.foreground().start_waiting();
-//     cx_b.foreground().start_waiting();
-
-//     let editor_a = workspace_a
-//         .update(cx_a, |workspace, cx| {
-//             workspace.open_path((worktree_id, "main.rs"), None, true, cx)
-//         })
-//         .await
-//         .unwrap()
-//         .downcast::<Editor>()
-//         .unwrap();
-
-//     let editor_b = workspace_b
-//         .update(cx_b, |workspace, cx| {
-//             workspace.open_path((worktree_id, "main.rs"), None, true, cx)
-//         })
-//         .await
-//         .unwrap()
-//         .downcast::<Editor>()
-//         .unwrap();
-
-//     let other_hints = Arc::new(AtomicBool::new(false));
-//     let fake_language_server = fake_language_servers.next().await.unwrap();
-//     let closure_other_hints = Arc::clone(&other_hints);
-//     fake_language_server
-//         .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-//             let task_other_hints = Arc::clone(&closure_other_hints);
-//             async move {
-//                 assert_eq!(
-//                     params.text_document.uri,
-//                     lsp::Url::from_file_path("/a/main.rs").unwrap(),
-//                 );
-//                 let other_hints = task_other_hints.load(atomic::Ordering::Acquire);
-//                 let character = if other_hints { 0 } else { 2 };
-//                 let label = if other_hints {
-//                     "other hint"
-//                 } else {
-//                     "initial hint"
-//                 };
-//                 Ok(Some(vec![lsp::InlayHint {
-//                     position: lsp::Position::new(0, character),
-//                     label: lsp::InlayHintLabel::String(label.to_string()),
-//                     kind: None,
-//                     text_edits: None,
-//                     tooltip: None,
-//                     padding_left: None,
-//                     padding_right: None,
-//                     data: None,
-//                 }]))
-//             }
-//         })
-//         .next()
-//         .await
-//         .unwrap();
-//     cx_a.foreground().finish_waiting();
-//     cx_b.foreground().finish_waiting();
-
-//     executor.run_until_parked();
-//     editor_a.update(cx_a, |editor, _| {
-//         assert!(
-//             extract_hint_labels(editor).is_empty(),
-//             "Host should get no hints due to them turned off"
-//         );
-//         let inlay_cache = editor.inlay_hint_cache();
-//         assert_eq!(
-//             inlay_cache.version(),
-//             0,
-//             "Turned off hints should not generate version updates"
-//         );
-//     });
-
-//     executor.run_until_parked();
-//     editor_b.update(cx_b, |editor, _| {
-//         assert_eq!(
-//             vec!["initial hint".to_string()],
-//             extract_hint_labels(editor),
-//             "Client should get its first hints when opens an editor"
-//         );
-//         let inlay_cache = editor.inlay_hint_cache();
-//         assert_eq!(
-//             inlay_cache.version(),
-//             1,
-//             "Should update cache verison after first hints"
-//         );
-//     });
-
-//     other_hints.fetch_or(true, atomic::Ordering::Release);
-//     fake_language_server
-//         .request::<lsp::request::InlayHintRefreshRequest>(())
-//         .await
-//         .expect("inlay refresh request failed");
-//     executor.run_until_parked();
-//     editor_a.update(cx_a, |editor, _| {
-//         assert!(
-//             extract_hint_labels(editor).is_empty(),
-//             "Host should get nop hints due to them turned off, even after the /refresh"
-//         );
-//         let inlay_cache = editor.inlay_hint_cache();
-//         assert_eq!(
-//             inlay_cache.version(),
-//             0,
-//             "Turned off hints should not generate version updates, again"
-//         );
-//     });
-
-//     executor.run_until_parked();
-//     editor_b.update(cx_b, |editor, _| {
-//         assert_eq!(
-//             vec!["other hint".to_string()],
-//             extract_hint_labels(editor),
-//             "Guest should get a /refresh LSP request propagated by host despite host hints are off"
-//         );
-//         let inlay_cache = editor.inlay_hint_cache();
-//         assert_eq!(
-//             inlay_cache.version(),
-//             2,
-//             "Guest should accepted all edits and bump its cache version every time"
-//         );
-//     });
-// }
-
-// fn extract_hint_labels(editor: &Editor) -> Vec<String> {
-//     let mut labels = Vec::new();
-//     for hint in editor.inlay_hint_cache().hints() {
-//         match hint.label {
-//             project::InlayHintLabel::String(s) => labels.push(s),
-//             _ => unreachable!(),
-//         }
-//     }
-//     labels
-// }
+use std::{
+    path::Path,
+    sync::{
+        atomic::{self, AtomicBool, AtomicUsize},
+        Arc,
+    },
+};
+
+use call::ActiveCall;
+use editor::{
+    test::editor_test_context::{AssertionContextManager, EditorTestContext},
+    ConfirmCodeAction, ConfirmCompletion, ConfirmRename, Editor, Redo, Rename, ToggleCodeActions,
+    Undo,
+};
+use futures::StreamExt;
+use gpui::{TestAppContext, VisualContext, VisualTestContext};
+use indoc::indoc;
+use language::{
+    language_settings::{AllLanguageSettings, InlayHintSettings},
+    tree_sitter_rust, FakeLspAdapter, Language, LanguageConfig,
+};
+use rpc::RECEIVE_TIMEOUT;
+use serde_json::json;
+use settings::SettingsStore;
+use text::Point;
+use workspace::Workspace;
+
+use crate::{rpc::RECONNECT_TIMEOUT, tests::TestServer};
+
+#[gpui::test(iterations = 10)]
+async fn test_host_disconnect(
+    cx_a: &mut TestAppContext,
+    cx_b: &mut TestAppContext,
+    cx_c: &mut TestAppContext,
+) {
+    let mut server = TestServer::start(cx_a.executor()).await;
+    let client_a = server.create_client(cx_a, "user_a").await;
+    let client_b = server.create_client(cx_b, "user_b").await;
+    let client_c = server.create_client(cx_c, "user_c").await;
+    server
+        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
+        .await;
+
+    cx_b.update(editor::init);
+
+    client_a
+        .fs()
+        .insert_tree(
+            "/a",
+            serde_json::json!({
+                "a.txt": "a-contents",
+                "b.txt": "b-contents",
+            }),
+        )
+        .await;
+
+    let active_call_a = cx_a.read(ActiveCall::global);
+    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
+
+    let worktree_a = project_a.read_with(cx_a, |project, _| project.worktrees().next().unwrap());
+    let project_id = active_call_a
+        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
+        .await
+        .unwrap();
+
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
+    cx_a.background_executor.run_until_parked();
+
+    assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
+
+    let workspace_b =
+        cx_b.add_window(|cx| Workspace::new(0, project_b.clone(), client_b.app_state.clone(), cx));
+    let cx_b = &mut VisualTestContext::from_window(*workspace_b, cx_b);
+
+    let editor_b = workspace_b
+        .update(cx_b, |workspace, cx| {
+            workspace.open_path((worktree_id, "b.txt"), None, true, cx)
+        })
+        .unwrap()
+        .await
+        .unwrap()
+        .downcast::<Editor>()
+        .unwrap();
+
+    //TODO: focus
+    assert!(cx_b.update_view(&editor_b, |editor, cx| editor.is_focused(cx)));
+    editor_b.update(cx_b, |editor, cx| editor.insert("X", cx));
+    //todo(is_edited)
+    // assert!(workspace_b.is_edited(cx_b));
+
+    // Drop client A's connection. Collaborators should disappear and the project should not be shown as shared.
+    server.forbid_connections();
+    server.disconnect_client(client_a.peer_id().unwrap());
+    cx_a.background_executor
+        .advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
+
+    project_a.read_with(cx_a, |project, _| project.collaborators().is_empty());
+
+    project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
+
+    project_b.read_with(cx_b, |project, _| project.is_read_only());
+
+    assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared()));
+
+    // Ensure client B's edited state is reset and that the whole window is blurred.
+
+    workspace_b
+        .update(cx_b, |_, cx| {
+            assert_eq!(cx.focused(), None);
+        })
+        .unwrap();
+    // assert!(!workspace_b.is_edited(cx_b));
+
+    // Ensure client B is not prompted to save edits when closing window after disconnecting.
+    let can_close = workspace_b
+        .update(cx_b, |workspace, cx| workspace.prepare_to_close(true, cx))
+        .unwrap()
+        .await
+        .unwrap();
+    assert!(can_close);
+
+    // Allow client A to reconnect to the server.
+    server.allow_connections();
+    cx_a.background_executor.advance_clock(RECEIVE_TIMEOUT);
+
+    // Client B calls client A again after they reconnected.
+    let active_call_b = cx_b.read(ActiveCall::global);
+    active_call_b
+        .update(cx_b, |call, cx| {
+            call.invite(client_a.user_id().unwrap(), None, cx)
+        })
+        .await
+        .unwrap();
+    cx_a.background_executor.run_until_parked();
+    active_call_a
+        .update(cx_a, |call, cx| call.accept_incoming(cx))
+        .await
+        .unwrap();
+
+    active_call_a
+        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
+        .await
+        .unwrap();
+
+    // Drop client A's connection again. We should still unshare it successfully.
+    server.forbid_connections();
+    server.disconnect_client(client_a.peer_id().unwrap());
+    cx_a.background_executor
+        .advance_clock(RECEIVE_TIMEOUT + RECONNECT_TIMEOUT);
+
+    project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
+}
+
+#[gpui::test]
+async fn test_newline_above_or_below_does_not_move_guest_cursor(
+    cx_a: &mut TestAppContext,
+    cx_b: &mut TestAppContext,
+) {
+    let mut server = TestServer::start(cx_a.executor()).await;
+    let client_a = server.create_client(cx_a, "user_a").await;
+    let client_b = server.create_client(cx_b, "user_b").await;
+    let executor = cx_a.executor();
+    server
+        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
+        .await;
+    let active_call_a = cx_a.read(ActiveCall::global);
+
+    client_a
+        .fs()
+        .insert_tree("/dir", json!({ "a.txt": "Some text\n" }))
+        .await;
+    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
+    let project_id = active_call_a
+        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
+        .await
+        .unwrap();
+
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
+
+    // Open a buffer as client A
+    let buffer_a = project_a
+        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
+        .await
+        .unwrap();
+    let window_a = cx_a.add_empty_window();
+    let editor_a =
+        window_a.build_view(cx_a, |cx| Editor::for_buffer(buffer_a, Some(project_a), cx));
+
+    let mut editor_cx_a = EditorTestContext {
+        cx: VisualTestContext::from_window(window_a, cx_a),
+        window: window_a.into(),
+        editor: editor_a,
+        assertion_cx: AssertionContextManager::new(),
+    };
+
+    let window_b = cx_b.add_empty_window();
+    let mut cx_b = VisualTestContext::from_window(window_b, cx_b);
+
+    // Open a buffer as client B
+    let buffer_b = project_b
+        .update(&mut cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
+        .await
+        .unwrap();
+    let editor_b = window_b.build_view(&mut cx_b, |cx| {
+        Editor::for_buffer(buffer_b, Some(project_b), cx)
+    });
+    let mut editor_cx_b = EditorTestContext {
+        cx: cx_b,
+        window: window_b.into(),
+        editor: editor_b,
+        assertion_cx: AssertionContextManager::new(),
+    };
+
+    // Test newline above
+    editor_cx_a.set_selections_state(indoc! {"
+        Some textˇ
+    "});
+    editor_cx_b.set_selections_state(indoc! {"
+        Some textˇ
+    "});
+    editor_cx_a.update_editor(|editor, cx| editor.newline_above(&editor::NewlineAbove, cx));
+    executor.run_until_parked();
+    editor_cx_a.assert_editor_state(indoc! {"
+        ˇ
+        Some text
+    "});
+    editor_cx_b.assert_editor_state(indoc! {"
+
+        Some textˇ
+    "});
+
+    // Test newline below
+    editor_cx_a.set_selections_state(indoc! {"
+
+        Some textˇ
+    "});
+    editor_cx_b.set_selections_state(indoc! {"
+
+        Some textˇ
+    "});
+    editor_cx_a.update_editor(|editor, cx| editor.newline_below(&editor::NewlineBelow, cx));
+    executor.run_until_parked();
+    editor_cx_a.assert_editor_state(indoc! {"
+
+        Some text
+        ˇ
+    "});
+    editor_cx_b.assert_editor_state(indoc! {"
+
+        Some textˇ
+
+    "});
+}
+
+#[gpui::test(iterations = 10)]
+async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
+    let mut server = TestServer::start(cx_a.executor()).await;
+    let client_a = server.create_client(cx_a, "user_a").await;
+    let client_b = server.create_client(cx_b, "user_b").await;
+    server
+        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
+        .await;
+    let active_call_a = cx_a.read(ActiveCall::global);
+
+    // Set up a fake language server.
+    let mut language = Language::new(
+        LanguageConfig {
+            name: "Rust".into(),
+            path_suffixes: vec!["rs".to_string()],
+            ..Default::default()
+        },
+        Some(tree_sitter_rust::language()),
+    );
+    let mut fake_language_servers = language
+        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+            capabilities: lsp::ServerCapabilities {
+                completion_provider: Some(lsp::CompletionOptions {
+                    trigger_characters: Some(vec![".".to_string()]),
+                    resolve_provider: Some(true),
+                    ..Default::default()
+                }),
+                ..Default::default()
+            },
+            ..Default::default()
+        }))
+        .await;
+    client_a.language_registry().add(Arc::new(language));
+
+    client_a
+        .fs()
+        .insert_tree(
+            "/a",
+            json!({
+                "main.rs": "fn main() { a }",
+                "other.rs": "",
+            }),
+        )
+        .await;
+    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
+    let project_id = active_call_a
+        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
+
+    // Open a file in an editor as the guest.
+    let buffer_b = project_b
+        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
+        .await
+        .unwrap();
+    let window_b = cx_b.add_empty_window();
+    let editor_b = window_b.build_view(cx_b, |cx| {
+        Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
+    });
+
+    let fake_language_server = fake_language_servers.next().await.unwrap();
+    cx_a.background_executor.run_until_parked();
+
+    buffer_b.read_with(cx_b, |buffer, _| {
+        assert!(!buffer.completion_triggers().is_empty())
+    });
+
+    let mut cx_b = VisualTestContext::from_window(window_b, cx_b);
+
+    // Type a completion trigger character as the guest.
+    editor_b.update(&mut cx_b, |editor, cx| {
+        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
+        editor.handle_input(".", cx);
+        editor_b.focus_handle(cx).focus(cx);
+    });
+
+    // Receive a completion request as the host's language server.
+    // Return some completions from the host's language server.
+    cx_a.executor().start_waiting();
+    fake_language_server
+        .handle_request::<lsp::request::Completion, _, _>(|params, _| async move {
+            assert_eq!(
+                params.text_document_position.text_document.uri,
+                lsp::Url::from_file_path("/a/main.rs").unwrap(),
+            );
+            assert_eq!(
+                params.text_document_position.position,
+                lsp::Position::new(0, 14),
+            );
+
+            Ok(Some(lsp::CompletionResponse::Array(vec![
+                lsp::CompletionItem {
+                    label: "first_method(…)".into(),
+                    detail: Some("fn(&mut self, B) -> C".into()),
+                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
+                        new_text: "first_method($1)".to_string(),
+                        range: lsp::Range::new(
+                            lsp::Position::new(0, 14),
+                            lsp::Position::new(0, 14),
+                        ),
+                    })),
+                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
+                    ..Default::default()
+                },
+                lsp::CompletionItem {
+                    label: "second_method(…)".into(),
+                    detail: Some("fn(&mut self, C) -> D<E>".into()),
+                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
+                        new_text: "second_method()".to_string(),
+                        range: lsp::Range::new(
+                            lsp::Position::new(0, 14),
+                            lsp::Position::new(0, 14),
+                        ),
+                    })),
+                    insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
+                    ..Default::default()
+                },
+            ])))
+        })
+        .next()
+        .await
+        .unwrap();
+    cx_a.executor().finish_waiting();
+
+    // Open the buffer on the host.
+    let buffer_a = project_a
+        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
+        .await
+        .unwrap();
+    cx_a.executor().run_until_parked();
+
+    buffer_a.read_with(cx_a, |buffer, _| {
+        assert_eq!(buffer.text(), "fn main() { a. }")
+    });
+
+    // Confirm a completion on the guest.
+
+    editor_b.update(&mut cx_b, |editor, cx| {
+        assert!(editor.context_menu_visible());
+        editor.confirm_completion(&ConfirmCompletion { item_ix: Some(0) }, cx);
+        assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
+    });
+
+    // Return a resolved completion from the host's language server.
+    // The resolved completion has an additional text edit.
+    fake_language_server.handle_request::<lsp::request::ResolveCompletionItem, _, _>(
+        |params, _| async move {
+            assert_eq!(params.label, "first_method(…)");
+            Ok(lsp::CompletionItem {
+                label: "first_method(…)".into(),
+                detail: Some("fn(&mut self, B) -> C".into()),
+                text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
+                    new_text: "first_method($1)".to_string(),
+                    range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
+                })),
+                additional_text_edits: Some(vec![lsp::TextEdit {
+                    new_text: "use d::SomeTrait;\n".to_string(),
+                    range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
+                }]),
+                insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
+                ..Default::default()
+            })
+        },
+    );
+
+    // The additional edit is applied.
+    cx_a.executor().run_until_parked();
+
+    buffer_a.read_with(cx_a, |buffer, _| {
+        assert_eq!(
+            buffer.text(),
+            "use d::SomeTrait;\nfn main() { a.first_method() }"
+        );
+    });
+
+    buffer_b.read_with(&mut cx_b, |buffer, _| {
+        assert_eq!(
+            buffer.text(),
+            "use d::SomeTrait;\nfn main() { a.first_method() }"
+        );
+    });
+}
+
+#[gpui::test(iterations = 10)]
+async fn test_collaborating_with_code_actions(
+    cx_a: &mut TestAppContext,
+    cx_b: &mut TestAppContext,
+) {
+    let mut server = TestServer::start(cx_a.executor()).await;
+    let client_a = server.create_client(cx_a, "user_a").await;
+    //
+    let client_b = server.create_client(cx_b, "user_b").await;
+    server
+        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
+        .await;
+    let active_call_a = cx_a.read(ActiveCall::global);
+
+    cx_b.update(editor::init);
+
+    // Set up a fake language server.
+    let mut language = Language::new(
+        LanguageConfig {
+            name: "Rust".into(),
+            path_suffixes: vec!["rs".to_string()],
+            ..Default::default()
+        },
+        Some(tree_sitter_rust::language()),
+    );
+    let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
+    client_a.language_registry().add(Arc::new(language));
+
+    client_a
+        .fs()
+        .insert_tree(
+            "/a",
+            json!({
+                "main.rs": "mod other;\nfn main() { let foo = other::foo(); }",
+                "other.rs": "pub fn foo() -> usize { 4 }",
+            }),
+        )
+        .await;
+    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
+    let project_id = active_call_a
+        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
+        .await
+        .unwrap();
+
+    // Join the project as client B.
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
+    let (workspace_b, cx_b) = client_b.build_workspace(&project_b, cx_b);
+    let editor_b = workspace_b
+        .update(cx_b, |workspace, cx| {
+            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
+        })
+        .await
+        .unwrap()
+        .downcast::<Editor>()
+        .unwrap();
+
+    let mut fake_language_server = fake_language_servers.next().await.unwrap();
+    let mut requests = fake_language_server
+        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
+            assert_eq!(
+                params.text_document.uri,
+                lsp::Url::from_file_path("/a/main.rs").unwrap(),
+            );
+            assert_eq!(params.range.start, lsp::Position::new(0, 0));
+            assert_eq!(params.range.end, lsp::Position::new(0, 0));
+            Ok(None)
+        });
+    cx_a.background_executor
+        .advance_clock(editor::CODE_ACTIONS_DEBOUNCE_TIMEOUT * 2);
+    requests.next().await;
+
+    // Move cursor to a location that contains code actions.
+    editor_b.update(cx_b, |editor, cx| {
+        editor.change_selections(None, cx, |s| {
+            s.select_ranges([Point::new(1, 31)..Point::new(1, 31)])
+        });
+    });
+    cx_b.focus_view(&editor_b);
+
+    let mut requests = fake_language_server
+        .handle_request::<lsp::request::CodeActionRequest, _, _>(|params, _| async move {
+            assert_eq!(
+                params.text_document.uri,
+                lsp::Url::from_file_path("/a/main.rs").unwrap(),
+            );
+            assert_eq!(params.range.start, lsp::Position::new(1, 31));
+            assert_eq!(params.range.end, lsp::Position::new(1, 31));
+
+            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
+                lsp::CodeAction {
+                    title: "Inline into all callers".to_string(),
+                    edit: Some(lsp::WorkspaceEdit {
+                        changes: Some(
+                            [
+                                (
+                                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
+                                    vec![lsp::TextEdit::new(
+                                        lsp::Range::new(
+                                            lsp::Position::new(1, 22),
+                                            lsp::Position::new(1, 34),
+                                        ),
+                                        "4".to_string(),
+                                    )],
+                                ),
+                                (
+                                    lsp::Url::from_file_path("/a/other.rs").unwrap(),
+                                    vec![lsp::TextEdit::new(
+                                        lsp::Range::new(
+                                            lsp::Position::new(0, 0),
+                                            lsp::Position::new(0, 27),
+                                        ),
+                                        "".to_string(),
+                                    )],
+                                ),
+                            ]
+                            .into_iter()
+                            .collect(),
+                        ),
+                        ..Default::default()
+                    }),
+                    data: Some(json!({
+                        "codeActionParams": {
+                            "range": {
+                                "start": {"line": 1, "column": 31},
+                                "end": {"line": 1, "column": 31},
+                            }
+                        }
+                    })),
+                    ..Default::default()
+                },
+            )]))
+        });
+    cx_a.background_executor
+        .advance_clock(editor::CODE_ACTIONS_DEBOUNCE_TIMEOUT * 2);
+    requests.next().await;
+
+    // Toggle code actions and wait for them to display.
+    editor_b.update(cx_b, |editor, cx| {
+        editor.toggle_code_actions(
+            &ToggleCodeActions {
+                deployed_from_indicator: false,
+            },
+            cx,
+        );
+    });
+    cx_a.background_executor.run_until_parked();
+
+    editor_b.update(cx_b, |editor, _| assert!(editor.context_menu_visible()));
+
+    fake_language_server.remove_request_handler::<lsp::request::CodeActionRequest>();
+
+    // Confirming the code action will trigger a resolve request.
+    let confirm_action = editor_b
+        .update(cx_b, |editor, cx| {
+            Editor::confirm_code_action(editor, &ConfirmCodeAction { item_ix: Some(0) }, cx)
+        })
+        .unwrap();
+    fake_language_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
+        |_, _| async move {
+            Ok(lsp::CodeAction {
+                title: "Inline into all callers".to_string(),
+                edit: Some(lsp::WorkspaceEdit {
+                    changes: Some(
+                        [
+                            (
+                                lsp::Url::from_file_path("/a/main.rs").unwrap(),
+                                vec![lsp::TextEdit::new(
+                                    lsp::Range::new(
+                                        lsp::Position::new(1, 22),
+                                        lsp::Position::new(1, 34),
+                                    ),
+                                    "4".to_string(),
+                                )],
+                            ),
+                            (
+                                lsp::Url::from_file_path("/a/other.rs").unwrap(),
+                                vec![lsp::TextEdit::new(
+                                    lsp::Range::new(
+                                        lsp::Position::new(0, 0),
+                                        lsp::Position::new(0, 27),
+                                    ),
+                                    "".to_string(),
+                                )],
+                            ),
+                        ]
+                        .into_iter()
+                        .collect(),
+                    ),
+                    ..Default::default()
+                }),
+                ..Default::default()
+            })
+        },
+    );
+
+    // After the action is confirmed, an editor containing both modified files is opened.
+    confirm_action.await.unwrap();
+
+    let code_action_editor = workspace_b.update(cx_b, |workspace, cx| {
+        workspace
+            .active_item(cx)
+            .unwrap()
+            .downcast::<Editor>()
+            .unwrap()
+    });
+    code_action_editor.update(cx_b, |editor, cx| {
+        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
+        editor.undo(&Undo, cx);
+        assert_eq!(
+            editor.text(cx),
+            "mod other;\nfn main() { let foo = other::foo(); }\npub fn foo() -> usize { 4 }"
+        );
+        editor.redo(&Redo, cx);
+        assert_eq!(editor.text(cx), "mod other;\nfn main() { let foo = 4; }\n");
+    });
+}
+
+#[gpui::test(iterations = 10)]
+async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
+    let mut server = TestServer::start(cx_a.executor()).await;
+    let client_a = server.create_client(cx_a, "user_a").await;
+    let client_b = server.create_client(cx_b, "user_b").await;
+    server
+        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
+        .await;
+    let active_call_a = cx_a.read(ActiveCall::global);
+
+    cx_b.update(editor::init);
+
+    // Set up a fake language server.
+    let mut language = Language::new(
+        LanguageConfig {
+            name: "Rust".into(),
+            path_suffixes: vec!["rs".to_string()],
+            ..Default::default()
+        },
+        Some(tree_sitter_rust::language()),
+    );
+    let mut fake_language_servers = language
+        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+            capabilities: lsp::ServerCapabilities {
+                rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
+                    prepare_provider: Some(true),
+                    work_done_progress_options: Default::default(),
+                })),
+                ..Default::default()
+            },
+            ..Default::default()
+        }))
+        .await;
+    client_a.language_registry().add(Arc::new(language));
+
+    client_a
+        .fs()
+        .insert_tree(
+            "/dir",
+            json!({
+                "one.rs": "const ONE: usize = 1;",
+                "two.rs": "const TWO: usize = one::ONE + one::ONE;"
+            }),
+        )
+        .await;
+    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
+    let project_id = active_call_a
+        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
+
+    let (workspace_b, cx_b) = client_b.build_workspace(&project_b, cx_b);
+    let editor_b = workspace_b
+        .update(cx_b, |workspace, cx| {
+            workspace.open_path((worktree_id, "one.rs"), None, true, cx)
+        })
+        .await
+        .unwrap()
+        .downcast::<Editor>()
+        .unwrap();
+    let fake_language_server = fake_language_servers.next().await.unwrap();
+
+    // Move cursor to a location that can be renamed.
+    let prepare_rename = editor_b.update(cx_b, |editor, cx| {
+        editor.change_selections(None, cx, |s| s.select_ranges([7..7]));
+        editor.rename(&Rename, cx).unwrap()
+    });
+
+    fake_language_server
+        .handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
+            assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
+            assert_eq!(params.position, lsp::Position::new(0, 7));
+            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
+                lsp::Position::new(0, 6),
+                lsp::Position::new(0, 9),
+            ))))
+        })
+        .next()
+        .await
+        .unwrap();
+    prepare_rename.await.unwrap();
+    editor_b.update(cx_b, |editor, cx| {
+        use editor::ToOffset;
+        let rename = editor.pending_rename().unwrap();
+        let buffer = editor.buffer().read(cx).snapshot(cx);
+        assert_eq!(
+            rename.range.start.to_offset(&buffer)..rename.range.end.to_offset(&buffer),
+            6..9
+        );
+        rename.editor.update(cx, |rename_editor, cx| {
+            rename_editor.buffer().update(cx, |rename_buffer, cx| {
+                rename_buffer.edit([(0..3, "THREE")], None, cx);
+            });
+        });
+    });
+
+    let confirm_rename = editor_b.update(cx_b, |editor, cx| {
+        Editor::confirm_rename(editor, &ConfirmRename, cx).unwrap()
+    });
+    fake_language_server
+        .handle_request::<lsp::request::Rename, _, _>(|params, _| async move {
+            assert_eq!(
+                params.text_document_position.text_document.uri.as_str(),
+                "file:///dir/one.rs"
+            );
+            assert_eq!(
+                params.text_document_position.position,
+                lsp::Position::new(0, 6)
+            );
+            assert_eq!(params.new_name, "THREE");
+            Ok(Some(lsp::WorkspaceEdit {
+                changes: Some(
+                    [
+                        (
+                            lsp::Url::from_file_path("/dir/one.rs").unwrap(),
+                            vec![lsp::TextEdit::new(
+                                lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
+                                "THREE".to_string(),
+                            )],
+                        ),
+                        (
+                            lsp::Url::from_file_path("/dir/two.rs").unwrap(),
+                            vec![
+                                lsp::TextEdit::new(
+                                    lsp::Range::new(
+                                        lsp::Position::new(0, 24),
+                                        lsp::Position::new(0, 27),
+                                    ),
+                                    "THREE".to_string(),
+                                ),
+                                lsp::TextEdit::new(
+                                    lsp::Range::new(
+                                        lsp::Position::new(0, 35),
+                                        lsp::Position::new(0, 38),
+                                    ),
+                                    "THREE".to_string(),
+                                ),
+                            ],
+                        ),
+                    ]
+                    .into_iter()
+                    .collect(),
+                ),
+                ..Default::default()
+            }))
+        })
+        .next()
+        .await
+        .unwrap();
+    confirm_rename.await.unwrap();
+
+    let rename_editor = workspace_b.update(cx_b, |workspace, cx| {
+        workspace.active_item_as::<Editor>(cx).unwrap()
+    });
+
+    rename_editor.update(cx_b, |editor, cx| {
+        assert_eq!(
+            editor.text(cx),
+            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
+        );
+        editor.undo(&Undo, cx);
+        assert_eq!(
+            editor.text(cx),
+            "const ONE: usize = 1;\nconst TWO: usize = one::ONE + one::ONE;"
+        );
+        editor.redo(&Redo, cx);
+        assert_eq!(
+            editor.text(cx),
+            "const THREE: usize = 1;\nconst TWO: usize = one::THREE + one::THREE;"
+        );
+    });
+
+    // Ensure temporary rename edits cannot be undone/redone.
+    editor_b.update(cx_b, |editor, cx| {
+        editor.undo(&Undo, cx);
+        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
+        editor.undo(&Undo, cx);
+        assert_eq!(editor.text(cx), "const ONE: usize = 1;");
+        editor.redo(&Redo, cx);
+        assert_eq!(editor.text(cx), "const THREE: usize = 1;");
+    })
+}
+
+#[gpui::test(iterations = 10)]
+async fn test_language_server_statuses(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
+    let mut server = TestServer::start(cx_a.executor()).await;
+    let executor = cx_a.executor();
+    let client_a = server.create_client(cx_a, "user_a").await;
+    let client_b = server.create_client(cx_b, "user_b").await;
+    server
+        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
+        .await;
+    let active_call_a = cx_a.read(ActiveCall::global);
+
+    cx_b.update(editor::init);
+
+    // Set up a fake language server.
+    let mut language = Language::new(
+        LanguageConfig {
+            name: "Rust".into(),
+            path_suffixes: vec!["rs".to_string()],
+            ..Default::default()
+        },
+        Some(tree_sitter_rust::language()),
+    );
+    let mut fake_language_servers = language
+        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+            name: "the-language-server",
+            ..Default::default()
+        }))
+        .await;
+    client_a.language_registry().add(Arc::new(language));
+
+    client_a
+        .fs()
+        .insert_tree(
+            "/dir",
+            json!({
+                "main.rs": "const ONE: usize = 1;",
+            }),
+        )
+        .await;
+    let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
+
+    let _buffer_a = project_a
+        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
+        .await
+        .unwrap();
+
+    let fake_language_server = fake_language_servers.next().await.unwrap();
+    fake_language_server.start_progress("the-token").await;
+    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
+        token: lsp::NumberOrString::String("the-token".to_string()),
+        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
+            lsp::WorkDoneProgressReport {
+                message: Some("the-message".to_string()),
+                ..Default::default()
+            },
+        )),
+    });
+    executor.run_until_parked();
+
+    project_a.read_with(cx_a, |project, _| {
+        let status = project.language_server_statuses().next().unwrap();
+        assert_eq!(status.name, "the-language-server");
+        assert_eq!(status.pending_work.len(), 1);
+        assert_eq!(
+            status.pending_work["the-token"].message.as_ref().unwrap(),
+            "the-message"
+        );
+    });
+
+    let project_id = active_call_a
+        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
+        .await
+        .unwrap();
+    executor.run_until_parked();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
+
+    project_b.read_with(cx_b, |project, _| {
+        let status = project.language_server_statuses().next().unwrap();
+        assert_eq!(status.name, "the-language-server");
+    });
+
+    fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
+        token: lsp::NumberOrString::String("the-token".to_string()),
+        value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Report(
+            lsp::WorkDoneProgressReport {
+                message: Some("the-message-2".to_string()),
+                ..Default::default()
+            },
+        )),
+    });
+    executor.run_until_parked();
+
+    project_a.read_with(cx_a, |project, _| {
+        let status = project.language_server_statuses().next().unwrap();
+        assert_eq!(status.name, "the-language-server");
+        assert_eq!(status.pending_work.len(), 1);
+        assert_eq!(
+            status.pending_work["the-token"].message.as_ref().unwrap(),
+            "the-message-2"
+        );
+    });
+
+    project_b.read_with(cx_b, |project, _| {
+        let status = project.language_server_statuses().next().unwrap();
+        assert_eq!(status.name, "the-language-server");
+        assert_eq!(status.pending_work.len(), 1);
+        assert_eq!(
+            status.pending_work["the-token"].message.as_ref().unwrap(),
+            "the-message-2"
+        );
+    });
+}
+
+#[gpui::test(iterations = 10)]
+async fn test_share_project(
+    cx_a: &mut TestAppContext,
+    cx_b: &mut TestAppContext,
+    cx_c: &mut TestAppContext,
+) {
+    let executor = cx_a.executor();
+    let window_b = cx_b.add_empty_window();
+    let mut server = TestServer::start(executor.clone()).await;
+    let client_a = server.create_client(cx_a, "user_a").await;
+    let client_b = server.create_client(cx_b, "user_b").await;
+    let client_c = server.create_client(cx_c, "user_c").await;
+    server
+        .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)])
+        .await;
+    let active_call_a = cx_a.read(ActiveCall::global);
+    let active_call_b = cx_b.read(ActiveCall::global);
+    let active_call_c = cx_c.read(ActiveCall::global);
+
+    client_a
+        .fs()
+        .insert_tree(
+            "/a",
+            json!({
+                ".gitignore": "ignored-dir",
+                "a.txt": "a-contents",
+                "b.txt": "b-contents",
+                "ignored-dir": {
+                    "c.txt": "",
+                    "d.txt": "",
+                }
+            }),
+        )
+        .await;
+
+    // Invite client B to collaborate on a project
+    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
+    active_call_a
+        .update(cx_a, |call, cx| {
+            call.invite(client_b.user_id().unwrap(), Some(project_a.clone()), cx)
+        })
+        .await
+        .unwrap();
+
+    // Join that project as client B
+
+    let incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming());
+    executor.run_until_parked();
+    let call = incoming_call_b.borrow().clone().unwrap();
+    assert_eq!(call.calling_user.github_login, "user_a");
+    let initial_project = call.initial_project.unwrap();
+    active_call_b
+        .update(cx_b, |call, cx| call.accept_incoming(cx))
+        .await
+        .unwrap();
+    let client_b_peer_id = client_b.peer_id().unwrap();
+    let project_b = client_b
+        .build_remote_project(initial_project.id, cx_b)
+        .await;
+
+    let replica_id_b = project_b.read_with(cx_b, |project, _| project.replica_id());
+
+    executor.run_until_parked();
+
+    project_a.read_with(cx_a, |project, _| {
+        let client_b_collaborator = project.collaborators().get(&client_b_peer_id).unwrap();
+        assert_eq!(client_b_collaborator.replica_id, replica_id_b);
+    });
+
+    project_b.read_with(cx_b, |project, cx| {
+        let worktree = project.worktrees().next().unwrap().read(cx);
+        assert_eq!(
+            worktree.paths().map(AsRef::as_ref).collect::<Vec<_>>(),
+            [
+                Path::new(".gitignore"),
+                Path::new("a.txt"),
+                Path::new("b.txt"),
+                Path::new("ignored-dir"),
+            ]
+        );
+    });
+
+    project_b
+        .update(cx_b, |project, cx| {
+            let worktree = project.worktrees().next().unwrap();
+            let entry = worktree.read(cx).entry_for_path("ignored-dir").unwrap();
+            project.expand_entry(worktree_id, entry.id, cx).unwrap()
+        })
+        .await
+        .unwrap();
+
+    project_b.read_with(cx_b, |project, cx| {
+        let worktree = project.worktrees().next().unwrap().read(cx);
+        assert_eq!(
+            worktree.paths().map(AsRef::as_ref).collect::<Vec<_>>(),
+            [
+                Path::new(".gitignore"),
+                Path::new("a.txt"),
+                Path::new("b.txt"),
+                Path::new("ignored-dir"),
+                Path::new("ignored-dir/c.txt"),
+                Path::new("ignored-dir/d.txt"),
+            ]
+        );
+    });
+
+    // Open the same file as client B and client A.
+    let buffer_b = project_b
+        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "b.txt"), cx))
+        .await
+        .unwrap();
+
+    buffer_b.read_with(cx_b, |buf, _| assert_eq!(buf.text(), "b-contents"));
+
+    project_a.read_with(cx_a, |project, cx| {
+        assert!(project.has_open_buffer((worktree_id, "b.txt"), cx))
+    });
+    let buffer_a = project_a
+        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "b.txt"), cx))
+        .await
+        .unwrap();
+
+    let editor_b = window_b.build_view(cx_b, |cx| Editor::for_buffer(buffer_b, None, cx));
+
+    // Client A sees client B's selection
+    executor.run_until_parked();
+
+    buffer_a.read_with(cx_a, |buffer, _| {
+        buffer
+            .snapshot()
+            .remote_selections_in_range(text::Anchor::MIN..text::Anchor::MAX)
+            .count()
+            == 1
+    });
+
+    // Edit the buffer as client B and see that edit as client A.
+    let mut cx_b = VisualTestContext::from_window(window_b, cx_b);
+    editor_b.update(&mut cx_b, |editor, cx| editor.handle_input("ok, ", cx));
+    executor.run_until_parked();
+
+    buffer_a.read_with(cx_a, |buffer, _| {
+        assert_eq!(buffer.text(), "ok, b-contents")
+    });
+
+    // Client B can invite client C on a project shared by client A.
+    active_call_b
+        .update(&mut cx_b, |call, cx| {
+            call.invite(client_c.user_id().unwrap(), Some(project_b.clone()), cx)
+        })
+        .await
+        .unwrap();
+
+    let incoming_call_c = active_call_c.read_with(cx_c, |call, _| call.incoming());
+    executor.run_until_parked();
+    let call = incoming_call_c.borrow().clone().unwrap();
+    assert_eq!(call.calling_user.github_login, "user_b");
+    let initial_project = call.initial_project.unwrap();
+    active_call_c
+        .update(cx_c, |call, cx| call.accept_incoming(cx))
+        .await
+        .unwrap();
+    let _project_c = client_c
+        .build_remote_project(initial_project.id, cx_c)
+        .await;
+
+    // Client B closes the editor, and client A sees client B's selections removed.
+    cx_b.update(move |_| drop(editor_b));
+    executor.run_until_parked();
+
+    buffer_a.read_with(cx_a, |buffer, _| {
+        buffer
+            .snapshot()
+            .remote_selections_in_range(text::Anchor::MIN..text::Anchor::MAX)
+            .count()
+            == 0
+    });
+}
+
+#[gpui::test(iterations = 10)]
+async fn test_on_input_format_from_host_to_guest(
+    cx_a: &mut TestAppContext,
+    cx_b: &mut TestAppContext,
+) {
+    let mut server = TestServer::start(cx_a.executor()).await;
+    let executor = cx_a.executor();
+    let client_a = server.create_client(cx_a, "user_a").await;
+    let client_b = server.create_client(cx_b, "user_b").await;
+    server
+        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
+        .await;
+    let active_call_a = cx_a.read(ActiveCall::global);
+
+    // Set up a fake language server.
+    let mut language = Language::new(
+        LanguageConfig {
+            name: "Rust".into(),
+            path_suffixes: vec!["rs".to_string()],
+            ..Default::default()
+        },
+        Some(tree_sitter_rust::language()),
+    );
+    let mut fake_language_servers = language
+        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+            capabilities: lsp::ServerCapabilities {
+                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
+                    first_trigger_character: ":".to_string(),
+                    more_trigger_character: Some(vec![">".to_string()]),
+                }),
+                ..Default::default()
+            },
+            ..Default::default()
+        }))
+        .await;
+    client_a.language_registry().add(Arc::new(language));
+
+    client_a
+        .fs()
+        .insert_tree(
+            "/a",
+            json!({
+                "main.rs": "fn main() { a }",
+                "other.rs": "// Test file",
+            }),
+        )
+        .await;
+    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
+    let project_id = active_call_a
+        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
+
+    // Open a file in an editor as the host.
+    let buffer_a = project_a
+        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
+        .await
+        .unwrap();
+    let window_a = cx_a.add_empty_window();
+    let editor_a = window_a
+        .update(cx_a, |_, cx| {
+            cx.new_view(|cx| Editor::for_buffer(buffer_a, Some(project_a.clone()), cx))
+        })
+        .unwrap();
+
+    let fake_language_server = fake_language_servers.next().await.unwrap();
+    executor.run_until_parked();
+
+    // Receive an OnTypeFormatting request as the host's language server.
+    // Return some formattings from the host's language server.
+    fake_language_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(
+        |params, _| async move {
+            assert_eq!(
+                params.text_document_position.text_document.uri,
+                lsp::Url::from_file_path("/a/main.rs").unwrap(),
+            );
+            assert_eq!(
+                params.text_document_position.position,
+                lsp::Position::new(0, 14),
+            );
+
+            Ok(Some(vec![lsp::TextEdit {
+                new_text: "~<".to_string(),
+                range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
+            }]))
+        },
+    );
+
+    // Open the buffer on the guest and see that the formattings worked
+    let buffer_b = project_b
+        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
+        .await
+        .unwrap();
+
+    let mut cx_a = VisualTestContext::from_window(window_a, cx_a);
+    // Type a on type formatting trigger character as the guest.
+    cx_a.focus_view(&editor_a);
+    editor_a.update(&mut cx_a, |editor, cx| {
+        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
+        editor.handle_input(">", cx);
+    });
+
+    executor.run_until_parked();
+
+    buffer_b.read_with(cx_b, |buffer, _| {
+        assert_eq!(buffer.text(), "fn main() { a>~< }")
+    });
+
+    // Undo should remove LSP edits first
+    editor_a.update(&mut cx_a, |editor, cx| {
+        assert_eq!(editor.text(cx), "fn main() { a>~< }");
+        editor.undo(&Undo, cx);
+        assert_eq!(editor.text(cx), "fn main() { a> }");
+    });
+    executor.run_until_parked();
+
+    buffer_b.read_with(cx_b, |buffer, _| {
+        assert_eq!(buffer.text(), "fn main() { a> }")
+    });
+
+    editor_a.update(&mut cx_a, |editor, cx| {
+        assert_eq!(editor.text(cx), "fn main() { a> }");
+        editor.undo(&Undo, cx);
+        assert_eq!(editor.text(cx), "fn main() { a }");
+    });
+    executor.run_until_parked();
+
+    buffer_b.read_with(cx_b, |buffer, _| {
+        assert_eq!(buffer.text(), "fn main() { a }")
+    });
+}
+
+#[gpui::test(iterations = 10)]
+async fn test_on_input_format_from_guest_to_host(
+    cx_a: &mut TestAppContext,
+    cx_b: &mut TestAppContext,
+) {
+    let mut server = TestServer::start(cx_a.executor()).await;
+    let executor = cx_a.executor();
+    let client_a = server.create_client(cx_a, "user_a").await;
+    let client_b = server.create_client(cx_b, "user_b").await;
+    server
+        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
+        .await;
+    let active_call_a = cx_a.read(ActiveCall::global);
+
+    // Set up a fake language server.
+    let mut language = Language::new(
+        LanguageConfig {
+            name: "Rust".into(),
+            path_suffixes: vec!["rs".to_string()],
+            ..Default::default()
+        },
+        Some(tree_sitter_rust::language()),
+    );
+    let mut fake_language_servers = language
+        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+            capabilities: lsp::ServerCapabilities {
+                document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
+                    first_trigger_character: ":".to_string(),
+                    more_trigger_character: Some(vec![">".to_string()]),
+                }),
+                ..Default::default()
+            },
+            ..Default::default()
+        }))
+        .await;
+    client_a.language_registry().add(Arc::new(language));
+
+    client_a
+        .fs()
+        .insert_tree(
+            "/a",
+            json!({
+                "main.rs": "fn main() { a }",
+                "other.rs": "// Test file",
+            }),
+        )
+        .await;
+    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
+    let project_id = active_call_a
+        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
+
+    // Open a file in an editor as the guest.
+    let buffer_b = project_b
+        .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
+        .await
+        .unwrap();
+    let window_b = cx_b.add_empty_window();
+    let editor_b = window_b.build_view(cx_b, |cx| {
+        Editor::for_buffer(buffer_b, Some(project_b.clone()), cx)
+    });
+
+    let fake_language_server = fake_language_servers.next().await.unwrap();
+    executor.run_until_parked();
+    let mut cx_b = VisualTestContext::from_window(window_b, cx_b);
+    // Type a on type formatting trigger character as the guest.
+    cx_b.focus_view(&editor_b);
+    editor_b.update(&mut cx_b, |editor, cx| {
+        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
+        editor.handle_input(":", cx);
+    });
+
+    // Receive an OnTypeFormatting request as the host's language server.
+    // Return some formattings from the host's language server.
+    executor.start_waiting();
+    fake_language_server
+        .handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
+            assert_eq!(
+                params.text_document_position.text_document.uri,
+                lsp::Url::from_file_path("/a/main.rs").unwrap(),
+            );
+            assert_eq!(
+                params.text_document_position.position,
+                lsp::Position::new(0, 14),
+            );
+
+            Ok(Some(vec![lsp::TextEdit {
+                new_text: "~:".to_string(),
+                range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
+            }]))
+        })
+        .next()
+        .await
+        .unwrap();
+    executor.finish_waiting();
+
+    // Open the buffer on the host and see that the formattings worked
+    let buffer_a = project_a
+        .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
+        .await
+        .unwrap();
+    executor.run_until_parked();
+
+    buffer_a.read_with(cx_a, |buffer, _| {
+        assert_eq!(buffer.text(), "fn main() { a:~: }")
+    });
+
+    // Undo should remove LSP edits first
+    editor_b.update(&mut cx_b, |editor, cx| {
+        assert_eq!(editor.text(cx), "fn main() { a:~: }");
+        editor.undo(&Undo, cx);
+        assert_eq!(editor.text(cx), "fn main() { a: }");
+    });
+    executor.run_until_parked();
+
+    buffer_a.read_with(cx_a, |buffer, _| {
+        assert_eq!(buffer.text(), "fn main() { a: }")
+    });
+
+    editor_b.update(&mut cx_b, |editor, cx| {
+        assert_eq!(editor.text(cx), "fn main() { a: }");
+        editor.undo(&Undo, cx);
+        assert_eq!(editor.text(cx), "fn main() { a }");
+    });
+    executor.run_until_parked();
+
+    buffer_a.read_with(cx_a, |buffer, _| {
+        assert_eq!(buffer.text(), "fn main() { a }")
+    });
+}
+
+#[gpui::test(iterations = 10)]
+async fn test_mutual_editor_inlay_hint_cache_update(
+    cx_a: &mut TestAppContext,
+    cx_b: &mut TestAppContext,
+) {
+    let mut server = TestServer::start(cx_a.executor()).await;
+    let executor = cx_a.executor();
+    let client_a = server.create_client(cx_a, "user_a").await;
+    let client_b = server.create_client(cx_b, "user_b").await;
+    server
+        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
+        .await;
+    let active_call_a = cx_a.read(ActiveCall::global);
+    let active_call_b = cx_b.read(ActiveCall::global);
+
+    cx_a.update(editor::init);
+    cx_b.update(editor::init);
+
+    cx_a.update(|cx| {
+        cx.update_global(|store: &mut SettingsStore, cx| {
+            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
+                settings.defaults.inlay_hints = Some(InlayHintSettings {
+                    enabled: true,
+                    show_type_hints: true,
+                    show_parameter_hints: false,
+                    show_other_hints: true,
+                })
+            });
+        });
+    });
+    cx_b.update(|cx| {
+        cx.update_global(|store: &mut SettingsStore, cx| {
+            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
+                settings.defaults.inlay_hints = Some(InlayHintSettings {
+                    enabled: true,
+                    show_type_hints: true,
+                    show_parameter_hints: false,
+                    show_other_hints: true,
+                })
+            });
+        });
+    });
+
+    let mut language = Language::new(
+        LanguageConfig {
+            name: "Rust".into(),
+            path_suffixes: vec!["rs".to_string()],
+            ..Default::default()
+        },
+        Some(tree_sitter_rust::language()),
+    );
+    let mut fake_language_servers = language
+        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+            capabilities: lsp::ServerCapabilities {
+                inlay_hint_provider: Some(lsp::OneOf::Left(true)),
+                ..Default::default()
+            },
+            ..Default::default()
+        }))
+        .await;
+    let language = Arc::new(language);
+    client_a.language_registry().add(Arc::clone(&language));
+    client_b.language_registry().add(language);
+
+    // Client A opens a project.
+    client_a
+        .fs()
+        .insert_tree(
+            "/a",
+            json!({
+                "main.rs": "fn main() { a } // and some long comment to ensure inlay hints are not trimmed out",
+                "other.rs": "// Test file",
+            }),
+        )
+        .await;
+    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
+    active_call_a
+        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
+        .await
+        .unwrap();
+    let project_id = active_call_a
+        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
+        .await
+        .unwrap();
+
+    // Client B joins the project
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
+    active_call_b
+        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
+        .await
+        .unwrap();
+
+    let (workspace_a, cx_a) = client_a.build_workspace(&project_a, cx_a);
+    executor.start_waiting();
+
+    // The host opens a rust file.
+    let _buffer_a = project_a
+        .update(cx_a, |project, cx| {
+            project.open_local_buffer("/a/main.rs", cx)
+        })
+        .await
+        .unwrap();
+    let fake_language_server = fake_language_servers.next().await.unwrap();
+    let editor_a = workspace_a
+        .update(cx_a, |workspace, cx| {
+            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
+        })
+        .await
+        .unwrap()
+        .downcast::<Editor>()
+        .unwrap();
+
+    // Set up the language server to return an additional inlay hint on each request.
+    let edits_made = Arc::new(AtomicUsize::new(0));
+    let closure_edits_made = Arc::clone(&edits_made);
+    fake_language_server
+        .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+            let task_edits_made = Arc::clone(&closure_edits_made);
+            async move {
+                assert_eq!(
+                    params.text_document.uri,
+                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
+                );
+                let edits_made = task_edits_made.load(atomic::Ordering::Acquire);
+                Ok(Some(vec![lsp::InlayHint {
+                    position: lsp::Position::new(0, edits_made as u32),
+                    label: lsp::InlayHintLabel::String(edits_made.to_string()),
+                    kind: None,
+                    text_edits: None,
+                    tooltip: None,
+                    padding_left: None,
+                    padding_right: None,
+                    data: None,
+                }]))
+            }
+        })
+        .next()
+        .await
+        .unwrap();
+
+    executor.run_until_parked();
+
+    let initial_edit = edits_made.load(atomic::Ordering::Acquire);
+    editor_a.update(cx_a, |editor, _| {
+        assert_eq!(
+            vec![initial_edit.to_string()],
+            extract_hint_labels(editor),
+            "Host should get its first hints when opens an editor"
+        );
+        let inlay_cache = editor.inlay_hint_cache();
+        assert_eq!(
+            inlay_cache.version(),
+            1,
+            "Host editor update the cache version after every cache/view change",
+        );
+    });
+    let (workspace_b, cx_b) = client_b.build_workspace(&project_b, cx_b);
+    let editor_b = workspace_b
+        .update(cx_b, |workspace, cx| {
+            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
+        })
+        .await
+        .unwrap()
+        .downcast::<Editor>()
+        .unwrap();
+
+    executor.run_until_parked();
+    editor_b.update(cx_b, |editor, _| {
+        assert_eq!(
+            vec![initial_edit.to_string()],
+            extract_hint_labels(editor),
+            "Client should get its first hints when opens an editor"
+        );
+        let inlay_cache = editor.inlay_hint_cache();
+        assert_eq!(
+            inlay_cache.version(),
+            1,
+            "Guest editor update the cache version after every cache/view change"
+        );
+    });
+
+    let after_client_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
+    editor_b.update(cx_b, |editor, cx| {
+        editor.change_selections(None, cx, |s| s.select_ranges([13..13].clone()));
+        editor.handle_input(":", cx);
+    });
+    cx_b.focus_view(&editor_b);
+
+    executor.run_until_parked();
+    editor_a.update(cx_a, |editor, _| {
+        assert_eq!(
+            vec![after_client_edit.to_string()],
+            extract_hint_labels(editor),
+        );
+        let inlay_cache = editor.inlay_hint_cache();
+        assert_eq!(inlay_cache.version(), 2);
+    });
+    editor_b.update(cx_b, |editor, _| {
+        assert_eq!(
+            vec![after_client_edit.to_string()],
+            extract_hint_labels(editor),
+        );
+        let inlay_cache = editor.inlay_hint_cache();
+        assert_eq!(inlay_cache.version(), 2);
+    });
+
+    let after_host_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
+    editor_a.update(cx_a, |editor, cx| {
+        editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
+        editor.handle_input("a change to increment both buffers' versions", cx);
+    });
+    cx_a.focus_view(&editor_a);
+
+    executor.run_until_parked();
+    editor_a.update(cx_a, |editor, _| {
+        assert_eq!(
+            vec![after_host_edit.to_string()],
+            extract_hint_labels(editor),
+        );
+        let inlay_cache = editor.inlay_hint_cache();
+        assert_eq!(inlay_cache.version(), 3);
+    });
+    editor_b.update(cx_b, |editor, _| {
+        assert_eq!(
+            vec![after_host_edit.to_string()],
+            extract_hint_labels(editor),
+        );
+        let inlay_cache = editor.inlay_hint_cache();
+        assert_eq!(inlay_cache.version(), 3);
+    });
+
+    let after_special_edit_for_refresh = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
+    fake_language_server
+        .request::<lsp::request::InlayHintRefreshRequest>(())
+        .await
+        .expect("inlay refresh request failed");
+
+    executor.run_until_parked();
+    editor_a.update(cx_a, |editor, _| {
+        assert_eq!(
+            vec![after_special_edit_for_refresh.to_string()],
+            extract_hint_labels(editor),
+            "Host should react to /refresh LSP request"
+        );
+        let inlay_cache = editor.inlay_hint_cache();
+        assert_eq!(
+            inlay_cache.version(),
+            4,
+            "Host should accepted all edits and bump its cache version every time"
+        );
+    });
+    editor_b.update(cx_b, |editor, _| {
+        assert_eq!(
+            vec![after_special_edit_for_refresh.to_string()],
+            extract_hint_labels(editor),
+            "Guest should get a /refresh LSP request propagated by host"
+        );
+        let inlay_cache = editor.inlay_hint_cache();
+        assert_eq!(
+            inlay_cache.version(),
+            4,
+            "Guest should accepted all edits and bump its cache version every time"
+        );
+    });
+}
+
+#[gpui::test(iterations = 10)]
+async fn test_inlay_hint_refresh_is_forwarded(
+    cx_a: &mut TestAppContext,
+    cx_b: &mut TestAppContext,
+) {
+    let mut server = TestServer::start(cx_a.executor()).await;
+    let executor = cx_a.executor();
+    let client_a = server.create_client(cx_a, "user_a").await;
+    let client_b = server.create_client(cx_b, "user_b").await;
+    server
+        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
+        .await;
+    let active_call_a = cx_a.read(ActiveCall::global);
+    let active_call_b = cx_b.read(ActiveCall::global);
+
+    cx_a.update(editor::init);
+    cx_b.update(editor::init);
+
+    cx_a.update(|cx| {
+        cx.update_global(|store: &mut SettingsStore, cx| {
+            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
+                settings.defaults.inlay_hints = Some(InlayHintSettings {
+                    enabled: false,
+                    show_type_hints: false,
+                    show_parameter_hints: false,
+                    show_other_hints: false,
+                })
+            });
+        });
+    });
+    cx_b.update(|cx| {
+        cx.update_global(|store: &mut SettingsStore, cx| {
+            store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
+                settings.defaults.inlay_hints = Some(InlayHintSettings {
+                    enabled: true,
+                    show_type_hints: true,
+                    show_parameter_hints: true,
+                    show_other_hints: true,
+                })
+            });
+        });
+    });
+
+    let mut language = Language::new(
+        LanguageConfig {
+            name: "Rust".into(),
+            path_suffixes: vec!["rs".to_string()],
+            ..Default::default()
+        },
+        Some(tree_sitter_rust::language()),
+    );
+    let mut fake_language_servers = language
+        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+            capabilities: lsp::ServerCapabilities {
+                inlay_hint_provider: Some(lsp::OneOf::Left(true)),
+                ..Default::default()
+            },
+            ..Default::default()
+        }))
+        .await;
+    let language = Arc::new(language);
+    client_a.language_registry().add(Arc::clone(&language));
+    client_b.language_registry().add(language);
+
+    client_a
+        .fs()
+        .insert_tree(
+            "/a",
+            json!({
+                "main.rs": "fn main() { a } // and some long comment to ensure inlay hints are not trimmed out",
+                "other.rs": "// Test file",
+            }),
+        )
+        .await;
+    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
+    active_call_a
+        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
+        .await
+        .unwrap();
+    let project_id = active_call_a
+        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
+        .await
+        .unwrap();
+
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
+    active_call_b
+        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
+        .await
+        .unwrap();
+
+    let (workspace_a, cx_a) = client_a.build_workspace(&project_a, cx_a);
+    let (workspace_b, cx_b) = client_b.build_workspace(&project_b, cx_b);
+
+    cx_a.background_executor.start_waiting();
+
+    let editor_a = workspace_a
+        .update(cx_a, |workspace, cx| {
+            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
+        })
+        .await
+        .unwrap()
+        .downcast::<Editor>()
+        .unwrap();
+
+    let editor_b = workspace_b
+        .update(cx_b, |workspace, cx| {
+            workspace.open_path((worktree_id, "main.rs"), None, true, cx)
+        })
+        .await
+        .unwrap()
+        .downcast::<Editor>()
+        .unwrap();
+
+    let other_hints = Arc::new(AtomicBool::new(false));
+    let fake_language_server = fake_language_servers.next().await.unwrap();
+    let closure_other_hints = Arc::clone(&other_hints);
+    fake_language_server
+        .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+            let task_other_hints = Arc::clone(&closure_other_hints);
+            async move {
+                assert_eq!(
+                    params.text_document.uri,
+                    lsp::Url::from_file_path("/a/main.rs").unwrap(),
+                );
+                let other_hints = task_other_hints.load(atomic::Ordering::Acquire);
+                let character = if other_hints { 0 } else { 2 };
+                let label = if other_hints {
+                    "other hint"
+                } else {
+                    "initial hint"
+                };
+                Ok(Some(vec![lsp::InlayHint {
+                    position: lsp::Position::new(0, character),
+                    label: lsp::InlayHintLabel::String(label.to_string()),
+                    kind: None,
+                    text_edits: None,
+                    tooltip: None,
+                    padding_left: None,
+                    padding_right: None,
+                    data: None,
+                }]))
+            }
+        })
+        .next()
+        .await
+        .unwrap();
+    executor.finish_waiting();
+
+    executor.run_until_parked();
+    editor_a.update(cx_a, |editor, _| {
+        assert!(
+            extract_hint_labels(editor).is_empty(),
+            "Host should get no hints due to them turned off"
+        );
+        let inlay_cache = editor.inlay_hint_cache();
+        assert_eq!(
+            inlay_cache.version(),
+            0,
+            "Turned off hints should not generate version updates"
+        );
+    });
+
+    executor.run_until_parked();
+    editor_b.update(cx_b, |editor, _| {
+        assert_eq!(
+            vec!["initial hint".to_string()],
+            extract_hint_labels(editor),
+            "Client should get its first hints when opens an editor"
+        );
+        let inlay_cache = editor.inlay_hint_cache();
+        assert_eq!(
+            inlay_cache.version(),
+            1,
+            "Should update cache verison after first hints"
+        );
+    });
+
+    other_hints.fetch_or(true, atomic::Ordering::Release);
+    fake_language_server
+        .request::<lsp::request::InlayHintRefreshRequest>(())
+        .await
+        .expect("inlay refresh request failed");
+    executor.run_until_parked();
+    editor_a.update(cx_a, |editor, _| {
+        assert!(
+            extract_hint_labels(editor).is_empty(),
+            "Host should get nop hints due to them turned off, even after the /refresh"
+        );
+        let inlay_cache = editor.inlay_hint_cache();
+        assert_eq!(
+            inlay_cache.version(),
+            0,
+            "Turned off hints should not generate version updates, again"
+        );
+    });
+
+    executor.run_until_parked();
+    editor_b.update(cx_b, |editor, _| {
+        assert_eq!(
+            vec!["other hint".to_string()],
+            extract_hint_labels(editor),
+            "Guest should get a /refresh LSP request propagated by host despite host hints are off"
+        );
+        let inlay_cache = editor.inlay_hint_cache();
+        assert_eq!(
+            inlay_cache.version(),
+            2,
+            "Guest should accepted all edits and bump its cache version every time"
+        );
+    });
+}
+
+fn extract_hint_labels(editor: &Editor) -> Vec<String> {
+    let mut labels = Vec::new();
+    for hint in editor.inlay_hint_cache().hints() {
+        match hint.label {
+            project::InlayHintLabel::String(s) => labels.push(s),
+            _ => unreachable!(),
+        }
+    }
+    labels
+}