diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index dac7adfdf8..b9428fc1f5 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -6438,13 +6438,12 @@ pub fn styled_runs_for_code_label<'a>( #[cfg(test)] mod tests { - use super::*; use gpui::{ geometry::rect::RectF, platform::{WindowBounds, WindowOptions}, }; - use language::{LanguageConfig, LanguageServerConfig}; + use language::{FakeLspAdapter, LanguageConfig}; use lsp::FakeLanguageServer; use project::FakeFs; use smol::stream::StreamExt; @@ -8880,26 +8879,27 @@ mod tests { cx.foreground().forbid_parking(); cx.update(populate_settings); - let (mut language_server_config, mut fake_servers) = LanguageServerConfig::fake(); - language_server_config.set_fake_capabilities(lsp::ServerCapabilities { - document_formatting_provider: Some(lsp::OneOf::Left(true)), - ..Default::default() - }); - let language = Arc::new(Language::new( + let mut language = Language::new( LanguageConfig { name: "Rust".into(), path_suffixes: vec!["rs".to_string()], - language_server: language_server_config, ..Default::default() }, Some(tree_sitter_rust::language()), - )); + ); + let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter { + capabilities: lsp::ServerCapabilities { + document_formatting_provider: Some(lsp::OneOf::Left(true)), + ..Default::default() + }, + ..Default::default() + }); let fs = FakeFs::new(cx.background().clone()); fs.insert_file("/file.rs", Default::default()).await; let project = Project::test(fs, cx); - project.update(cx, |project, _| project.languages().add(language)); + project.update(cx, |project, _| project.languages().add(Arc::new(language))); let worktree_id = project .update(cx, |project, cx| { @@ -8913,6 +8913,8 @@ mod tests { .update(cx, |project, cx| project.open_buffer((worktree_id, ""), cx)) .await .unwrap(); + + cx.foreground().start_waiting(); let mut fake_server = fake_servers.next().await.unwrap(); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); @@ -8934,6 +8936,7 @@ mod tests { }) .next() .await; + cx.foreground().start_waiting(); save.await.unwrap(); assert_eq!( editor.read_with(cx, |editor, cx| editor.text(cx)), @@ -8955,6 +8958,7 @@ mod tests { }); let save = cx.update(|cx| editor.save(project.clone(), cx)); cx.foreground().advance_clock(items::FORMAT_TIMEOUT); + cx.foreground().start_waiting(); save.await.unwrap(); assert_eq!( editor.read_with(cx, |editor, cx| editor.text(cx)), @@ -8967,23 +8971,24 @@ mod tests { async fn test_completion(cx: &mut gpui::TestAppContext) { cx.update(populate_settings); - let (mut language_server_config, mut fake_servers) = LanguageServerConfig::fake(); - language_server_config.set_fake_capabilities(lsp::ServerCapabilities { - completion_provider: Some(lsp::CompletionOptions { - trigger_characters: Some(vec![".".to_string(), ":".to_string()]), - ..Default::default() - }), - ..Default::default() - }); - let language = Arc::new(Language::new( + let mut language = Language::new( LanguageConfig { name: "Rust".into(), path_suffixes: vec!["rs".to_string()], - language_server: language_server_config, ..Default::default() }, Some(tree_sitter_rust::language()), - )); + ); + let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter { + capabilities: lsp::ServerCapabilities { + completion_provider: Some(lsp::CompletionOptions { + trigger_characters: Some(vec![".".to_string(), ":".to_string()]), + ..Default::default() + }), + ..Default::default() + }, + ..Default::default() + }); let text = " one @@ -8996,7 +9001,7 @@ mod tests { fs.insert_file("/file.rs", text).await; let project = Project::test(fs, cx); - project.update(cx, |project, _| project.languages().add(language)); + project.update(cx, |project, _| project.languages().add(Arc::new(language))); let worktree_id = project .update(cx, |project, cx| { diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 8d573574fd..a98f1a9845 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -138,16 +138,13 @@ impl Default for LanguageConfig { pub struct LanguageServerConfig { pub disk_based_diagnostic_sources: HashSet, pub disk_based_diagnostics_progress_token: Option, - #[cfg(any(test, feature = "test-support"))] - #[serde(skip)] - fake_config: Option, } #[cfg(any(test, feature = "test-support"))] -struct FakeLanguageServerConfig { - servers_tx: mpsc::UnboundedSender, - capabilities: lsp::ServerCapabilities, - initializer: Option>, +pub struct FakeLspAdapter { + pub name: &'static str, + pub capabilities: lsp::ServerCapabilities, + pub initializer: Option>, } #[derive(Clone, Debug, Deserialize)] @@ -162,6 +159,12 @@ pub struct Language { pub(crate) config: LanguageConfig, pub(crate) grammar: Option>, pub(crate) adapter: Option>, + + #[cfg(any(test, feature = "test-support"))] + fake_adapter: Option<( + mpsc::UnboundedSender, + Arc, + )>, } pub struct Grammar { @@ -262,26 +265,22 @@ impl LanguageRegistry { cx: &mut MutableAppContext, ) -> Option>> { #[cfg(any(test, feature = "test-support"))] - if language.config.language_server.fake_config.is_some() { + if language.fake_adapter.is_some() { let language = language.clone(); return Some(cx.spawn(|mut cx| async move { - let fake_config = language - .config - .language_server - .fake_config - .as_ref() - .unwrap(); + let (servers_tx, fake_adapter) = language.fake_adapter.as_ref().unwrap(); let (server, mut fake_server) = cx.update(|cx| { lsp::LanguageServer::fake_with_capabilities( - fake_config.capabilities.clone(), + fake_adapter.capabilities.clone(), cx, ) }); - if let Some(initializer) = &fake_config.initializer { + + if let Some(initializer) = &fake_adapter.initializer { initializer(&mut fake_server); } - let servers_tx = fake_config.servers_tx.clone(); + let servers_tx = servers_tx.clone(); cx.background() .spawn(async move { fake_server @@ -426,6 +425,9 @@ impl Language { }) }), adapter: None, + + #[cfg(any(test, feature = "test-support"))] + fake_adapter: None, } } @@ -478,6 +480,18 @@ impl Language { self } + #[cfg(any(test, feature = "test-support"))] + pub fn set_fake_lsp_adapter( + &mut self, + fake_lsp_adapter: FakeLspAdapter, + ) -> mpsc::UnboundedReceiver { + let (servers_tx, servers_rx) = mpsc::unbounded(); + let adapter = Arc::new(fake_lsp_adapter); + self.fake_adapter = Some((servers_tx, adapter.clone())); + self.adapter = Some(adapter); + servers_rx + } + pub fn name(&self) -> Arc { self.config.name.clone() } @@ -601,33 +615,42 @@ impl CodeLabel { } #[cfg(any(test, feature = "test-support"))] -impl LanguageServerConfig { - pub fn fake() -> (Self, mpsc::UnboundedReceiver) { - let (servers_tx, servers_rx) = mpsc::unbounded(); - ( - Self { - fake_config: Some(FakeLanguageServerConfig { - servers_tx, - capabilities: lsp::LanguageServer::full_capabilities(), - initializer: None, - }), - disk_based_diagnostics_progress_token: Some("fakeServer/check".to_string()), - ..Default::default() - }, - servers_rx, - ) +impl Default for FakeLspAdapter { + fn default() -> Self { + Self { + name: "the-fake-language-server", + capabilities: lsp::LanguageServer::full_capabilities(), + initializer: None, + } + } +} + +impl LspAdapter for FakeLspAdapter { + fn name(&self) -> LanguageServerName { + LanguageServerName(self.name.into()) } - pub fn set_fake_capabilities(&mut self, capabilities: lsp::ServerCapabilities) { - self.fake_config.as_mut().unwrap().capabilities = capabilities; + fn fetch_latest_server_version( + &self, + _: Arc, + ) -> BoxFuture<'static, Result>> { + unreachable!(); } - pub fn set_fake_initializer( - &mut self, - initializer: impl 'static + Send + Sync + Fn(&mut lsp::FakeLanguageServer), - ) { - self.fake_config.as_mut().unwrap().initializer = Some(Box::new(initializer)); + fn fetch_server_binary( + &self, + _: Box, + _: Arc, + _: PathBuf, + ) -> BoxFuture<'static, Result> { + unreachable!(); } + + fn cached_server_binary(&self, _: PathBuf) -> BoxFuture<'static, Option> { + unreachable!(); + } + + fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {} } impl ToLspPosition for PointUtf16 { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 6f550d1020..1dd0f907f7 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4606,8 +4606,8 @@ mod tests { use futures::StreamExt; use gpui::test::subscribe; use language::{ - tree_sitter_rust, Diagnostic, LanguageConfig, LanguageServerConfig, OffsetRangeExt, Point, - ToPoint, + tree_sitter_rust, Diagnostic, FakeLspAdapter, LanguageConfig, LanguageServerConfig, + OffsetRangeExt, Point, ToPoint, }; use lsp::Url; use serde_json::json; @@ -4683,41 +4683,44 @@ mod tests { async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { cx.foreground().forbid_parking(); - let (mut rust_lsp_config, mut fake_rust_servers) = LanguageServerConfig::fake(); - let (mut json_lsp_config, mut fake_json_servers) = LanguageServerConfig::fake(); - rust_lsp_config.set_fake_capabilities(lsp::ServerCapabilities { - completion_provider: Some(lsp::CompletionOptions { - trigger_characters: Some(vec![".".to_string(), "::".to_string()]), - ..Default::default() - }), - ..Default::default() - }); - json_lsp_config.set_fake_capabilities(lsp::ServerCapabilities { - completion_provider: Some(lsp::CompletionOptions { - trigger_characters: Some(vec![":".to_string()]), - ..Default::default() - }), - ..Default::default() - }); - - let rust_language = Arc::new(Language::new( + let mut rust_language = Language::new( LanguageConfig { name: "Rust".into(), path_suffixes: vec!["rs".to_string()], - language_server: rust_lsp_config, ..Default::default() }, Some(tree_sitter_rust::language()), - )); - let json_language = Arc::new(Language::new( + ); + let mut json_language = Language::new( LanguageConfig { name: "JSON".into(), path_suffixes: vec!["json".to_string()], - language_server: json_lsp_config, ..Default::default() }, None, - )); + ); + let mut fake_rust_servers = rust_language.set_fake_lsp_adapter(FakeLspAdapter { + name: "the-rust-language-server", + capabilities: lsp::ServerCapabilities { + completion_provider: Some(lsp::CompletionOptions { + trigger_characters: Some(vec![".".to_string(), "::".to_string()]), + ..Default::default() + }), + ..Default::default() + }, + ..Default::default() + }); + let mut fake_json_servers = json_language.set_fake_lsp_adapter(FakeLspAdapter { + name: "the-json-language-server", + capabilities: lsp::ServerCapabilities { + completion_provider: Some(lsp::CompletionOptions { + trigger_characters: Some(vec![":".to_string()]), + ..Default::default() + }), + ..Default::default() + }, + ..Default::default() + }); let fs = FakeFs::new(cx.background()); fs.insert_tree( @@ -4733,8 +4736,8 @@ mod tests { let project = Project::test(fs, cx); project.update(cx, |project, _| { - project.languages.add(rust_language); - project.languages.add(json_language); + project.languages.add(Arc::new(rust_language)); + project.languages.add(Arc::new(json_language)); }); let worktree_id = project @@ -4903,21 +4906,20 @@ mod tests { async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) { cx.foreground().forbid_parking(); - let (language_server_config, mut fake_servers) = LanguageServerConfig::fake(); - let progress_token = language_server_config - .disk_based_diagnostics_progress_token - .clone() - .unwrap(); - - let language = Arc::new(Language::new( + let progress_token = "the-progress-token".to_string(); + let mut language = Language::new( LanguageConfig { name: "Rust".into(), path_suffixes: vec!["rs".to_string()], - language_server: language_server_config, + language_server: LanguageServerConfig { + disk_based_diagnostics_progress_token: Some(progress_token.clone()), + ..Default::default() + }, ..Default::default() }, Some(tree_sitter_rust::language()), - )); + ); + let mut fake_servers = language.set_fake_lsp_adapter(Default::default()); let fs = FakeFs::new(cx.background()); fs.insert_tree( @@ -4930,7 +4932,7 @@ mod tests { .await; let project = Project::test(fs, cx); - project.update(cx, |project, _| project.languages.add(language)); + project.update(cx, |project, _| project.languages.add(Arc::new(language))); let (tree, _) = project .update(cx, |project, cx| { @@ -5022,19 +5024,20 @@ mod tests { async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) { cx.foreground().forbid_parking(); - let (mut lsp_config, mut fake_servers) = LanguageServerConfig::fake(); - lsp_config - .disk_based_diagnostic_sources - .insert("disk".to_string()); - let language = Arc::new(Language::new( + // let (mut lsp_config, mut fake_servers) = LanguageServerConfig::fake(); + let mut language = Language::new( LanguageConfig { name: "Rust".into(), path_suffixes: vec!["rs".to_string()], - language_server: lsp_config, + language_server: LanguageServerConfig { + disk_based_diagnostic_sources: ["disk".to_string()].into_iter().collect(), + ..Default::default() + }, ..Default::default() }, Some(tree_sitter_rust::language()), - )); + ); + let mut fake_servers = language.set_fake_lsp_adapter(Default::default()); let text = " fn a() { A } @@ -5047,7 +5050,7 @@ mod tests { fs.insert_tree("/dir", json!({ "a.rs": text })).await; let project = Project::test(fs, cx); - project.update(cx, |project, _| project.languages.add(language)); + project.update(cx, |project, _| project.languages.add(Arc::new(language))); let worktree_id = project .update(cx, |project, cx| { @@ -5396,16 +5399,15 @@ mod tests { async fn test_edits_from_lsp_with_past_version(cx: &mut gpui::TestAppContext) { cx.foreground().forbid_parking(); - let (lsp_config, mut fake_servers) = LanguageServerConfig::fake(); - let language = Arc::new(Language::new( + let mut language = Language::new( LanguageConfig { name: "Rust".into(), path_suffixes: vec!["rs".to_string()], - language_server: lsp_config, ..Default::default() }, Some(tree_sitter_rust::language()), - )); + ); + let mut fake_servers = language.set_fake_lsp_adapter(Default::default()); let text = " fn a() { @@ -5430,7 +5432,7 @@ mod tests { .await; let project = Project::test(fs, cx); - project.update(cx, |project, _| project.languages.add(language)); + project.update(cx, |project, _| project.languages.add(Arc::new(language))); let worktree_id = project .update(cx, |project, cx| { @@ -5746,16 +5748,15 @@ mod tests { #[gpui::test] async fn test_definition(cx: &mut gpui::TestAppContext) { - let (language_server_config, mut fake_servers) = LanguageServerConfig::fake(); - let language = Arc::new(Language::new( + let mut language = Language::new( LanguageConfig { name: "Rust".into(), path_suffixes: vec!["rs".to_string()], - language_server: language_server_config, ..Default::default() }, Some(tree_sitter_rust::language()), - )); + ); + let mut fake_servers = language.set_fake_lsp_adapter(Default::default()); let fs = FakeFs::new(cx.background()); fs.insert_tree( @@ -5768,9 +5769,7 @@ mod tests { .await; let project = Project::test(fs, cx); - project.update(cx, |project, _| { - Arc::get_mut(&mut project.languages).unwrap().add(language); - }); + project.update(cx, |project, _| project.languages.add(Arc::new(language))); let (tree, _) = project .update(cx, |project, cx| { @@ -6682,16 +6681,15 @@ mod tests { async fn test_rename(cx: &mut gpui::TestAppContext) { cx.foreground().forbid_parking(); - let (language_server_config, mut fake_servers) = LanguageServerConfig::fake(); - let language = Arc::new(Language::new( + let mut language = Language::new( LanguageConfig { name: "Rust".into(), path_suffixes: vec!["rs".to_string()], - language_server: language_server_config, ..Default::default() }, Some(tree_sitter_rust::language()), - )); + ); + let mut fake_servers = language.set_fake_lsp_adapter(Default::default()); let fs = FakeFs::new(cx.background()); fs.insert_tree( @@ -6704,9 +6702,7 @@ mod tests { .await; let project = Project::test(fs.clone(), cx); - project.update(cx, |project, _| { - Arc::get_mut(&mut project.languages).unwrap().add(language); - }); + project.update(cx, |project, _| project.languages.add(Arc::new(language))); let (tree, _) = project .update(cx, |project, cx| { diff --git a/crates/server/src/rpc.rs b/crates/server/src/rpc.rs index b329839de2..0f8720c673 100644 --- a/crates/server/src/rpc.rs +++ b/crates/server/src/rpc.rs @@ -1088,10 +1088,10 @@ mod tests { }; use gpui::{executor, geometry::vector::vec2f, ModelHandle, TestAppContext, ViewHandle}; use language::{ - tree_sitter_rust, Diagnostic, DiagnosticEntry, Language, LanguageConfig, LanguageRegistry, - LanguageServerConfig, OffsetRangeExt, Point, ToLspPosition, + tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language, LanguageConfig, + LanguageRegistry, OffsetRangeExt, Point, ToLspPosition, }; - use lsp; + use lsp::{self, FakeLanguageServer}; use parking_lot::Mutex; use postage::barrier; use project::{ @@ -2039,22 +2039,20 @@ mod tests { cx_b: &mut TestAppContext, ) { cx_a.foreground().forbid_parking(); - let mut lang_registry = Arc::new(LanguageRegistry::test()); + let lang_registry = Arc::new(LanguageRegistry::test()); let fs = FakeFs::new(cx_a.background()); // Set up a fake language server. - let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake(); - Arc::get_mut(&mut lang_registry) - .unwrap() - .add(Arc::new(Language::new( - LanguageConfig { - name: "Rust".into(), - path_suffixes: vec!["rs".to_string()], - language_server: language_server_config, - ..Default::default() - }, - Some(tree_sitter_rust::language()), - ))); + 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()); + lang_registry.add(Arc::new(language)); // Connect to a server as 2 clients. let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; @@ -2261,29 +2259,29 @@ mod tests { cx_b: &mut TestAppContext, ) { cx_a.foreground().forbid_parking(); - let mut lang_registry = Arc::new(LanguageRegistry::test()); + let lang_registry = Arc::new(LanguageRegistry::test()); let fs = FakeFs::new(cx_a.background()); // Set up a fake language server. - let (mut language_server_config, mut fake_language_servers) = LanguageServerConfig::fake(); - language_server_config.set_fake_capabilities(lsp::ServerCapabilities { - completion_provider: Some(lsp::CompletionOptions { - trigger_characters: Some(vec![".".to_string()]), + 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(FakeLspAdapter { + capabilities: lsp::ServerCapabilities { + completion_provider: Some(lsp::CompletionOptions { + trigger_characters: Some(vec![".".to_string()]), + ..Default::default() + }), + ..Default::default() + }, ..Default::default() }); - Arc::get_mut(&mut lang_registry) - .unwrap() - .add(Arc::new(Language::new( - LanguageConfig { - name: "Rust".into(), - path_suffixes: vec!["rs".to_string()], - language_server: language_server_config, - ..Default::default() - }, - Some(tree_sitter_rust::language()), - ))); + lang_registry.add(Arc::new(language)); // Connect to a server as 2 clients. let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; @@ -2463,22 +2461,20 @@ mod tests { #[gpui::test(iterations = 10)] async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { cx_a.foreground().forbid_parking(); - let mut lang_registry = Arc::new(LanguageRegistry::test()); + let lang_registry = Arc::new(LanguageRegistry::test()); let fs = FakeFs::new(cx_a.background()); // Set up a fake language server. - let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake(); - Arc::get_mut(&mut lang_registry) - .unwrap() - .add(Arc::new(Language::new( - LanguageConfig { - name: "Rust".into(), - path_suffixes: vec!["rs".to_string()], - language_server: language_server_config, - ..Default::default() - }, - Some(tree_sitter_rust::language()), - ))); + 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()); + lang_registry.add(Arc::new(language)); // Connect to a server as 2 clients. let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; @@ -2563,7 +2559,7 @@ mod tests { #[gpui::test(iterations = 10)] async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { cx_a.foreground().forbid_parking(); - let mut lang_registry = Arc::new(LanguageRegistry::test()); + let lang_registry = Arc::new(LanguageRegistry::test()); let fs = FakeFs::new(cx_a.background()); fs.insert_tree( "/root-1", @@ -2582,18 +2578,16 @@ mod tests { .await; // Set up a fake language server. - let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake(); - Arc::get_mut(&mut lang_registry) - .unwrap() - .add(Arc::new(Language::new( - LanguageConfig { - name: "Rust".into(), - path_suffixes: vec!["rs".to_string()], - language_server: language_server_config, - ..Default::default() - }, - Some(tree_sitter_rust::language()), - ))); + 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()); + lang_registry.add(Arc::new(language)); // Connect to a server as 2 clients. let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; @@ -2705,7 +2699,7 @@ mod tests { #[gpui::test(iterations = 10)] async fn test_references(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { cx_a.foreground().forbid_parking(); - let mut lang_registry = Arc::new(LanguageRegistry::test()); + let lang_registry = Arc::new(LanguageRegistry::test()); let fs = FakeFs::new(cx_a.background()); fs.insert_tree( "/root-1", @@ -2725,18 +2719,16 @@ mod tests { .await; // Set up a fake language server. - let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake(); - Arc::get_mut(&mut lang_registry) - .unwrap() - .add(Arc::new(Language::new( - LanguageConfig { - name: "Rust".into(), - path_suffixes: vec!["rs".to_string()], - language_server: language_server_config, - ..Default::default() - }, - Some(tree_sitter_rust::language()), - ))); + 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()); + lang_registry.add(Arc::new(language)); // Connect to a server as 2 clients. let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; @@ -2967,16 +2959,16 @@ mod tests { .await; // Set up a fake language server. - let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake(); - lang_registry.add(Arc::new(Language::new( + let mut language = Language::new( LanguageConfig { name: "Rust".into(), path_suffixes: vec!["rs".to_string()], - language_server: language_server_config, ..Default::default() }, Some(tree_sitter_rust::language()), - ))); + ); + let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()); + lang_registry.add(Arc::new(language)); // Connect to a server as 2 clients. let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; @@ -3092,7 +3084,7 @@ mod tests { #[gpui::test(iterations = 10)] async fn test_project_symbols(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { cx_a.foreground().forbid_parking(); - let mut lang_registry = Arc::new(LanguageRegistry::test()); + let lang_registry = Arc::new(LanguageRegistry::test()); let fs = FakeFs::new(cx_a.background()); fs.insert_tree( "/code", @@ -3112,18 +3104,16 @@ mod tests { .await; // Set up a fake language server. - let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake(); - Arc::get_mut(&mut lang_registry) - .unwrap() - .add(Arc::new(Language::new( - LanguageConfig { - name: "Rust".into(), - path_suffixes: vec!["rs".to_string()], - language_server: language_server_config, - ..Default::default() - }, - Some(tree_sitter_rust::language()), - ))); + 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()); + lang_registry.add(Arc::new(language)); // Connect to a server as 2 clients. let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; @@ -3231,7 +3221,7 @@ mod tests { mut rng: StdRng, ) { cx_a.foreground().forbid_parking(); - let mut lang_registry = Arc::new(LanguageRegistry::test()); + let lang_registry = Arc::new(LanguageRegistry::test()); let fs = FakeFs::new(cx_a.background()); fs.insert_tree( "/root", @@ -3244,19 +3234,16 @@ mod tests { .await; // Set up a fake language server. - let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake(); - - Arc::get_mut(&mut lang_registry) - .unwrap() - .add(Arc::new(Language::new( - LanguageConfig { - name: "Rust".into(), - path_suffixes: vec!["rs".to_string()], - language_server: language_server_config, - ..Default::default() - }, - Some(tree_sitter_rust::language()), - ))); + 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()); + lang_registry.add(Arc::new(language)); // Connect to a server as 2 clients. let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; @@ -3337,23 +3324,21 @@ mod tests { cx_b: &mut TestAppContext, ) { cx_a.foreground().forbid_parking(); - let mut lang_registry = Arc::new(LanguageRegistry::test()); + let lang_registry = Arc::new(LanguageRegistry::test()); let fs = FakeFs::new(cx_a.background()); cx_b.update(|cx| editor::init(cx)); // Set up a fake language server. - let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake(); - Arc::get_mut(&mut lang_registry) - .unwrap() - .add(Arc::new(Language::new( - LanguageConfig { - name: "Rust".into(), - path_suffixes: vec!["rs".to_string()], - language_server: language_server_config, - ..Default::default() - }, - Some(tree_sitter_rust::language()), - ))); + 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()); + lang_registry.add(Arc::new(language)); // Connect to a server as 2 clients. let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; @@ -3573,23 +3558,21 @@ mod tests { #[gpui::test(iterations = 10)] async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { cx_a.foreground().forbid_parking(); - let mut lang_registry = Arc::new(LanguageRegistry::test()); + let lang_registry = Arc::new(LanguageRegistry::test()); let fs = FakeFs::new(cx_a.background()); cx_b.update(|cx| editor::init(cx)); // Set up a fake language server. - let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake(); - Arc::get_mut(&mut lang_registry) - .unwrap() - .add(Arc::new(Language::new( - LanguageConfig { - name: "Rust".into(), - path_suffixes: vec!["rs".to_string()], - language_server: language_server_config, - ..Default::default() - }, - Some(tree_sitter_rust::language()), - ))); + 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()); + lang_registry.add(Arc::new(language)); // Connect to a server as 2 clients. let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; @@ -4838,7 +4821,7 @@ mod tests { let rng = Arc::new(Mutex::new(rng)); let guest_lang_registry = Arc::new(LanguageRegistry::test()); - let (language_server_config, _fake_language_servers) = LanguageServerConfig::fake(); + let host_language_registry = Arc::new(LanguageRegistry::test()); let fs = FakeFs::new(cx.background()); fs.insert_tree( @@ -4852,6 +4835,7 @@ mod tests { let operations = Rc::new(Cell::new(0)); let mut server = TestServer::start(cx.foreground(), cx.background()).await; let mut clients = Vec::new(); + let files = Arc::new(Mutex::new(Vec::new())); let mut next_entity_id = 100000; let mut host_cx = TestAppContext::new( @@ -4868,7 +4852,7 @@ mod tests { Project::local( host.client.clone(), host.user_store.clone(), - Arc::new(LanguageRegistry::test()), + host_language_registry.clone(), fs.clone(), cx, ) @@ -4891,9 +4875,138 @@ mod tests { .await .unwrap(); + // Set up fake language servers. + let mut language = Language::new( + LanguageConfig { + name: "Rust".into(), + path_suffixes: vec!["rs".to_string()], + ..Default::default() + }, + None, + ); + let _fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter { + name: "the-fake-language-server", + capabilities: lsp::LanguageServer::full_capabilities(), + initializer: Some(Box::new({ + let rng = rng.clone(); + let files = files.clone(); + let project = host_project.downgrade(); + move |fake_server: &mut FakeLanguageServer| { + fake_server.handle_request::( + |_, _| async move { + Some(lsp::CompletionResponse::Array(vec![lsp::CompletionItem { + text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit { + range: lsp::Range::new( + lsp::Position::new(0, 0), + lsp::Position::new(0, 0), + ), + new_text: "the-new-text".to_string(), + })), + ..Default::default() + }])) + }, + ); + + fake_server.handle_request::( + |_, _| async move { + Some(vec![lsp::CodeActionOrCommand::CodeAction( + lsp::CodeAction { + title: "the-code-action".to_string(), + ..Default::default() + }, + )]) + }, + ); + + fake_server.handle_request::( + |params, _| async move { + Some(lsp::PrepareRenameResponse::Range(lsp::Range::new( + params.position, + params.position, + ))) + }, + ); + + fake_server.handle_request::({ + let files = files.clone(); + let rng = rng.clone(); + move |_, _| { + let files = files.clone(); + let rng = rng.clone(); + async move { + let files = files.lock(); + let mut rng = rng.lock(); + let count = rng.gen_range::(1..3); + let files = (0..count) + .map(|_| files.choose(&mut *rng).unwrap()) + .collect::>(); + log::info!("LSP: Returning definitions in files {:?}", &files); + Some(lsp::GotoDefinitionResponse::Array( + files + .into_iter() + .map(|file| lsp::Location { + uri: lsp::Url::from_file_path(file).unwrap(), + range: Default::default(), + }) + .collect(), + )) + } + } + }); + + fake_server.handle_request::({ + let rng = rng.clone(); + let project = project.clone(); + move |params, mut cx| { + let highlights = if let Some(project) = project.upgrade(&cx) { + project.update(&mut cx, |project, cx| { + let path = params + .text_document_position_params + .text_document + .uri + .to_file_path() + .unwrap(); + let (worktree, relative_path) = + project.find_local_worktree(&path, cx)?; + let project_path = + ProjectPath::from((worktree.read(cx).id(), relative_path)); + let buffer = + project.get_open_buffer(&project_path, cx)?.read(cx); + + let mut highlights = Vec::new(); + let highlight_count = rng.lock().gen_range(1..=5); + let mut prev_end = 0; + for _ in 0..highlight_count { + let range = + buffer.random_byte_range(prev_end, &mut *rng.lock()); + let start = buffer + .offset_to_point_utf16(range.start) + .to_lsp_position(); + let end = buffer + .offset_to_point_utf16(range.end) + .to_lsp_position(); + highlights.push(lsp::DocumentHighlight { + range: lsp::Range::new(start, end), + kind: Some(lsp::DocumentHighlightKind::READ), + }); + prev_end = range.end; + } + Some(highlights) + }) + } else { + None + }; + async move { highlights } + } + }); + } + })), + }); + host_language_registry.add(Arc::new(language)); + clients.push(cx.foreground().spawn(host.simulate_host( host_project, - language_server_config, + files, operations.clone(), max_operations, rng.clone(), @@ -5324,264 +5437,128 @@ mod tests { }) } - fn simulate_host( + async fn simulate_host( mut self, project: ModelHandle, - mut language_server_config: LanguageServerConfig, + files: Arc>>, operations: Rc>, max_operations: usize, rng: Arc>, mut cx: TestAppContext, - ) -> impl Future { - let files: Arc>> = Default::default(); + ) -> (Self, TestAppContext) { + let fs = project.read_with(&cx, |project, _| project.fs().clone()); + while operations.get() < max_operations { + operations.set(operations.get() + 1); - // Set up a fake language server. - language_server_config.set_fake_initializer({ - let rng = rng.clone(); - let files = files.clone(); - let project = project.downgrade(); - move |fake_server| { - fake_server.handle_request::( - |_, _| async move { - Some(lsp::CompletionResponse::Array(vec![lsp::CompletionItem { - text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit { - range: lsp::Range::new( - lsp::Position::new(0, 0), - lsp::Position::new(0, 0), - ), - new_text: "the-new-text".to_string(), - })), - ..Default::default() - }])) - }, - ); - - fake_server.handle_request::( - |_, _| async move { - Some(vec![lsp::CodeActionOrCommand::CodeAction( - lsp::CodeAction { - title: "the-code-action".to_string(), - ..Default::default() - }, - )]) - }, - ); - - fake_server.handle_request::( - |params, _| async move { - Some(lsp::PrepareRenameResponse::Range(lsp::Range::new( - params.position, - params.position, - ))) - }, - ); - - fake_server.handle_request::({ - let files = files.clone(); - let rng = rng.clone(); - move |_, _| { - let files = files.clone(); - let rng = rng.clone(); - async move { - let files = files.lock(); - let mut rng = rng.lock(); - let count = rng.gen_range::(1..3); - let files = (0..count) - .map(|_| files.choose(&mut *rng).unwrap()) - .collect::>(); - log::info!("LSP: Returning definitions in files {:?}", &files); - Some(lsp::GotoDefinitionResponse::Array( - files - .into_iter() - .map(|file| lsp::Location { - uri: lsp::Url::from_file_path(file).unwrap(), - range: Default::default(), - }) - .collect(), - )) - } - } - }); - - fake_server.handle_request::({ - let rng = rng.clone(); - let project = project.clone(); - move |params, mut cx| { - let highlights = if let Some(project) = project.upgrade(&cx) { - project.update(&mut cx, |project, cx| { - let path = params - .text_document_position_params - .text_document - .uri - .to_file_path() - .unwrap(); - let (worktree, relative_path) = - project.find_local_worktree(&path, cx)?; - let project_path = - ProjectPath::from((worktree.read(cx).id(), relative_path)); - let buffer = - project.get_open_buffer(&project_path, cx)?.read(cx); - - let mut highlights = Vec::new(); - let highlight_count = rng.lock().gen_range(1..=5); - let mut prev_end = 0; - for _ in 0..highlight_count { - let range = - buffer.random_byte_range(prev_end, &mut *rng.lock()); - let start = buffer - .offset_to_point_utf16(range.start) - .to_lsp_position(); - let end = buffer - .offset_to_point_utf16(range.end) - .to_lsp_position(); - highlights.push(lsp::DocumentHighlight { - range: lsp::Range::new(start, end), - kind: Some(lsp::DocumentHighlightKind::READ), - }); - prev_end = range.end; - } - Some(highlights) - }) - } else { - None - }; - async move { highlights } - } - }); - } - }); - - project.update(&mut cx, |project, _| { - project.languages().add(Arc::new(Language::new( - LanguageConfig { - name: "Rust".into(), - path_suffixes: vec!["rs".to_string()], - language_server: language_server_config, - ..Default::default() - }, - None, - ))); - }); - - async move { - let fs = project.read_with(&cx, |project, _| project.fs().clone()); - while operations.get() < max_operations { - operations.set(operations.get() + 1); - - let distribution = rng.lock().gen_range::(0..100); - match distribution { - 0..=20 if !files.lock().is_empty() => { - let path = files.lock().choose(&mut *rng.lock()).unwrap().clone(); - let mut path = path.as_path(); - while let Some(parent_path) = path.parent() { - path = parent_path; - if rng.lock().gen() { - break; - } - } - - log::info!("Host: find/create local worktree {:?}", path); - let find_or_create_worktree = project.update(&mut cx, |project, cx| { - project.find_or_create_local_worktree(path, true, cx) - }); - let find_or_create_worktree = async move { - find_or_create_worktree.await.unwrap(); - }; + let distribution = rng.lock().gen_range::(0..100); + match distribution { + 0..=20 if !files.lock().is_empty() => { + let path = files.lock().choose(&mut *rng.lock()).unwrap().clone(); + let mut path = path.as_path(); + while let Some(parent_path) = path.parent() { + path = parent_path; if rng.lock().gen() { - cx.background().spawn(find_or_create_worktree).detach(); - } else { - find_or_create_worktree.await; - } - } - 10..=80 if !files.lock().is_empty() => { - let buffer = if self.buffers.is_empty() || rng.lock().gen() { - let file = files.lock().choose(&mut *rng.lock()).unwrap().clone(); - let (worktree, path) = project - .update(&mut cx, |project, cx| { - project.find_or_create_local_worktree( - file.clone(), - true, - cx, - ) - }) - .await - .unwrap(); - let project_path = - worktree.read_with(&cx, |worktree, _| (worktree.id(), path)); - log::info!( - "Host: opening path {:?}, worktree {}, relative_path {:?}", - file, - project_path.0, - project_path.1 - ); - let buffer = project - .update(&mut cx, |project, cx| { - project.open_buffer(project_path, cx) - }) - .await - .unwrap(); - self.buffers.insert(buffer.clone()); - buffer - } else { - self.buffers - .iter() - .choose(&mut *rng.lock()) - .unwrap() - .clone() - }; - - if rng.lock().gen_bool(0.1) { - cx.update(|cx| { - log::info!( - "Host: dropping buffer {:?}", - buffer.read(cx).file().unwrap().full_path(cx) - ); - self.buffers.remove(&buffer); - drop(buffer); - }); - } else { - buffer.update(&mut cx, |buffer, cx| { - log::info!( - "Host: updating buffer {:?} ({})", - buffer.file().unwrap().full_path(cx), - buffer.remote_id() - ); - buffer.randomly_edit(&mut *rng.lock(), 5, cx) - }); - } - } - _ => loop { - let path_component_count = rng.lock().gen_range::(1..=5); - let mut path = PathBuf::new(); - path.push("/"); - for _ in 0..path_component_count { - let letter = rng.lock().gen_range(b'a'..=b'z'); - path.push(std::str::from_utf8(&[letter]).unwrap()); - } - path.set_extension("rs"); - let parent_path = path.parent().unwrap(); - - log::info!("Host: creating file {:?}", path,); - - if fs.create_dir(&parent_path).await.is_ok() - && fs.create_file(&path, Default::default()).await.is_ok() - { - files.lock().push(path); break; - } else { - log::info!("Host: cannot create file"); } - }, - } + } - cx.background().simulate_random_delay().await; + log::info!("Host: find/create local worktree {:?}", path); + let find_or_create_worktree = project.update(&mut cx, |project, cx| { + project.find_or_create_local_worktree(path, true, cx) + }); + let find_or_create_worktree = async move { + find_or_create_worktree.await.unwrap(); + }; + if rng.lock().gen() { + cx.background().spawn(find_or_create_worktree).detach(); + } else { + find_or_create_worktree.await; + } + } + 10..=80 if !files.lock().is_empty() => { + let buffer = if self.buffers.is_empty() || rng.lock().gen() { + let file = files.lock().choose(&mut *rng.lock()).unwrap().clone(); + let (worktree, path) = project + .update(&mut cx, |project, cx| { + project.find_or_create_local_worktree(file.clone(), true, cx) + }) + .await + .unwrap(); + let project_path = + worktree.read_with(&cx, |worktree, _| (worktree.id(), path)); + log::info!( + "Host: opening path {:?}, worktree {}, relative_path {:?}", + file, + project_path.0, + project_path.1 + ); + let buffer = project + .update(&mut cx, |project, cx| { + project.open_buffer(project_path, cx) + }) + .await + .unwrap(); + self.buffers.insert(buffer.clone()); + buffer + } else { + self.buffers + .iter() + .choose(&mut *rng.lock()) + .unwrap() + .clone() + }; + + if rng.lock().gen_bool(0.1) { + cx.update(|cx| { + log::info!( + "Host: dropping buffer {:?}", + buffer.read(cx).file().unwrap().full_path(cx) + ); + self.buffers.remove(&buffer); + drop(buffer); + }); + } else { + buffer.update(&mut cx, |buffer, cx| { + log::info!( + "Host: updating buffer {:?} ({})", + buffer.file().unwrap().full_path(cx), + buffer.remote_id() + ); + buffer.randomly_edit(&mut *rng.lock(), 5, cx) + }); + } + } + _ => loop { + let path_component_count = rng.lock().gen_range::(1..=5); + let mut path = PathBuf::new(); + path.push("/"); + for _ in 0..path_component_count { + let letter = rng.lock().gen_range(b'a'..=b'z'); + path.push(std::str::from_utf8(&[letter]).unwrap()); + } + path.set_extension("rs"); + let parent_path = path.parent().unwrap(); + + log::info!("Host: creating file {:?}", path,); + + if fs.create_dir(&parent_path).await.is_ok() + && fs.create_file(&path, Default::default()).await.is_ok() + { + files.lock().push(path); + break; + } else { + log::info!("Host: cannot create file"); + } + }, } - log::info!("Host done"); - - self.project = Some(project); - (self, cx) + cx.background().simulate_random_delay().await; } + + log::info!("Host done"); + + self.project = Some(project); + (self, cx) } pub async fn simulate_guest(