diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 4008a941dd..d6d449fd47 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -15,12 +15,14 @@ use gpui::{executor::Deterministic, test::EmptyView, AppContext, ModelHandle, Te use indoc::indoc; use language::{ language_settings::{AllLanguageSettings, Formatter, InlayHintSettings}, - tree_sitter_rust, Anchor, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language, - LanguageConfig, LineEnding, OffsetRangeExt, Point, Rope, + tree_sitter_rust, Anchor, BundledFormatter, Diagnostic, DiagnosticEntry, FakeLspAdapter, + Language, LanguageConfig, LineEnding, OffsetRangeExt, Point, Rope, }; use live_kit_client::MacOSDisplay; use lsp::LanguageServerId; -use project::{search::SearchQuery, DiagnosticSummary, HoverBlockKind, Project, ProjectPath}; +use project::{ + search::SearchQuery, DiagnosticSummary, FormatTrigger, HoverBlockKind, Project, ProjectPath, +}; use rand::prelude::*; use serde_json::json; use settings::SettingsStore; @@ -4407,8 +4409,6 @@ async fn test_formatting_buffer( cx_a: &mut TestAppContext, cx_b: &mut TestAppContext, ) { - use project::FormatTrigger; - let mut server = TestServer::start(&deterministic).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; @@ -4511,6 +4511,134 @@ async fn test_formatting_buffer( ); } +#[gpui::test(iterations = 10)] +async fn test_prettier_formatting_buffer( + deterministic: Arc, + cx_a: &mut TestAppContext, + cx_b: &mut TestAppContext, +) { + let mut server = TestServer::start(&deterministic).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 test_plugin = "test_plugin"; + let mut fake_language_servers = language + .set_fake_lsp_adapter(Arc::new(FakeLspAdapter { + enabled_formatters: vec![BundledFormatter::Prettier { + parser_name: Some("test_parser"), + plugin_names: vec![test_plugin], + }], + ..Default::default() + })) + .await; + let language = Arc::new(language); + client_a.language_registry().add(Arc::clone(&language)); + + // Here we insert a fake tree with a directory that exists on disk. This is needed + // because later we'll invoke a command, which requires passing a working directory + // that points to a valid location on disk. + let directory = env::current_dir().unwrap(); + let buffer_text = "let one = \"two\""; + client_a + .fs() + .insert_tree(&directory, json!({ "a.rs": buffer_text })) + .await; + let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await; + let prettier_format_suffix = project_a.update(cx_a, |project, _| { + let suffix = project.enable_test_prettier(&[test_plugin]); + project.languages().add(language); + suffix + }); + let buffer_a = cx_a + .background() + .spawn(project_a.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), 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; + let buffer_b = cx_b + .background() + .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))) + .await + .unwrap(); + + cx_a.update(|cx| { + cx.update_global(|store: &mut SettingsStore, cx| { + store.update_user_settings::(cx, |file| { + file.defaults.formatter = Some(Formatter::Auto); + }); + }); + }); + cx_b.update(|cx| { + cx.update_global(|store: &mut SettingsStore, cx| { + store.update_user_settings::(cx, |file| { + file.defaults.formatter = Some(Formatter::LanguageServer); + }); + }); + }); + let fake_language_server = fake_language_servers.next().await.unwrap(); + fake_language_server.handle_request::(|_, _| async move { + panic!( + "Unexpected: prettier should be preferred since it's enabled and language supports it" + ) + }); + + project_b + .update(cx_b, |project, cx| { + project.format( + HashSet::from_iter([buffer_b.clone()]), + true, + FormatTrigger::Save, + cx, + ) + }) + .await + .unwrap(); + cx_a.foreground().run_until_parked(); + cx_b.foreground().run_until_parked(); + assert_eq!( + buffer_b.read_with(cx_b, |buffer, _| buffer.text()), + buffer_text.to_string() + "\n" + prettier_format_suffix, + "Prettier formatting was not applied to client buffer after client's request" + ); + + project_a + .update(cx_a, |project, cx| { + project.format( + HashSet::from_iter([buffer_a.clone()]), + true, + FormatTrigger::Manual, + cx, + ) + }) + .await + .unwrap(); + cx_a.foreground().run_until_parked(); + cx_b.foreground().run_until_parked(); + assert_eq!( + buffer_b.read_with(cx_b, |buffer, _| buffer.text()), + buffer_text.to_string() + "\n" + prettier_format_suffix + "\n" + prettier_format_suffix, + "Prettier formatting was not applied to client buffer after host's request" + ); +} + #[gpui::test(iterations = 10)] async fn test_definition( deterministic: Arc, diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 4cb0c009bb..c68f72d16f 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -5076,7 +5076,9 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) { - init_test(cx, |_| {}); + init_test(cx, |settings| { + settings.defaults.formatter = Some(language_settings::Formatter::LanguageServer) + }); let mut language = Language::new( LanguageConfig { @@ -5092,6 +5094,12 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) { document_formatting_provider: Some(lsp::OneOf::Left(true)), ..Default::default() }, + // Enable Prettier formatting for the same buffer, and ensure + // LSP is called instead of Prettier. + enabled_formatters: vec![BundledFormatter::Prettier { + parser_name: Some("test_parser"), + plugin_names: Vec::new(), + }], ..Default::default() })) .await; @@ -5100,7 +5108,10 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) { fs.insert_file("/file.rs", Default::default()).await; let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; - project.update(cx, |project, _| project.languages().add(Arc::new(language))); + project.update(cx, |project, _| { + project.enable_test_prettier(&[]); + project.languages().add(Arc::new(language)); + }); let buffer = project .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx)) .await @@ -5218,7 +5229,9 @@ async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) { - init_test(cx, |_| {}); + init_test(cx, |settings| { + settings.defaults.formatter = Some(language_settings::Formatter::Auto) + }); let mut cx = EditorLspTestContext::new_rust( lsp::ServerCapabilities { @@ -7864,12 +7877,24 @@ async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) { editor.perform_format(project.clone(), FormatTrigger::Manual, cx) }); format.await.unwrap(); - assert_eq!( editor.read_with(cx, |editor, cx| editor.text(cx)), buffer_text.to_string() + prettier_format_suffix, "Test prettier formatting was not applied to the original buffer text", ); + + update_test_language_settings(cx, |settings| { + settings.defaults.formatter = Some(language_settings::Formatter::Auto) + }); + let format = editor.update(cx, |editor, cx| { + editor.perform_format(project.clone(), FormatTrigger::Manual, cx) + }); + format.await.unwrap(); + assert_eq!( + editor.read_with(cx, |editor, cx| editor.text(cx)), + buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix, + "Autoformatting (via test prettier) was not applied to the original buffer text", + ); } fn empty_range(row: usize, column: usize) -> Range {