diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 3d28d7aa2cf8c56360925c513f2923173b1b23ca..6c331be13986265f3070c7724f701981e5cf9a0a 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -7737,9 +7737,8 @@ mod tests { }), ..Default::default() }, - &cx, - ) - .await; + cx.background(), + ); let text = " one diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index bf24118aff1ddbacb87513408b37f79a10e205e9..ec217d58459619c953a11b3a036598babdd817cd 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -12,11 +12,15 @@ use gpui::AppContext; use highlight_map::HighlightMap; use lazy_static::lazy_static; use parking_lot::Mutex; +use postage::prelude::Stream; use serde::Deserialize; use std::{cell::RefCell, ops::Range, path::Path, str, sync::Arc}; use theme::SyntaxTheme; use tree_sitter::{self, Query}; +#[cfg(any(test, feature = "test-support"))] +use futures::channel::mpsc; + pub use buffer::Operation; pub use buffer::*; pub use diagnostic_set::DiagnosticEntry; @@ -79,7 +83,13 @@ pub struct LanguageServerConfig { pub disk_based_diagnostics_progress_token: Option, #[cfg(any(test, feature = "test-support"))] #[serde(skip)] - pub fake_server: Option<(Arc, Arc)>, + fake_config: Option, +} + +#[cfg(any(test, feature = "test-support"))] +struct FakeLanguageServerConfig { + servers_tx: mpsc::UnboundedSender, + capabilities: lsp::ServerCapabilities, } #[derive(Clone, Debug, Deserialize)] @@ -224,8 +234,21 @@ impl Language { ) -> Result>> { if let Some(config) = &self.config.language_server { #[cfg(any(test, feature = "test-support"))] - if let Some((server, started)) = &config.fake_server { - started.store(true, std::sync::atomic::Ordering::SeqCst); + if let Some(fake_config) = &config.fake_config { + let (server, fake_server) = lsp::LanguageServer::fake_with_capabilities( + fake_config.capabilities.clone(), + cx.background().clone(), + ); + + let servers_tx = fake_config.servers_tx.clone(); + let mut initialized = server.capabilities(); + cx.background() + .spawn(async move { + while initialized.recv().await.is_none() {} + servers_tx.unbounded_send(fake_server).ok(); + }) + .detach(); + return Ok(Some(server.clone())); } @@ -357,25 +380,24 @@ impl CompletionLabel { #[cfg(any(test, feature = "test-support"))] impl LanguageServerConfig { - pub async fn fake(cx: &gpui::TestAppContext) -> (Self, lsp::FakeLanguageServer) { - Self::fake_with_capabilities(Default::default(), cx).await + pub fn fake() -> (Self, mpsc::UnboundedReceiver) { + Self::fake_with_capabilities(Default::default()) } - pub async fn fake_with_capabilities( - capabilites: lsp::ServerCapabilities, - cx: &gpui::TestAppContext, - ) -> (Self, lsp::FakeLanguageServer) { - let (server, fake) = lsp::LanguageServer::fake_with_capabilities(capabilites, cx).await; - fake.started - .store(false, std::sync::atomic::Ordering::SeqCst); - let started = fake.started.clone(); + pub fn fake_with_capabilities( + capabilities: lsp::ServerCapabilities, + ) -> (Self, mpsc::UnboundedReceiver) { + let (servers_tx, servers_rx) = mpsc::unbounded(); ( Self { - fake_server: Some((server, started)), + fake_config: Some(FakeLanguageServerConfig { + servers_tx, + capabilities, + }), disk_based_diagnostics_progress_token: Some("fakeServer/check".to_string()), ..Default::default() }, - fake, + servers_rx, ) } } diff --git a/crates/language/src/tests.rs b/crates/language/src/tests.rs index c7ea90714cdb474bd42018f6a249e5c8029c47c4..0ae1fbe7074ce341bd4e9ea45b4336096b572df5 100644 --- a/crates/language/src/tests.rs +++ b/crates/language/src/tests.rs @@ -557,7 +557,7 @@ fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppConte #[gpui::test] async fn test_diagnostics(mut cx: gpui::TestAppContext) { - let (language_server, mut fake) = lsp::LanguageServer::fake(&cx).await; + let (language_server, mut fake) = lsp::LanguageServer::fake(cx.background()); let mut rust_lang = rust_lang(); rust_lang.config.language_server = Some(LanguageServerConfig { disk_based_diagnostic_sources: HashSet::from_iter(["disk".to_string()]), @@ -840,7 +840,7 @@ async fn test_diagnostics(mut cx: gpui::TestAppContext) { #[gpui::test] async fn test_edits_from_lsp_with_past_version(mut cx: gpui::TestAppContext) { - let (language_server, mut fake) = lsp::LanguageServer::fake(&cx).await; + let (language_server, mut fake) = lsp::LanguageServer::fake(cx.background()); let text = " fn a() { diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 73f4fe698b8182dccdcdcaa49a9d699bd357ee94..797233290f5001a7eb9bf1865f08aaecdaa4bb21 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -491,33 +491,31 @@ pub struct FakeLanguageServer { >, outgoing_tx: channel::Sender>, incoming_rx: channel::Receiver>, - pub started: Arc, } #[cfg(any(test, feature = "test-support"))] impl LanguageServer { - pub async fn fake(cx: &gpui::TestAppContext) -> (Arc, FakeLanguageServer) { - Self::fake_with_capabilities(Default::default(), cx).await + pub fn fake(executor: Arc) -> (Arc, FakeLanguageServer) { + Self::fake_with_capabilities(Default::default(), executor) } - pub async fn fake_with_capabilities( + pub fn fake_with_capabilities( capabilities: ServerCapabilities, - cx: &gpui::TestAppContext, + executor: Arc, ) -> (Arc, FakeLanguageServer) { let (stdin_writer, stdin_reader) = async_pipe::pipe(); let (stdout_writer, stdout_reader) = async_pipe::pipe(); - let mut fake = FakeLanguageServer::new(cx, stdin_reader, stdout_writer); - fake.handle_request::(move |_| InitializeResult { - capabilities, - ..Default::default() + let mut fake = FakeLanguageServer::new(executor.clone(), stdin_reader, stdout_writer); + fake.handle_request::({ + move |_| InitializeResult { + capabilities, + ..Default::default() + } }); let server = - Self::new_internal(stdin_writer, stdout_reader, Path::new("/"), cx.background()) - .unwrap(); - fake.receive_notification::() - .await; + Self::new_internal(stdin_writer, stdout_reader, Path::new("/"), executor).unwrap(); (server, fake) } @@ -526,7 +524,7 @@ impl LanguageServer { #[cfg(any(test, feature = "test-support"))] impl FakeLanguageServer { fn new( - cx: &gpui::TestAppContext, + executor: Arc, stdin: async_pipe::PipeReader, stdout: async_pipe::PipeWriter, ) -> Self { @@ -538,12 +536,11 @@ impl FakeLanguageServer { outgoing_tx: outgoing_tx.clone(), incoming_rx, handlers: Default::default(), - started: Arc::new(std::sync::atomic::AtomicBool::new(true)), }; // Receive incoming messages let handlers = this.handlers.clone(); - cx.background() + executor .spawn(async move { let mut buffer = Vec::new(); let mut stdin = smol::io::BufReader::new(stdin); @@ -582,7 +579,7 @@ impl FakeLanguageServer { .detach(); // Send outgoing messages - cx.background() + executor .spawn(async move { let mut stdout = smol::io::BufWriter::new(stdout); while let Some(notification) = outgoing_rx.next().await { @@ -595,9 +592,6 @@ impl FakeLanguageServer { } pub async fn notify(&mut self, params: T::Params) { - if !self.started.load(std::sync::atomic::Ordering::SeqCst) { - panic!("can't simulate an LSP notification before the server has been started"); - } let message = serde_json::to_vec(&Notification { jsonrpc: JSON_RPC_VERSION, method: T::METHOD, @@ -777,7 +771,7 @@ mod tests { #[gpui::test] async fn test_fake(cx: TestAppContext) { - let (server, mut fake) = LanguageServer::fake(&cx).await; + let (server, mut fake) = LanguageServer::fake(cx.background()); let (message_tx, message_rx) = channel::unbounded(); let (diagnostics_tx, diagnostics_rx) = channel::unbounded(); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 02dfc9263b1429180fd6042333eace9c1a6e08fa..a0fa97bd38078281e47956c48b15f2de53c4b2bb 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -346,15 +346,10 @@ impl Project { #[cfg(any(test, feature = "test-support"))] pub fn shared_buffer(&self, peer_id: PeerId, remote_id: u64) -> Option> { - let result = self - .shared_buffers + self.shared_buffers .get(&peer_id) .and_then(|buffers| buffers.get(&remote_id)) - .cloned(); - if result.is_none() { - dbg!(&self.shared_buffers); - } - result + .cloned() } #[cfg(any(test, feature = "test-support"))] @@ -3051,7 +3046,7 @@ mod tests { #[gpui::test] async fn test_language_server_diagnostics(mut cx: gpui::TestAppContext) { - let (language_server_config, mut fake_server) = LanguageServerConfig::fake(&cx).await; + let (language_server_config, mut fake_servers) = LanguageServerConfig::fake(); let progress_token = language_server_config .disk_based_diagnostics_progress_token .clone() @@ -3114,6 +3109,7 @@ mod tests { let mut events = subscribe(&project, &mut cx); + let mut fake_server = fake_servers.next().await.unwrap(); fake_server.start_progress(&progress_token).await; assert_eq!( events.next().await.unwrap(), @@ -3215,7 +3211,7 @@ mod tests { #[gpui::test] async fn test_definition(mut cx: gpui::TestAppContext) { - let (language_server_config, mut fake_server) = LanguageServerConfig::fake(&cx).await; + let (language_server_config, mut fake_servers) = LanguageServerConfig::fake(); let mut languages = LanguageRegistry::new(); languages.add(Arc::new(Language::new( @@ -3270,6 +3266,7 @@ mod tests { .await .unwrap(); + let mut fake_server = fake_servers.next().await.unwrap(); fake_server.handle_request::(move |params| { let params = params.text_document_position_params; assert_eq!( diff --git a/crates/server/src/rpc.rs b/crates/server/src/rpc.rs index aacace9ec5799ff223bc965ad5e1d980d67cc0e6..3bad2f259909ce1057309259186da7214ee508aa 100644 --- a/crates/server/src/rpc.rs +++ b/crates/server/src/rpc.rs @@ -1992,8 +1992,7 @@ mod tests { let fs = Arc::new(FakeFs::new(cx_a.background())); // Set up a fake language server. - let (language_server_config, mut fake_language_server) = - LanguageServerConfig::fake(&cx_a).await; + let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake(); Arc::get_mut(&mut lang_registry) .unwrap() .add(Arc::new(Language::new( @@ -2062,6 +2061,7 @@ mod tests { .unwrap(); // Simulate a language server reporting errors for a file. + let mut fake_language_server = fake_language_servers.next().await.unwrap(); fake_language_server .notify::(lsp::PublishDiagnosticsParams { uri: lsp::Url::from_file_path("/a/a.rs").unwrap(), @@ -2217,18 +2217,14 @@ mod tests { let fs = Arc::new(FakeFs::new(cx_a.background())); // Set up a fake language server. - let (language_server_config, mut fake_language_server) = - LanguageServerConfig::fake_with_capabilities( - lsp::ServerCapabilities { - completion_provider: Some(lsp::CompletionOptions { - trigger_characters: Some(vec![".".to_string()]), - ..Default::default() - }), + let (language_server_config, mut fake_language_servers) = + LanguageServerConfig::fake_with_capabilities(lsp::ServerCapabilities { + completion_provider: Some(lsp::CompletionOptions { + trigger_characters: Some(vec![".".to_string()]), ..Default::default() - }, - &cx_a, - ) - .await; + }), + ..Default::default() + }); Arc::get_mut(&mut lang_registry) .unwrap() .add(Arc::new(Language::new( @@ -2310,6 +2306,11 @@ mod tests { ) }); + let mut fake_language_server = fake_language_servers.next().await.unwrap(); + buffer_b + .condition(&cx_b, |buffer, _| !buffer.completion_triggers().is_empty()) + .await; + // Type a completion trigger character as the guest. editor_b.update(&mut cx_b, |editor, cx| { editor.select_ranges([13..13], None, cx); @@ -2423,8 +2424,7 @@ mod tests { let fs = Arc::new(FakeFs::new(cx_a.background())); // Set up a fake language server. - let (language_server_config, mut fake_language_server) = - LanguageServerConfig::fake(&cx_a).await; + let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake(); Arc::get_mut(&mut lang_registry) .unwrap() .add(Arc::new(Language::new( @@ -2498,6 +2498,7 @@ mod tests { project.format(HashSet::from_iter([buffer_b.clone()]), true, cx) }); + let mut fake_language_server = fake_language_servers.next().await.unwrap(); fake_language_server.handle_request::(|_| { Some(vec![ lsp::TextEdit { @@ -2544,8 +2545,7 @@ mod tests { .await; // Set up a fake language server. - let (language_server_config, mut fake_language_server) = - LanguageServerConfig::fake(&cx_a).await; + let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake(); Arc::get_mut(&mut lang_registry) .unwrap() .add(Arc::new(Language::new( @@ -2610,6 +2610,8 @@ mod tests { // Request the definition of a symbol as the guest. let definitions_1 = project_b.update(&mut cx_b, |p, cx| p.definition(&buffer_b, 23, cx)); + + let mut fake_language_server = fake_language_servers.next().await.unwrap(); fake_language_server.handle_request::(|_| { Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location::new( lsp::Url::from_file_path("/root-2/b.rs").unwrap(), @@ -2691,8 +2693,7 @@ mod tests { .await; // Set up a fake language server. - let (language_server_config, mut fake_language_server) = - LanguageServerConfig::fake(&cx_a).await; + let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake(); Arc::get_mut(&mut lang_registry) .unwrap() @@ -2768,6 +2769,7 @@ mod tests { definitions = project_b.update(&mut cx_b, |p, cx| p.definition(&buffer_b1, 23, cx)); } + let mut fake_language_server = fake_language_servers.next().await.unwrap(); fake_language_server.handle_request::(|_| { Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location::new( lsp::Url::from_file_path("/root/b.rs").unwrap(), @@ -2794,14 +2796,7 @@ mod tests { cx_b.update(|cx| editor::init(cx, &mut path_openers_b)); // Set up a fake language server. - let (language_server_config, mut fake_language_server) = - LanguageServerConfig::fake_with_capabilities( - lsp::ServerCapabilities { - ..Default::default() - }, - &cx_a, - ) - .await; + let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake(); Arc::get_mut(&mut lang_registry) .unwrap() .add(Arc::new(Language::new( @@ -2881,6 +2876,8 @@ mod tests { .unwrap() .downcast::() .unwrap(); + + let mut fake_language_server = fake_language_servers.next().await.unwrap(); fake_language_server .handle_request::(|params| { assert_eq!(