Files
oak-gpui/crates/collab/src/tests/editor_tests.rs
T
2024-01-03 12:29:16 -08:00

1890 lines
70 KiB
Rust

//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
// }