diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index e7a58075ac..3ac3c456f1 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -5442,6 +5442,7 @@ mod tests { use super::*; use language::LanguageConfig; use lsp::FakeLanguageServer; + use postage::prelude::Stream; use project::{FakeFs, ProjectPath}; use std::{cell::RefCell, rc::Rc, time::Instant}; use text::Point; @@ -7780,7 +7781,7 @@ mod tests { &mut fake, "/file", Point::new(0, 4), - &[ + vec![ (Point::new(0, 4)..Point::new(0, 4), "first_completion"), (Point::new(0, 4)..Point::new(0, 4), "second_completion"), ], @@ -7842,7 +7843,7 @@ mod tests { &mut fake, "/file", Point::new(2, 7), - &[ + vec![ (Point::new(2, 6)..Point::new(2, 7), "fourth_completion"), (Point::new(2, 6)..Point::new(2, 7), "fifth_completion"), (Point::new(2, 6)..Point::new(2, 7), "sixth_completion"), @@ -7861,7 +7862,7 @@ mod tests { &mut fake, "/file", Point::new(2, 8), - &[ + vec![ (Point::new(2, 6)..Point::new(2, 8), "fourth_completion"), (Point::new(2, 6)..Point::new(2, 8), "fifth_completion"), (Point::new(2, 6)..Point::new(2, 8), "sixth_completion"), @@ -7891,47 +7892,45 @@ mod tests { async fn handle_completion_request( fake: &mut FakeLanguageServer, - path: &str, + path: &'static str, position: Point, - completions: &[(Range, &str)], + completions: Vec<(Range, &'static str)>, ) { - let (id, params) = fake.receive_request::().await; - assert_eq!( - params.text_document_position.text_document.uri, - lsp::Url::from_file_path(path).unwrap() - ); - assert_eq!( - params.text_document_position.position, - lsp::Position::new(position.row, position.column) - ); - - let completions = completions - .iter() - .map(|(range, new_text)| lsp::CompletionItem { - label: new_text.to_string(), - text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit { - range: lsp::Range::new( - lsp::Position::new(range.start.row, range.start.column), - lsp::Position::new(range.start.row, range.start.column), - ), - new_text: new_text.to_string(), - })), - ..Default::default() - }) - .collect(); - fake.respond(id, Some(lsp::CompletionResponse::Array(completions))) - .await; + fake.handle_request::(move |params| { + assert_eq!( + params.text_document_position.text_document.uri, + lsp::Url::from_file_path(path).unwrap() + ); + assert_eq!( + params.text_document_position.position, + lsp::Position::new(position.row, position.column) + ); + Some(lsp::CompletionResponse::Array( + completions + .into_iter() + .map(|(range, new_text)| lsp::CompletionItem { + label: new_text.to_string(), + text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit { + range: lsp::Range::new( + lsp::Position::new(range.start.row, range.start.column), + lsp::Position::new(range.start.row, range.start.column), + ), + new_text: new_text.to_string(), + })), + ..Default::default() + }) + .collect(), + )) + }) + .recv() + .await; } async fn handle_resolve_completion_request( fake: &mut FakeLanguageServer, - edit: Option<(Range, &str)>, + edit: Option<(Range, &'static str)>, ) { - let (id, _) = fake - .receive_request::() - .await; - fake.respond( - id, + fake.handle_request::(move |_| { lsp::CompletionItem { additional_text_edits: edit.map(|(range, new_text)| { vec![lsp::TextEdit::new( @@ -7943,8 +7942,9 @@ mod tests { )] }), ..Default::default() - }, - ) + } + }) + .recv() .await; } } diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 4c673e2ff4..3a27b8f98b 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -56,6 +56,18 @@ struct Request<'a, T> { params: T, } +#[cfg(any(test, feature = "test-support"))] +#[derive(Deserialize)] +struct AnyRequest<'a> { + id: usize, + #[serde(borrow)] + jsonrpc: &'a str, + #[serde(borrow)] + method: &'a str, + #[serde(borrow)] + params: &'a RawValue, +} + #[derive(Serialize, Deserialize)] struct AnyResponse<'a> { id: usize, @@ -469,19 +481,19 @@ impl Drop for Subscription { #[cfg(any(test, feature = "test-support"))] pub struct FakeLanguageServer { - buffer: Vec, - stdin: smol::io::BufReader, - stdout: smol::io::BufWriter, - executor: std::rc::Rc, + handlers: Arc< + Mutex< + HashMap< + &'static str, + Box (Vec, barrier::Sender)>, + >, + >, + >, + outgoing_tx: channel::Sender>, + incoming_rx: channel::Receiver>, pub started: Arc, } -#[cfg(any(test, feature = "test-support"))] -pub struct RequestId { - id: usize, - _type: std::marker::PhantomData, -} - #[cfg(any(test, feature = "test-support"))] impl LanguageServer { pub async fn fake(cx: &gpui::TestAppContext) -> (Arc, FakeLanguageServer) { @@ -492,28 +504,18 @@ impl LanguageServer { capabilities: ServerCapabilities, cx: &gpui::TestAppContext, ) -> (Arc, FakeLanguageServer) { - let stdin = async_pipe::pipe(); - let stdout = async_pipe::pipe(); - let mut fake = FakeLanguageServer { - stdin: smol::io::BufReader::new(stdin.1), - stdout: smol::io::BufWriter::new(stdout.0), - buffer: Vec::new(), - executor: cx.foreground(), - started: Arc::new(std::sync::atomic::AtomicBool::new(true)), - }; + 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 server = - Self::new_internal(stdin.0, stdout.1, Path::new("/"), cx.background()).unwrap(); - - let (init_id, _) = fake.receive_request::().await; - fake.respond( - init_id, - InitializeResult { - capabilities, - ..Default::default() - }, - ) - .await; + Self::new_internal(stdin_writer, stdout_reader, Path::new("/"), cx.background()) + .unwrap(); fake.receive_notification::() .await; @@ -523,6 +525,75 @@ impl LanguageServer { #[cfg(any(test, feature = "test-support"))] impl FakeLanguageServer { + fn new( + cx: &gpui::TestAppContext, + stdin: async_pipe::PipeReader, + stdout: async_pipe::PipeWriter, + ) -> Self { + use futures::StreamExt as _; + + let (incoming_tx, incoming_rx) = channel::unbounded(); + let (outgoing_tx, mut outgoing_rx) = channel::unbounded(); + let this = Self { + 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() + .spawn(async move { + let mut buffer = Vec::new(); + let mut stdin = smol::io::BufReader::new(stdin); + while Self::receive(&mut stdin, &mut buffer).await.is_ok() { + if let Ok(request) = serde_json::from_slice::(&mut buffer) { + assert_eq!(request.jsonrpc, JSON_RPC_VERSION); + + let handler = handlers.lock().remove(request.method); + if let Some(handler) = handler { + let (response, sent) = + handler(request.id, request.params.get().as_bytes()); + log::debug!("handled lsp request. method:{}", request.method); + outgoing_tx.send(response).await.unwrap(); + drop(sent); + } else { + log::debug!("unhandled lsp request. method:{}", request.method); + outgoing_tx + .send( + serde_json::to_vec(&AnyResponse { + id: request.id, + error: Some(Error { + message: "no handler".to_string(), + }), + result: None, + }) + .unwrap(), + ) + .await + .unwrap(); + } + } else { + incoming_tx.send(buffer.clone()).await.unwrap(); + } + } + }) + .detach(); + + // Send outgoing messages + cx.background() + .spawn(async move { + let mut stdout = smol::io::BufWriter::new(stdout); + while let Some(notification) = outgoing_rx.next().await { + Self::send(&mut stdout, ¬ification).await; + } + }) + .detach(); + + this + } + 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"); @@ -533,54 +604,53 @@ impl FakeLanguageServer { params, }) .unwrap(); - self.send(message).await; + self.outgoing_tx.send(message).await.unwrap(); } - pub async fn respond<'a, T: request::Request>( - &mut self, - request_id: RequestId, - result: T::Result, - ) { - let result = serde_json::to_string(&result).unwrap(); - let message = serde_json::to_vec(&AnyResponse { - id: request_id.id, - error: None, - result: Some(&RawValue::from_string(result).unwrap()), - }) - .unwrap(); - self.send(message).await; - } + pub async fn receive_notification(&mut self) -> T::Params { + use futures::StreamExt as _; - pub async fn receive_request(&mut self) -> (RequestId, T::Params) { - let executor = self.executor.clone(); - executor.start_waiting(); loop { - self.receive().await; - if let Ok(request) = serde_json::from_slice::>(&self.buffer) { - assert_eq!(request.method, T::METHOD); - assert_eq!(request.jsonrpc, JSON_RPC_VERSION); - executor.finish_waiting(); - return ( - RequestId { - id: request.id, - _type: std::marker::PhantomData, - }, - request.params, - ); + let bytes = self.incoming_rx.next().await.unwrap(); + if let Ok(notification) = serde_json::from_slice::>(&bytes) { + assert_eq!(notification.method, T::METHOD); + return notification.params; } else { log::info!( "skipping message in fake language server {:?}", - std::str::from_utf8(&self.buffer) + std::str::from_utf8(&bytes) ); } } } - pub async fn receive_notification(&mut self) -> T::Params { - self.receive().await; - let notification = serde_json::from_slice::>(&self.buffer).unwrap(); - assert_eq!(notification.method, T::METHOD); - notification.params + pub fn handle_request(&mut self, handler: F) -> barrier::Receiver + where + T: 'static + request::Request, + F: 'static + Send + FnOnce(T::Params) -> T::Result, + { + let (responded_tx, responded_rx) = barrier::channel(); + let prev_handler = self.handlers.lock().insert( + T::METHOD, + Box::new(|id, params| { + let result = handler(serde_json::from_slice::(params).unwrap()); + let result = serde_json::to_string(&result).unwrap(); + let result = serde_json::from_str::<&RawValue>(&result).unwrap(); + let response = AnyResponse { + id, + error: None, + result: Some(result), + }; + (serde_json::to_vec(&response).unwrap(), responded_tx) + }), + ); + if prev_handler.is_some() { + panic!( + "registered a new handler for LSP method '{}' before the previous handler was called", + T::METHOD + ); + } + responded_rx } pub async fn start_progress(&mut self, token: impl Into) { @@ -599,39 +669,37 @@ impl FakeLanguageServer { .await; } - async fn send(&mut self, message: Vec) { - self.stdout + async fn send(stdout: &mut smol::io::BufWriter, message: &[u8]) { + stdout .write_all(CONTENT_LEN_HEADER.as_bytes()) .await .unwrap(); - self.stdout + stdout .write_all((format!("{}", message.len())).as_bytes()) .await .unwrap(); - self.stdout.write_all("\r\n\r\n".as_bytes()).await.unwrap(); - self.stdout.write_all(&message).await.unwrap(); - self.stdout.flush().await.unwrap(); + stdout.write_all("\r\n\r\n".as_bytes()).await.unwrap(); + stdout.write_all(&message).await.unwrap(); + stdout.flush().await.unwrap(); } - async fn receive(&mut self) { - self.buffer.clear(); - self.stdin - .read_until(b'\n', &mut self.buffer) - .await - .unwrap(); - self.stdin - .read_until(b'\n', &mut self.buffer) - .await - .unwrap(); - let message_len: usize = std::str::from_utf8(&self.buffer) + async fn receive( + stdin: &mut smol::io::BufReader, + buffer: &mut Vec, + ) -> Result<()> { + buffer.clear(); + stdin.read_until(b'\n', buffer).await?; + stdin.read_until(b'\n', buffer).await?; + let message_len: usize = std::str::from_utf8(buffer) .unwrap() .strip_prefix(CONTENT_LEN_HEADER) .unwrap() .trim_end() .parse() .unwrap(); - self.buffer.resize(message_len, 0); - self.stdin.read_exact(&mut self.buffer).await.unwrap(); + buffer.resize(message_len, 0); + stdin.read_exact(buffer).await?; + Ok(()) } } @@ -757,9 +825,9 @@ mod tests { "file://b/c" ); + fake.handle_request::(|_| ()); + drop(server); - let (shutdown_request, _) = fake.receive_request::().await; - fake.respond(shutdown_request, ()).await; fake.receive_notification::().await; } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 36d04ae9ee..74fdf7fc8e 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -2368,6 +2368,7 @@ impl Project { .ok_or_else(|| anyhow!("invalid completion"))?, language, )?; + dbg!(&completion); cx.spawn(|this, mut cx| async move { match this .update(&mut cx, |this, cx| { @@ -2414,14 +2415,15 @@ impl Project { .and_then(language::proto::deserialize_anchor) .ok_or_else(|| anyhow!("invalid position"))?; cx.spawn(|this, mut cx| async move { + eprintln!("getting code actions"); match this .update(&mut cx, |this, cx| this.code_actions(&buffer, position, cx)) .await { - Ok(completions) => rpc.respond( + Ok(actions) => rpc.respond( receipt, proto::GetCodeActionsResponse { - actions: completions + actions: dbg!(actions) .iter() .map(language::proto::serialize_code_action) .collect(), @@ -2430,7 +2432,7 @@ impl Project { Err(error) => rpc.respond_with_error( receipt, proto::Error { - message: error.to_string(), + message: dbg!(error.to_string()), }, ), } @@ -3205,6 +3207,7 @@ mod tests { "a.rs": "const fn a() { A }", "b.rs": "const y: i32 = crate::a()", })); + let dir_path = dir.path().to_path_buf(); let http_client = FakeHttpClient::with_404_response(); let client = Client::new(http_client.clone()); @@ -3229,7 +3232,6 @@ mod tests { cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete()) .await; - // Cause worktree to start the fake language server let buffer = project .update(&mut cx, |project, cx| { project.open_buffer( @@ -3242,28 +3244,26 @@ mod tests { }) .await .unwrap(); - let definitions = - project.update(&mut cx, |project, cx| project.definition(&buffer, 22, cx)); - let (request_id, request) = fake_server - .receive_request::() - .await; - let request_params = request.text_document_position_params; - assert_eq!( - request_params.text_document.uri.to_file_path().unwrap(), - dir.path().join("b.rs") - ); - assert_eq!(request_params.position, lsp::Position::new(0, 22)); - fake_server - .respond( - request_id, - Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location::new( - lsp::Url::from_file_path(dir.path().join("a.rs")).unwrap(), - lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)), - ))), - ) - .await; - let mut definitions = definitions.await.unwrap(); + fake_server.handle_request::(move |params| { + let params = params.text_document_position_params; + assert_eq!( + params.text_document.uri.to_file_path().unwrap(), + dir_path.join("b.rs") + ); + assert_eq!(params.position, lsp::Position::new(0, 22)); + + Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location::new( + lsp::Url::from_file_path(dir_path.join("a.rs")).unwrap(), + lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)), + ))) + }); + + let mut definitions = project + .update(&mut cx, |project, cx| project.definition(&buffer, 22, cx)) + .await + .unwrap(); + assert_eq!(definitions.len(), 1); let definition = definitions.pop().unwrap(); cx.update(|cx| { diff --git a/crates/server/src/rpc.rs b/crates/server/src/rpc.rs index 72edd9659f..45d1114133 100644 --- a/crates/server/src/rpc.rs +++ b/crates/server/src/rpc.rs @@ -2414,52 +2414,46 @@ mod tests { }); // Receive a completion request as the host's language server. - let (request_id, params) = fake_language_server - .receive_request::() - .await; - 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), - ); - // Return some completions from the host's language server. - fake_language_server - .respond( - request_id, - 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".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() - }, - ])), - ) - .await; + fake_language_server.handle_request::(|params| { + 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), + ); + + 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".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() + }, + ])) + }); // Open the buffer on the host. let buffer_a = project_a @@ -2480,43 +2474,32 @@ mod tests { 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::(|params| { + assert_eq!(params.label, "first_method(…)"); + 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() + } + }); + buffer_a .condition(&cx_a, |buffer, _| { buffer.text() == "fn main() { a.first_method() }" }) .await; - // Receive a request resolve the selected completion on the host's language server. - let (request_id, params) = fake_language_server - .receive_request::() - .await; - assert_eq!(params.label, "first_method(…)"); - - // Return a resolved completion from the host's language server. - // The resolved completion has an additional text edit. - fake_language_server - .respond( - request_id, - 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() - }, - ) - .await; - // The additional edit is applied. buffer_b .condition(&cx_b, |buffer, _| { @@ -2610,24 +2593,20 @@ mod tests { let format = project_b.update(&mut cx_b, |project, cx| { project.format(HashSet::from_iter([buffer_b.clone()]), true, cx) }); - let (request_id, _) = fake_language_server - .receive_request::() - .await; - fake_language_server - .respond( - request_id, - Some(vec![ - lsp::TextEdit { - range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)), - new_text: "h".to_string(), - }, - lsp::TextEdit { - range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)), - new_text: "y".to_string(), - }, - ]), - ) - .await; + + fake_language_server.handle_request::(|_| { + Some(vec![ + lsp::TextEdit { + range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)), + new_text: "h".to_string(), + }, + lsp::TextEdit { + range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)), + new_text: "y".to_string(), + }, + ]) + }); + format.await.unwrap(); assert_eq!( buffer_b.read_with(&cx_b, |buffer, _| buffer.text()), @@ -2714,26 +2693,22 @@ mod tests { .await .unwrap(); - // Open the file to be formatted on client B. + // Open the file on client B. let buffer_b = cx_b .background() .spawn(project_b.update(&mut cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))) .await .unwrap(); + // 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 (request_id, _) = fake_language_server - .receive_request::() - .await; - fake_language_server - .respond( - request_id, - Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location::new( - lsp::Url::from_file_path("/root-2/b.rs").unwrap(), - lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)), - ))), - ) - .await; + fake_language_server.handle_request::(|_| { + Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location::new( + lsp::Url::from_file_path("/root-2/b.rs").unwrap(), + lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)), + ))) + }); + let definitions_1 = definitions_1.await.unwrap(); cx_b.read(|cx| { assert_eq!(definitions_1.len(), 1); @@ -2752,18 +2727,13 @@ mod tests { // Try getting more definitions for the same buffer, ensuring the buffer gets reused from // the previous call to `definition`. let definitions_2 = project_b.update(&mut cx_b, |p, cx| p.definition(&buffer_b, 33, cx)); - let (request_id, _) = fake_language_server - .receive_request::() - .await; - fake_language_server - .respond( - request_id, - Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location::new( - lsp::Url::from_file_path("/root-2/b.rs").unwrap(), - lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)), - ))), - ) - .await; + fake_language_server.handle_request::(|_| { + Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location::new( + lsp::Url::from_file_path("/root-2/b.rs").unwrap(), + lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)), + ))) + }); + let definitions_2 = definitions_2.await.unwrap(); cx_b.read(|cx| { assert_eq!(definitions_2.len(), 1); @@ -2887,18 +2857,12 @@ mod tests { definitions = project_b.update(&mut cx_b, |p, cx| p.definition(&buffer_b1, 23, cx)); } - let (request_id, _) = fake_language_server - .receive_request::() - .await; - fake_language_server - .respond( - request_id, - Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location::new( - lsp::Url::from_file_path("/root/b.rs").unwrap(), - lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)), - ))), - ) - .await; + fake_language_server.handle_request::(|_| { + Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location::new( + lsp::Url::from_file_path("/root/b.rs").unwrap(), + lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)), + ))) + }); let buffer_b2 = buffer_b2.await.unwrap(); let definitions = definitions.await.unwrap();