Let fake and real LanguageServer access AsyncAppContext in handler callbacks

Also, reimplement FakeLanguageServer by wrapping LanguageServer, instead of
duplicating its functionality differently.
This commit is contained in:
Max Brunsfeld 2022-03-31 21:52:14 -07:00
parent afbddc1bcd
commit e987a8ba63
5 changed files with 317 additions and 419 deletions

View file

@ -8928,7 +8928,7 @@ mod tests {
.unwrap(); .unwrap();
cx.foreground().start_waiting(); cx.foreground().start_waiting();
let mut fake_server = fake_servers.next().await.unwrap(); let fake_server = fake_servers.next().await.unwrap();
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
@ -8942,10 +8942,10 @@ mod tests {
params.text_document.uri, params.text_document.uri,
lsp::Url::from_file_path("/file.rs").unwrap() lsp::Url::from_file_path("/file.rs").unwrap()
); );
Some(vec![lsp::TextEdit::new( Ok(Some(vec![lsp::TextEdit::new(
lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)), lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
", ".to_string(), ", ".to_string(),
)]) )]))
}) })
.next() .next()
.await; .await;
@ -9173,7 +9173,7 @@ mod tests {
params.text_document_position.position, params.text_document_position.position,
lsp::Position::new(position.row, position.column) lsp::Position::new(position.row, position.column)
); );
Some(lsp::CompletionResponse::Array( Ok(Some(lsp::CompletionResponse::Array(
completions completions
.iter() .iter()
.map(|(range, new_text)| lsp::CompletionItem { .map(|(range, new_text)| lsp::CompletionItem {
@ -9188,7 +9188,7 @@ mod tests {
..Default::default() ..Default::default()
}) })
.collect(), .collect(),
)) )))
} }
}) })
.next() .next()
@ -9202,7 +9202,7 @@ mod tests {
fake.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _| { fake.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _| {
let edit = edit.clone(); let edit = edit.clone();
async move { async move {
lsp::CompletionItem { Ok(lsp::CompletionItem {
additional_text_edits: edit.map(|(range, new_text)| { additional_text_edits: edit.map(|(range, new_text)| {
vec![lsp::TextEdit::new( vec![lsp::TextEdit::new(
lsp::Range::new( lsp::Range::new(
@ -9213,7 +9213,7 @@ mod tests {
)] )]
}), }),
..Default::default() ..Default::default()
} })
} }
}) })
.next() .next()

View file

@ -263,14 +263,12 @@ impl LanguageRegistry {
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
if language.fake_adapter.is_some() { if language.fake_adapter.is_some() {
let language = language.clone(); let language = language.clone();
return Some(cx.spawn(|mut cx| async move { return Some(cx.spawn(|cx| async move {
let (servers_tx, fake_adapter) = language.fake_adapter.as_ref().unwrap(); let (servers_tx, fake_adapter) = language.fake_adapter.as_ref().unwrap();
let (server, mut fake_server) = cx.update(|cx| { let (server, mut fake_server) = lsp::LanguageServer::fake_with_capabilities(
lsp::LanguageServer::fake_with_capabilities( fake_adapter.capabilities.clone(),
fake_adapter.capabilities.clone(), cx.clone(),
cx, );
)
});
if let Some(initializer) = &fake_adapter.initializer { if let Some(initializer) = &fake_adapter.initializer {
initializer(&mut fake_server); initializer(&mut fake_server);
@ -297,10 +295,9 @@ impl LanguageRegistry {
let this = self.clone(); let this = self.clone();
let adapter = language.adapter.clone()?; let adapter = language.adapter.clone()?;
let background = cx.background().clone();
let lsp_binary_statuses = self.lsp_binary_statuses_tx.clone(); let lsp_binary_statuses = self.lsp_binary_statuses_tx.clone();
let login_shell_env_loaded = self.login_shell_env_loaded.clone(); let login_shell_env_loaded = self.login_shell_env_loaded.clone();
Some(cx.background().spawn(async move { Some(cx.spawn(|cx| async move {
login_shell_env_loaded.await; login_shell_env_loaded.await;
let server_binary_path = this let server_binary_path = this
.lsp_binary_paths .lsp_binary_paths
@ -328,8 +325,7 @@ impl LanguageRegistry {
&server_binary_path, &server_binary_path,
server_args, server_args,
&root_path, &root_path,
adapter.initialization_options(), cx,
background,
)?; )?;
Ok(server) Ok(server)
})) }))

View file

@ -1,15 +1,17 @@
pub use lsp_types::*;
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use collections::HashMap; use collections::HashMap;
use futures::{channel::oneshot, io::BufWriter, AsyncRead, AsyncWrite}; use futures::{channel::oneshot, io::BufWriter, AsyncRead, AsyncWrite};
use gpui::{executor, Task}; use gpui::{executor, AsyncAppContext, Task};
use parking_lot::{Mutex, RwLock}; use parking_lot::Mutex;
use postage::{barrier, prelude::Stream}; use postage::{barrier, prelude::Stream};
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::{json, value::RawValue, Value}; use serde_json::{json, value::RawValue, Value};
use smol::{ use smol::{
channel, channel,
io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader}, io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader},
process::Command, process,
}; };
use std::{ use std::{
future::Future, future::Future,
@ -22,15 +24,12 @@ use std::{
}, },
}; };
use std::{path::Path, process::Stdio}; use std::{path::Path, process::Stdio};
use util::TryFutureExt; use util::{ResultExt, TryFutureExt};
pub use lsp_types::*;
const JSON_RPC_VERSION: &'static str = "2.0"; const JSON_RPC_VERSION: &'static str = "2.0";
const CONTENT_LEN_HEADER: &'static str = "Content-Length: "; const CONTENT_LEN_HEADER: &'static str = "Content-Length: ";
type NotificationHandler = type NotificationHandler = Box<dyn Send + FnMut(Option<usize>, &str, AsyncAppContext)>;
Box<dyn Send + Sync + FnMut(Option<usize>, &str, &mut channel::Sender<Vec<u8>>) -> Result<()>>;
type ResponseHandler = Box<dyn Send + FnOnce(Result<&str, Error>)>; type ResponseHandler = Box<dyn Send + FnOnce(Result<&str, Error>)>;
pub struct LanguageServer { pub struct LanguageServer {
@ -39,18 +38,17 @@ pub struct LanguageServer {
outbound_tx: channel::Sender<Vec<u8>>, outbound_tx: channel::Sender<Vec<u8>>,
name: String, name: String,
capabilities: ServerCapabilities, capabilities: ServerCapabilities,
notification_handlers: Arc<RwLock<HashMap<&'static str, NotificationHandler>>>, notification_handlers: Arc<Mutex<HashMap<&'static str, NotificationHandler>>>,
response_handlers: Arc<Mutex<HashMap<usize, ResponseHandler>>>, response_handlers: Arc<Mutex<HashMap<usize, ResponseHandler>>>,
executor: Arc<executor::Background>, executor: Arc<executor::Background>,
io_tasks: Mutex<Option<(Task<Option<()>>, Task<Option<()>>)>>, io_tasks: Mutex<Option<(Task<Option<()>>, Task<Option<()>>)>>,
output_done_rx: Mutex<Option<barrier::Receiver>>, output_done_rx: Mutex<Option<barrier::Receiver>>,
root_path: PathBuf, root_path: PathBuf,
options: Option<Value>,
} }
pub struct Subscription { pub struct Subscription {
method: &'static str, method: &'static str,
notification_handlers: Arc<RwLock<HashMap<&'static str, NotificationHandler>>>, notification_handlers: Arc<Mutex<HashMap<&'static str, NotificationHandler>>>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -61,18 +59,6 @@ struct Request<'a, T> {
params: 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)] #[derive(Serialize, Deserialize)]
struct AnyResponse<'a> { struct AnyResponse<'a> {
id: usize, id: usize,
@ -85,7 +71,8 @@ struct AnyResponse<'a> {
#[derive(Serialize)] #[derive(Serialize)]
struct Response<T> { struct Response<T> {
id: usize, id: usize,
result: T, result: Option<T>,
error: Option<Error>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -118,15 +105,14 @@ impl LanguageServer {
binary_path: &Path, binary_path: &Path,
args: &[&str], args: &[&str],
root_path: &Path, root_path: &Path,
options: Option<Value>, cx: AsyncAppContext,
background: Arc<executor::Background>,
) -> Result<Self> { ) -> Result<Self> {
let working_dir = if root_path.is_dir() { let working_dir = if root_path.is_dir() {
root_path root_path
} else { } else {
root_path.parent().unwrap_or(Path::new("/")) root_path.parent().unwrap_or(Path::new("/"))
}; };
let mut server = Command::new(binary_path) let mut server = process::Command::new(binary_path)
.current_dir(working_dir) .current_dir(working_dir)
.args(args) .args(args)
.stdin(Stdio::piped()) .stdin(Stdio::piped())
@ -136,95 +122,91 @@ impl LanguageServer {
let stdin = server.stdin.take().unwrap(); let stdin = server.stdin.take().unwrap();
let stdout = server.stdout.take().unwrap(); let stdout = server.stdout.take().unwrap();
let mut server = let mut server =
Self::new_internal(server_id, stdin, stdout, root_path, options, background); Self::new_internal(server_id, stdin, stdout, root_path, cx, |notification| {
log::info!(
"unhandled notification {}:\n{}",
notification.method,
serde_json::to_string_pretty(
&Value::from_str(notification.params.get()).unwrap()
)
.unwrap()
);
});
if let Some(name) = binary_path.file_name() { if let Some(name) = binary_path.file_name() {
server.name = name.to_string_lossy().to_string(); server.name = name.to_string_lossy().to_string();
} }
Ok(server) Ok(server)
} }
fn new_internal<Stdin, Stdout>( fn new_internal<Stdin, Stdout, F>(
server_id: usize, server_id: usize,
stdin: Stdin, stdin: Stdin,
stdout: Stdout, stdout: Stdout,
root_path: &Path, root_path: &Path,
options: Option<Value>, cx: AsyncAppContext,
executor: Arc<executor::Background>, mut on_unhandled_notification: F,
) -> Self ) -> Self
where where
Stdin: AsyncWrite + Unpin + Send + 'static, Stdin: AsyncWrite + Unpin + Send + 'static,
Stdout: AsyncRead + Unpin + Send + 'static, Stdout: AsyncRead + Unpin + Send + 'static,
F: FnMut(AnyNotification) + 'static + Send,
{ {
let mut stdin = BufWriter::new(stdin); let mut stdin = BufWriter::new(stdin);
let mut stdout = BufReader::new(stdout); let mut stdout = BufReader::new(stdout);
let (outbound_tx, outbound_rx) = channel::unbounded::<Vec<u8>>(); let (outbound_tx, outbound_rx) = channel::unbounded::<Vec<u8>>();
let notification_handlers = let notification_handlers =
Arc::new(RwLock::new(HashMap::<_, NotificationHandler>::default())); Arc::new(Mutex::new(HashMap::<_, NotificationHandler>::default()));
let response_handlers = Arc::new(Mutex::new(HashMap::<_, ResponseHandler>::default())); let response_handlers = Arc::new(Mutex::new(HashMap::<_, ResponseHandler>::default()));
let input_task = executor.spawn( let input_task = cx.spawn(|cx| {
{ let notification_handlers = notification_handlers.clone();
let notification_handlers = notification_handlers.clone(); let response_handlers = response_handlers.clone();
let response_handlers = response_handlers.clone(); async move {
let mut outbound_tx = outbound_tx.clone(); let _clear_response_handlers = ClearResponseHandlers(response_handlers.clone());
async move { let mut buffer = Vec::new();
let _clear_response_handlers = ClearResponseHandlers(response_handlers.clone()); loop {
let mut buffer = Vec::new(); buffer.clear();
loop { stdout.read_until(b'\n', &mut buffer).await?;
buffer.clear(); stdout.read_until(b'\n', &mut buffer).await?;
stdout.read_until(b'\n', &mut buffer).await?; let message_len: usize = std::str::from_utf8(&buffer)?
stdout.read_until(b'\n', &mut buffer).await?; .strip_prefix(CONTENT_LEN_HEADER)
let message_len: usize = std::str::from_utf8(&buffer)? .ok_or_else(|| anyhow!("invalid header"))?
.strip_prefix(CONTENT_LEN_HEADER) .trim_end()
.ok_or_else(|| anyhow!("invalid header"))? .parse()?;
.trim_end()
.parse()?;
buffer.resize(message_len, 0); buffer.resize(message_len, 0);
stdout.read_exact(&mut buffer).await?; stdout.read_exact(&mut buffer).await?;
log::trace!("incoming message:{}", String::from_utf8_lossy(&buffer)); log::trace!("incoming message:{}", String::from_utf8_lossy(&buffer));
if let Ok(AnyNotification { id, method, params }) = if let Ok(msg) = serde_json::from_slice::<AnyNotification>(&buffer) {
serde_json::from_slice(&buffer) if let Some(handler) = notification_handlers.lock().get_mut(msg.method) {
{ handler(msg.id, msg.params.get(), cx.clone());
if let Some(handler) = notification_handlers.write().get_mut(method) {
if let Err(e) = handler(id, params.get(), &mut outbound_tx) {
log::error!("error handling {} message: {:?}", method, e);
}
} else {
log::info!(
"unhandled notification {}:\n{}",
method,
serde_json::to_string_pretty(
&Value::from_str(params.get()).unwrap()
)
.unwrap()
);
}
} else if let Ok(AnyResponse { id, error, result }) =
serde_json::from_slice(&buffer)
{
if let Some(handler) = response_handlers.lock().remove(&id) {
if let Some(error) = error {
handler(Err(error));
} else if let Some(result) = result {
handler(Ok(result.get()));
} else {
handler(Ok("null"));
}
}
} else { } else {
return Err(anyhow!( on_unhandled_notification(msg);
"failed to deserialize message:\n{}",
std::str::from_utf8(&buffer)?
));
} }
} else if let Ok(AnyResponse { id, error, result }) =
serde_json::from_slice(&buffer)
{
if let Some(handler) = response_handlers.lock().remove(&id) {
if let Some(error) = error {
handler(Err(error));
} else if let Some(result) = result {
handler(Ok(result.get()));
} else {
handler(Ok("null"));
}
}
} else {
return Err(anyhow!(
"failed to deserialize message:\n{}",
std::str::from_utf8(&buffer)?
));
} }
} }
} }
.log_err(), .log_err()
); });
let (output_done_tx, output_done_rx) = barrier::channel(); let (output_done_tx, output_done_rx) = barrier::channel();
let output_task = executor.spawn({ let output_task = cx.background().spawn({
let response_handlers = response_handlers.clone(); let response_handlers = response_handlers.clone();
async move { async move {
let _clear_response_handlers = ClearResponseHandlers(response_handlers); let _clear_response_handlers = ClearResponseHandlers(response_handlers);
@ -253,18 +235,15 @@ impl LanguageServer {
capabilities: Default::default(), capabilities: Default::default(),
next_id: Default::default(), next_id: Default::default(),
outbound_tx, outbound_tx,
executor: executor.clone(), executor: cx.background().clone(),
io_tasks: Mutex::new(Some((input_task, output_task))), io_tasks: Mutex::new(Some((input_task, output_task))),
output_done_rx: Mutex::new(Some(output_done_rx)), output_done_rx: Mutex::new(Some(output_done_rx)),
root_path: root_path.to_path_buf(), root_path: root_path.to_path_buf(),
options,
} }
} }
pub async fn initialize(mut self) -> Result<Arc<Self>> { pub async fn initialize(mut self, options: Option<Value>) -> Result<Arc<Self>> {
let options = self.options.take(); let root_uri = Url::from_file_path(&self.root_path).unwrap();
let mut this = Arc::new(self);
let root_uri = Url::from_file_path(&this.root_path).unwrap();
#[allow(deprecated)] #[allow(deprecated)]
let params = InitializeParams { let params = InitializeParams {
process_id: Default::default(), process_id: Default::default(),
@ -290,12 +269,13 @@ impl LanguageServer {
value_set: vec![ value_set: vec![
CodeActionKind::REFACTOR.as_str().into(), CodeActionKind::REFACTOR.as_str().into(),
CodeActionKind::QUICKFIX.as_str().into(), CodeActionKind::QUICKFIX.as_str().into(),
CodeActionKind::SOURCE.as_str().into(),
], ],
}, },
}), }),
data_support: Some(true), data_support: Some(true),
resolve_support: Some(CodeActionCapabilityResolveSupport { resolve_support: Some(CodeActionCapabilityResolveSupport {
properties: vec!["edit".to_string()], properties: vec!["edit".to_string(), "command".to_string()],
}), }),
..Default::default() ..Default::default()
}), }),
@ -326,16 +306,14 @@ impl LanguageServer {
locale: Default::default(), locale: Default::default(),
}; };
let response = this.request::<request::Initialize>(params).await?; let response = self.request::<request::Initialize>(params).await?;
{ if let Some(info) = response.server_info {
let this = Arc::get_mut(&mut this).unwrap(); self.name = info.name;
if let Some(info) = response.server_info {
this.name = info.name;
}
this.capabilities = response.capabilities;
} }
this.notify::<notification::Initialized>(InitializedParams {})?; self.capabilities = response.capabilities;
Ok(this)
self.notify::<notification::Initialized>(InitializedParams {})?;
Ok(Arc::new(self))
} }
pub fn shutdown(&self) -> Option<impl 'static + Send + Future<Output = Option<()>>> { pub fn shutdown(&self) -> Option<impl 'static + Send + Future<Output = Option<()>>> {
@ -370,37 +348,42 @@ impl LanguageServer {
} }
} }
pub fn on_notification<T, F>(&mut self, f: F) -> Subscription #[must_use]
pub fn on_notification<T, F>(&self, f: F) -> Subscription
where where
T: notification::Notification, T: notification::Notification,
F: 'static + Send + Sync + FnMut(T::Params), F: 'static + Send + FnMut(T::Params, AsyncAppContext),
{ {
self.on_custom_notification(T::METHOD, f) self.on_custom_notification(T::METHOD, f)
} }
pub fn on_request<T, F>(&mut self, f: F) -> Subscription #[must_use]
pub fn on_request<T, F, Fut>(&self, f: F) -> Subscription
where where
T: request::Request, T: request::Request,
F: 'static + Send + Sync + FnMut(T::Params) -> Result<T::Result>, T::Params: 'static + Send,
F: 'static + Send + FnMut(T::Params, AsyncAppContext) -> Fut,
Fut: 'static + Future<Output = Result<T::Result>>,
{ {
self.on_custom_request(T::METHOD, f) self.on_custom_request(T::METHOD, f)
} }
pub fn on_custom_notification<Params, F>( pub fn remove_request_handler<T: request::Request>(&self) {
&mut self, self.notification_handlers.lock().remove(T::METHOD);
method: &'static str, }
mut f: F,
) -> Subscription #[must_use]
pub fn on_custom_notification<Params, F>(&self, method: &'static str, mut f: F) -> Subscription
where where
F: 'static + Send + Sync + FnMut(Params), F: 'static + Send + FnMut(Params, AsyncAppContext),
Params: DeserializeOwned, Params: DeserializeOwned,
{ {
let prev_handler = self.notification_handlers.write().insert( let prev_handler = self.notification_handlers.lock().insert(
method, method,
Box::new(move |_, params, _| { Box::new(move |_, params, cx| {
let params = serde_json::from_str(params)?; if let Some(params) = serde_json::from_str(params).log_err() {
f(params); f(params, cx);
Ok(()) }
}), }),
); );
assert!( assert!(
@ -413,26 +396,52 @@ impl LanguageServer {
} }
} }
pub fn on_custom_request<Params, Res, F>( #[must_use]
&mut self, pub fn on_custom_request<Params, Res, Fut, F>(
&self,
method: &'static str, method: &'static str,
mut f: F, mut f: F,
) -> Subscription ) -> Subscription
where where
F: 'static + Send + Sync + FnMut(Params) -> Result<Res>, F: 'static + Send + FnMut(Params, AsyncAppContext) -> Fut,
Params: DeserializeOwned, Fut: 'static + Future<Output = Result<Res>>,
Params: DeserializeOwned + Send + 'static,
Res: Serialize, Res: Serialize,
{ {
let prev_handler = self.notification_handlers.write().insert( let outbound_tx = self.outbound_tx.clone();
let prev_handler = self.notification_handlers.lock().insert(
method, method,
Box::new(move |id, params, tx| { Box::new(move |id, params, cx| {
if let Some(id) = id { if let Some(id) = id {
let params = serde_json::from_str(params)?; if let Some(params) = serde_json::from_str(params).log_err() {
let result = f(params)?; let response = f(params, cx.clone());
let response = serde_json::to_vec(&Response { id, result })?; cx.foreground()
tx.try_send(response)?; .spawn({
let outbound_tx = outbound_tx.clone();
async move {
let response = match response.await {
Ok(result) => Response {
id,
result: Some(result),
error: None,
},
Err(error) => Response {
id,
result: None,
error: Some(Error {
message: error.to_string(),
}),
},
};
if let Some(response) = serde_json::to_vec(&response).log_err()
{
outbound_tx.try_send(response).ok();
}
}
})
.detach();
}
} }
Ok(())
}), }),
); );
assert!( assert!(
@ -458,7 +467,7 @@ impl LanguageServer {
} }
pub fn request<T: request::Request>( pub fn request<T: request::Request>(
self: &Arc<Self>, &self,
params: T::Params, params: T::Params,
) -> impl Future<Output = Result<T::Result>> ) -> impl Future<Output = Result<T::Result>>
where where
@ -549,36 +558,16 @@ impl Subscription {
impl Drop for Subscription { impl Drop for Subscription {
fn drop(&mut self) { fn drop(&mut self) {
self.notification_handlers.write().remove(self.method); self.notification_handlers.lock().remove(self.method);
} }
} }
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
pub struct FakeLanguageServer { pub struct FakeLanguageServer {
handlers: FakeLanguageServerHandlers, server: Arc<LanguageServer>,
outgoing_tx: futures::channel::mpsc::UnboundedSender<Vec<u8>>, notifications_rx: channel::Receiver<(String, String)>,
incoming_rx: futures::channel::mpsc::UnboundedReceiver<Vec<u8>>,
_input_task: Task<Result<()>>,
_output_task: Task<Result<()>>,
} }
#[cfg(any(test, feature = "test-support"))]
type FakeLanguageServerHandlers = Arc<
Mutex<
HashMap<
&'static str,
Box<
dyn Send
+ FnMut(
usize,
&[u8],
gpui::AsyncAppContext,
) -> futures::future::BoxFuture<'static, Vec<u8>>,
>,
>,
>,
>;
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
impl LanguageServer { impl LanguageServer {
pub fn full_capabilities() -> ServerCapabilities { pub fn full_capabilities() -> ServerCapabilities {
@ -591,177 +580,101 @@ impl LanguageServer {
} }
} }
pub fn fake(cx: &mut gpui::MutableAppContext) -> (Self, FakeLanguageServer) { pub fn fake(cx: AsyncAppContext) -> (Self, FakeLanguageServer) {
Self::fake_with_capabilities(Self::full_capabilities(), cx) Self::fake_with_capabilities(Self::full_capabilities(), cx)
} }
pub fn fake_with_capabilities( pub fn fake_with_capabilities(
capabilities: ServerCapabilities, capabilities: ServerCapabilities,
cx: &mut gpui::MutableAppContext, cx: AsyncAppContext,
) -> (Self, FakeLanguageServer) { ) -> (Self, FakeLanguageServer) {
let (stdin_writer, stdin_reader) = async_pipe::pipe(); let (stdin_writer, stdin_reader) = async_pipe::pipe();
let (stdout_writer, stdout_reader) = async_pipe::pipe(); let (stdout_writer, stdout_reader) = async_pipe::pipe();
let (notifications_tx, notifications_rx) = channel::unbounded();
let mut fake = FakeLanguageServer::new(stdin_reader, stdout_writer, cx);
fake.handle_request::<request::Initialize, _, _>({
let capabilities = capabilities.clone();
move |_, _| {
let capabilities = capabilities.clone();
async move {
InitializeResult {
capabilities,
..Default::default()
}
}
}
});
let executor = cx.background().clone();
let server = Self::new_internal( let server = Self::new_internal(
0, 0,
stdin_writer, stdin_writer,
stdout_reader, stdout_reader,
Path::new("/"), Path::new("/"),
None, cx.clone(),
executor, |_| {},
); );
let fake = FakeLanguageServer {
server: Arc::new(Self::new_internal(
0,
stdout_writer,
stdin_reader,
Path::new("/"),
cx.clone(),
move |msg| {
notifications_tx
.try_send((msg.method.to_string(), msg.params.get().to_string()))
.ok();
},
)),
notifications_rx,
};
fake.handle_request::<request::Initialize, _, _>({
let capabilities = capabilities.clone();
move |_, _| {
let capabilities = capabilities.clone();
async move {
Ok(InitializeResult {
capabilities,
..Default::default()
})
}
}
});
(server, fake) (server, fake)
} }
} }
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
impl FakeLanguageServer { impl FakeLanguageServer {
fn new( pub fn notify<T: notification::Notification>(&self, params: T::Params) {
stdin: async_pipe::PipeReader, self.server.notify::<T>(params).ok();
stdout: async_pipe::PipeWriter,
cx: &mut gpui::MutableAppContext,
) -> Self {
use futures::StreamExt as _;
let (incoming_tx, incoming_rx) = futures::channel::mpsc::unbounded();
let (outgoing_tx, mut outgoing_rx) = futures::channel::mpsc::unbounded();
let handlers = FakeLanguageServerHandlers::default();
let input_task = cx.spawn(|cx| {
let handlers = handlers.clone();
let outgoing_tx = outgoing_tx.clone();
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() {
cx.background().simulate_random_delay().await;
if let Ok(request) = serde_json::from_slice::<AnyRequest>(&buffer) {
assert_eq!(request.jsonrpc, JSON_RPC_VERSION);
let response;
if let Some(handler) = handlers.lock().get_mut(request.method) {
response =
handler(request.id, request.params.get().as_bytes(), cx.clone())
.await;
log::debug!("handled lsp request. method:{}", request.method);
} else {
response = serde_json::to_vec(&AnyResponse {
id: request.id,
error: Some(Error {
message: "no handler".to_string(),
}),
result: None,
})
.unwrap();
log::debug!("unhandled lsp request. method:{}", request.method);
}
outgoing_tx.unbounded_send(response)?;
} else {
incoming_tx.unbounded_send(buffer.clone())?;
}
}
Ok::<_, anyhow::Error>(())
}
});
let output_task = cx.background().spawn(async move {
let mut stdout = smol::io::BufWriter::new(stdout);
while let Some(message) = outgoing_rx.next().await {
stdout.write_all(CONTENT_LEN_HEADER.as_bytes()).await?;
stdout
.write_all((format!("{}", message.len())).as_bytes())
.await?;
stdout.write_all("\r\n\r\n".as_bytes()).await?;
stdout.write_all(&message).await?;
stdout.flush().await?;
}
Ok(())
});
Self {
outgoing_tx,
incoming_rx,
handlers,
_input_task: input_task,
_output_task: output_task,
}
}
pub fn notify<T: notification::Notification>(&mut self, params: T::Params) {
let message = serde_json::to_vec(&Notification {
jsonrpc: JSON_RPC_VERSION,
method: T::METHOD,
params,
})
.unwrap();
self.outgoing_tx.unbounded_send(message).unwrap();
} }
pub async fn receive_notification<T: notification::Notification>(&mut self) -> T::Params { pub async fn receive_notification<T: notification::Notification>(&mut self) -> T::Params {
use futures::StreamExt as _; use futures::StreamExt as _;
loop { loop {
let bytes = self.incoming_rx.next().await.unwrap(); let (method, params) = self.notifications_rx.next().await.unwrap();
if let Ok(notification) = serde_json::from_slice::<Notification<T::Params>>(&bytes) { if &method == T::METHOD {
assert_eq!(notification.method, T::METHOD); return serde_json::from_str::<T::Params>(&params).unwrap();
return notification.params;
} else { } else {
log::info!( log::info!("skipping message in fake language server {:?}", params);
"skipping message in fake language server {:?}",
std::str::from_utf8(&bytes)
);
} }
} }
} }
pub fn handle_request<T, F, Fut>( pub fn handle_request<T, F, Fut>(
&mut self, &self,
mut handler: F, mut handler: F,
) -> futures::channel::mpsc::UnboundedReceiver<()> ) -> futures::channel::mpsc::UnboundedReceiver<()>
where where
T: 'static + request::Request, T: 'static + request::Request,
T::Params: 'static + Send,
F: 'static + Send + FnMut(T::Params, gpui::AsyncAppContext) -> Fut, F: 'static + Send + FnMut(T::Params, gpui::AsyncAppContext) -> Fut,
Fut: 'static + Send + Future<Output = T::Result>, Fut: 'static + Send + Future<Output = Result<T::Result>>,
{ {
use futures::FutureExt as _;
let (responded_tx, responded_rx) = futures::channel::mpsc::unbounded(); let (responded_tx, responded_rx) = futures::channel::mpsc::unbounded();
self.handlers.lock().insert( self.server.remove_request_handler::<T>();
T::METHOD, self.server
Box::new(move |id, params, cx| { .on_request::<T, _, _>(move |params, cx| {
let result = handler(serde_json::from_slice::<T::Params>(params).unwrap(), cx); let result = handler(params, cx.clone());
let responded_tx = responded_tx.clone(); let responded_tx = responded_tx.clone();
async move { async move {
cx.background().simulate_random_delay().await;
let result = result.await; let result = result.await;
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),
};
responded_tx.unbounded_send(()).ok(); responded_tx.unbounded_send(()).ok();
serde_json::to_vec(&response).unwrap() result
} }
.boxed() })
}), .detach();
);
responded_rx responded_rx
} }
@ -769,7 +682,7 @@ impl FakeLanguageServer {
where where
T: 'static + request::Request, T: 'static + request::Request,
{ {
self.handlers.lock().remove(T::METHOD); self.server.remove_request_handler::<T>();
} }
pub async fn start_progress(&mut self, token: impl Into<String>) { pub async fn start_progress(&mut self, token: impl Into<String>) {
@ -785,25 +698,6 @@ impl FakeLanguageServer {
value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(Default::default())), value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(Default::default())),
}); });
} }
async fn receive(
stdin: &mut smol::io::BufReader<async_pipe::PipeReader>,
buffer: &mut Vec<u8>,
) -> 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)
.ok_or_else(|| anyhow!("invalid content length header"))?
.trim_end()
.parse()
.unwrap();
buffer.resize(message_len, 0);
stdin.read_exact(buffer).await?;
Ok(())
}
} }
struct ClearResponseHandlers(Arc<Mutex<HashMap<usize, ResponseHandler>>>); struct ClearResponseHandlers(Arc<Mutex<HashMap<usize, ResponseHandler>>>);
@ -828,22 +722,22 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_fake(cx: &mut TestAppContext) { async fn test_fake(cx: &mut TestAppContext) {
let (mut server, mut fake) = cx.update(LanguageServer::fake); let (server, mut fake) = LanguageServer::fake(cx.to_async());
let (message_tx, message_rx) = channel::unbounded(); let (message_tx, message_rx) = channel::unbounded();
let (diagnostics_tx, diagnostics_rx) = channel::unbounded(); let (diagnostics_tx, diagnostics_rx) = channel::unbounded();
server server
.on_notification::<notification::ShowMessage, _>(move |params| { .on_notification::<notification::ShowMessage, _>(move |params, _| {
message_tx.try_send(params).unwrap() message_tx.try_send(params).unwrap()
}) })
.detach(); .detach();
server server
.on_notification::<notification::PublishDiagnostics, _>(move |params| { .on_notification::<notification::PublishDiagnostics, _>(move |params, _| {
diagnostics_tx.try_send(params).unwrap() diagnostics_tx.try_send(params).unwrap()
}) })
.detach(); .detach();
let server = server.initialize().await.unwrap(); let server = server.initialize(None).await.unwrap();
server server
.notify::<notification::DidOpenTextDocument>(DidOpenTextDocumentParams { .notify::<notification::DidOpenTextDocument>(DidOpenTextDocumentParams {
text_document: TextDocumentItem::new( text_document: TextDocumentItem::new(
@ -878,7 +772,7 @@ mod tests {
"file://b/c" "file://b/c"
); );
fake.handle_request::<request::Shutdown, _, _>(|_, _| async move {}); fake.handle_request::<request::Shutdown, _, _>(|_, _| async move { Ok(()) });
drop(server); drop(server);
fake.receive_notification::<notification::Exit>().await; fake.receive_notification::<notification::Exit>().await;

View file

@ -1325,7 +1325,7 @@ impl Project {
cx, cx,
); );
cx.spawn_weak(|this, mut cx| async move { cx.spawn_weak(|this, mut cx| async move {
let mut language_server = language_server?.await.log_err()?; let language_server = language_server?.await.log_err()?;
let this = this.upgrade(&cx)?; let this = this.upgrade(&cx)?;
let (language_server_events_tx, language_server_events_rx) = let (language_server_events_tx, language_server_events_rx) =
smol::channel::unbounded(); smol::channel::unbounded();
@ -1333,7 +1333,7 @@ impl Project {
language_server language_server
.on_notification::<lsp::notification::PublishDiagnostics, _>({ .on_notification::<lsp::notification::PublishDiagnostics, _>({
let language_server_events_tx = language_server_events_tx.clone(); let language_server_events_tx = language_server_events_tx.clone();
move |params| { move |params, _| {
language_server_events_tx language_server_events_tx
.try_send(LanguageServerEvent::DiagnosticsUpdate(params)) .try_send(LanguageServerEvent::DiagnosticsUpdate(params))
.ok(); .ok();
@ -1342,31 +1342,33 @@ impl Project {
.detach(); .detach();
language_server language_server
.on_request::<lsp::request::WorkspaceConfiguration, _>({ .on_request::<lsp::request::WorkspaceConfiguration, _, _>({
let settings = this let settings = this
.read_with(&cx, |this, _| this.language_server_settings.clone()); .read_with(&cx, |this, _| this.language_server_settings.clone());
move |params| { move |params, _| {
let settings = settings.lock(); let settings = settings.lock().clone();
Ok(params async move {
.items Ok(params
.into_iter() .items
.map(|item| { .into_iter()
if let Some(section) = &item.section { .map(|item| {
settings if let Some(section) = &item.section {
.get(section) settings
.cloned() .get(section)
.unwrap_or(serde_json::Value::Null) .cloned()
} else { .unwrap_or(serde_json::Value::Null)
settings.clone() } else {
} settings.clone()
}) }
.collect()) })
.collect())
}
} }
}) })
.detach(); .detach();
language_server language_server
.on_notification::<lsp::notification::Progress, _>(move |params| { .on_notification::<lsp::notification::Progress, _>(move |params, _| {
let token = match params.token { let token = match params.token {
lsp::NumberOrString::String(token) => token, lsp::NumberOrString::String(token) => token,
lsp::NumberOrString::Number(token) => { lsp::NumberOrString::Number(token) => {
@ -1406,6 +1408,11 @@ impl Project {
}) })
.detach(); .detach();
let language_server = language_server
.initialize(adapter.initialization_options())
.await
.log_err()?;
// Process all the LSP events. // Process all the LSP events.
cx.spawn(|mut cx| { cx.spawn(|mut cx| {
let this = this.downgrade(); let this = this.downgrade();
@ -1424,7 +1431,6 @@ impl Project {
}) })
.detach(); .detach();
let language_server = language_server.initialize().await.log_err()?;
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
this.language_servers this.language_servers
.insert(key.clone(), (adapter, language_server.clone())); .insert(key.clone(), (adapter, language_server.clone()));
@ -4974,9 +4980,9 @@ mod tests {
}); });
let mut rust_shutdown_requests = fake_rust_server let mut rust_shutdown_requests = fake_rust_server
.handle_request::<lsp::request::Shutdown, _, _>(|_, _| future::ready(())); .handle_request::<lsp::request::Shutdown, _, _>(|_, _| future::ready(Ok(())));
let mut json_shutdown_requests = fake_json_server let mut json_shutdown_requests = fake_json_server
.handle_request::<lsp::request::Shutdown, _, _>(|_, _| future::ready(())); .handle_request::<lsp::request::Shutdown, _, _>(|_, _| future::ready(Ok(())));
futures::join!(rust_shutdown_requests.next(), json_shutdown_requests.next()); futures::join!(rust_shutdown_requests.next(), json_shutdown_requests.next());
let mut fake_rust_server = fake_rust_servers.next().await.unwrap(); let mut fake_rust_server = fake_rust_servers.next().await.unwrap();
@ -5917,19 +5923,11 @@ mod tests {
.await; .await;
let buffer = project let buffer = project
.update(cx, |project, cx| { .update(cx, |project, cx| project.open_buffer((worktree_id, ""), cx))
project.open_buffer(
ProjectPath {
worktree_id,
path: Path::new("").into(),
},
cx,
)
})
.await .await
.unwrap(); .unwrap();
let mut fake_server = fake_servers.next().await.unwrap(); let fake_server = fake_servers.next().await.unwrap();
fake_server.handle_request::<lsp::request::GotoDefinition, _, _>(|params, _| async move { fake_server.handle_request::<lsp::request::GotoDefinition, _, _>(|params, _| async move {
let params = params.text_document_position_params; let params = params.text_document_position_params;
assert_eq!( assert_eq!(
@ -5938,9 +5936,11 @@ mod tests {
); );
assert_eq!(params.position, lsp::Position::new(0, 22)); assert_eq!(params.position, lsp::Position::new(0, 22));
Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location::new( Ok(Some(lsp::GotoDefinitionResponse::Scalar(
lsp::Url::from_file_path("/dir/a.rs").unwrap(), lsp::Location::new(
lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)), lsp::Url::from_file_path("/dir/a.rs").unwrap(),
lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
),
))) )))
}); });
@ -6854,7 +6854,7 @@ mod tests {
.await .await
.unwrap(); .unwrap();
let mut fake_server = fake_servers.next().await.unwrap(); let fake_server = fake_servers.next().await.unwrap();
let response = project.update(cx, |project, cx| { let response = project.update(cx, |project, cx| {
project.prepare_rename(buffer.clone(), 7, cx) project.prepare_rename(buffer.clone(), 7, cx)
@ -6863,10 +6863,10 @@ mod tests {
.handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move { .handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs"); assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
assert_eq!(params.position, lsp::Position::new(0, 7)); assert_eq!(params.position, lsp::Position::new(0, 7));
Some(lsp::PrepareRenameResponse::Range(lsp::Range::new( Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
lsp::Position::new(0, 6), lsp::Position::new(0, 6),
lsp::Position::new(0, 9), lsp::Position::new(0, 9),
))) ))))
}) })
.next() .next()
.await .await
@ -6889,7 +6889,7 @@ mod tests {
lsp::Position::new(0, 7) lsp::Position::new(0, 7)
); );
assert_eq!(params.new_name, "THREE"); assert_eq!(params.new_name, "THREE");
Some(lsp::WorkspaceEdit { Ok(Some(lsp::WorkspaceEdit {
changes: Some( changes: Some(
[ [
( (
@ -6926,7 +6926,7 @@ mod tests {
.collect(), .collect(),
), ),
..Default::default() ..Default::default()
}) }))
}) })
.next() .next()
.await .await

View file

@ -2342,7 +2342,7 @@ mod tests {
Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx) Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
}); });
let mut fake_language_server = fake_language_servers.next().await.unwrap(); let fake_language_server = fake_language_servers.next().await.unwrap();
buffer_b buffer_b
.condition(&cx_b, |buffer, _| !buffer.completion_triggers().is_empty()) .condition(&cx_b, |buffer, _| !buffer.completion_triggers().is_empty())
.await; .await;
@ -2368,7 +2368,7 @@ mod tests {
lsp::Position::new(0, 14), lsp::Position::new(0, 14),
); );
Some(lsp::CompletionResponse::Array(vec![ Ok(Some(lsp::CompletionResponse::Array(vec![
lsp::CompletionItem { lsp::CompletionItem {
label: "first_method(…)".into(), label: "first_method(…)".into(),
detail: Some("fn(&mut self, B) -> C".into()), detail: Some("fn(&mut self, B) -> C".into()),
@ -2395,7 +2395,7 @@ mod tests {
insert_text_format: Some(lsp::InsertTextFormat::SNIPPET), insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
..Default::default() ..Default::default()
}, },
])) ])))
}) })
.next() .next()
.await .await
@ -2425,7 +2425,7 @@ mod tests {
fake_language_server.handle_request::<lsp::request::ResolveCompletionItem, _, _>( fake_language_server.handle_request::<lsp::request::ResolveCompletionItem, _, _>(
|params, _| async move { |params, _| async move {
assert_eq!(params.label, "first_method(…)"); assert_eq!(params.label, "first_method(…)");
lsp::CompletionItem { Ok(lsp::CompletionItem {
label: "first_method(…)".into(), label: "first_method(…)".into(),
detail: Some("fn(&mut self, B) -> C".into()), detail: Some("fn(&mut self, B) -> C".into()),
text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit { text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
@ -2441,7 +2441,7 @@ mod tests {
}]), }]),
insert_text_format: Some(lsp::InsertTextFormat::SNIPPET), insert_text_format: Some(lsp::InsertTextFormat::SNIPPET),
..Default::default() ..Default::default()
} })
}, },
); );
@ -2530,9 +2530,9 @@ mod tests {
.await .await
.unwrap(); .unwrap();
let mut fake_language_server = fake_language_servers.next().await.unwrap(); let fake_language_server = fake_language_servers.next().await.unwrap();
fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move { fake_language_server.handle_request::<lsp::request::Formatting, _, _>(|_, _| async move {
Some(vec![ Ok(Some(vec![
lsp::TextEdit { lsp::TextEdit {
range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)), range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 4)),
new_text: "h".to_string(), new_text: "h".to_string(),
@ -2541,7 +2541,7 @@ mod tests {
range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)), range: lsp::Range::new(lsp::Position::new(0, 7), lsp::Position::new(0, 7)),
new_text: "y".to_string(), new_text: "y".to_string(),
}, },
]) ]))
}); });
project_b project_b
@ -2637,12 +2637,14 @@ mod tests {
.unwrap(); .unwrap();
// Request the definition of a symbol as the guest. // Request the definition of a symbol as the guest.
let mut fake_language_server = fake_language_servers.next().await.unwrap(); let fake_language_server = fake_language_servers.next().await.unwrap();
fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>( fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(
|_, _| async move { |_, _| async move {
Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location::new( Ok(Some(lsp::GotoDefinitionResponse::Scalar(
lsp::Url::from_file_path("/root-2/b.rs").unwrap(), lsp::Location::new(
lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)), lsp::Url::from_file_path("/root-2/b.rs").unwrap(),
lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
),
))) )))
}, },
); );
@ -2669,9 +2671,11 @@ mod tests {
// the previous call to `definition`. // the previous call to `definition`.
fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>( fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(
|_, _| async move { |_, _| async move {
Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location::new( Ok(Some(lsp::GotoDefinitionResponse::Scalar(
lsp::Url::from_file_path("/root-2/b.rs").unwrap(), lsp::Location::new(
lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)), lsp::Url::from_file_path("/root-2/b.rs").unwrap(),
lsp::Range::new(lsp::Position::new(1, 6), lsp::Position::new(1, 11)),
),
))) )))
}, },
); );
@ -2778,14 +2782,14 @@ mod tests {
.unwrap(); .unwrap();
// Request references to a symbol as the guest. // Request references to a symbol as the guest.
let mut fake_language_server = fake_language_servers.next().await.unwrap(); let fake_language_server = fake_language_servers.next().await.unwrap();
fake_language_server.handle_request::<lsp::request::References, _, _>( fake_language_server.handle_request::<lsp::request::References, _, _>(
|params, _| async move { |params, _| async move {
assert_eq!( assert_eq!(
params.text_document_position.text_document.uri.as_str(), params.text_document_position.text_document.uri.as_str(),
"file:///root-1/one.rs" "file:///root-1/one.rs"
); );
Some(vec![ Ok(Some(vec![
lsp::Location { lsp::Location {
uri: lsp::Url::from_file_path("/root-1/two.rs").unwrap(), uri: lsp::Url::from_file_path("/root-1/two.rs").unwrap(),
range: lsp::Range::new( range: lsp::Range::new(
@ -2807,7 +2811,7 @@ mod tests {
lsp::Position::new(0, 40), lsp::Position::new(0, 40),
), ),
}, },
]) ]))
}, },
); );
@ -3018,7 +3022,7 @@ mod tests {
.unwrap(); .unwrap();
// Request document highlights as the guest. // Request document highlights as the guest.
let mut fake_language_server = fake_language_servers.next().await.unwrap(); let fake_language_server = fake_language_servers.next().await.unwrap();
fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>( fake_language_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>(
|params, _| async move { |params, _| async move {
assert_eq!( assert_eq!(
@ -3033,7 +3037,7 @@ mod tests {
params.text_document_position_params.position, params.text_document_position_params.position,
lsp::Position::new(0, 34) lsp::Position::new(0, 34)
); );
Some(vec![ Ok(Some(vec![
lsp::DocumentHighlight { lsp::DocumentHighlight {
kind: Some(lsp::DocumentHighlightKind::WRITE), kind: Some(lsp::DocumentHighlightKind::WRITE),
range: lsp::Range::new( range: lsp::Range::new(
@ -3055,7 +3059,7 @@ mod tests {
lsp::Position::new(0, 47), lsp::Position::new(0, 47),
), ),
}, },
]) ]))
}, },
); );
@ -3162,11 +3166,11 @@ mod tests {
.await .await
.unwrap(); .unwrap();
let mut fake_language_server = fake_language_servers.next().await.unwrap(); let fake_language_server = fake_language_servers.next().await.unwrap();
fake_language_server.handle_request::<lsp::request::WorkspaceSymbol, _, _>( fake_language_server.handle_request::<lsp::request::WorkspaceSymbol, _, _>(
|_, _| async move { |_, _| async move {
#[allow(deprecated)] #[allow(deprecated)]
Some(vec![lsp::SymbolInformation { Ok(Some(vec![lsp::SymbolInformation {
name: "TWO".into(), name: "TWO".into(),
location: lsp::Location { location: lsp::Location {
uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(), uri: lsp::Url::from_file_path("/code/crate-2/two.rs").unwrap(),
@ -3176,7 +3180,7 @@ mod tests {
tags: None, tags: None,
container_name: None, container_name: None,
deprecated: None, deprecated: None,
}]) }]))
}, },
); );
@ -3292,12 +3296,14 @@ mod tests {
.await .await
.unwrap(); .unwrap();
let mut fake_language_server = fake_language_servers.next().await.unwrap(); let fake_language_server = fake_language_servers.next().await.unwrap();
fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>( fake_language_server.handle_request::<lsp::request::GotoDefinition, _, _>(
|_, _| async move { |_, _| async move {
Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location::new( Ok(Some(lsp::GotoDefinitionResponse::Scalar(
lsp::Url::from_file_path("/root/b.rs").unwrap(), lsp::Location::new(
lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)), lsp::Url::from_file_path("/root/b.rs").unwrap(),
lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)),
),
))) )))
}, },
); );
@ -3413,7 +3419,7 @@ mod tests {
); );
assert_eq!(params.range.start, lsp::Position::new(0, 0)); assert_eq!(params.range.start, lsp::Position::new(0, 0));
assert_eq!(params.range.end, lsp::Position::new(0, 0)); assert_eq!(params.range.end, lsp::Position::new(0, 0));
None Ok(None)
}) })
.next() .next()
.await; .await;
@ -3433,7 +3439,7 @@ mod tests {
assert_eq!(params.range.start, lsp::Position::new(1, 31)); assert_eq!(params.range.start, lsp::Position::new(1, 31));
assert_eq!(params.range.end, lsp::Position::new(1, 31)); assert_eq!(params.range.end, lsp::Position::new(1, 31));
Some(vec![lsp::CodeActionOrCommand::CodeAction( Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
lsp::CodeAction { lsp::CodeAction {
title: "Inline into all callers".to_string(), title: "Inline into all callers".to_string(),
edit: Some(lsp::WorkspaceEdit { edit: Some(lsp::WorkspaceEdit {
@ -3475,7 +3481,7 @@ mod tests {
})), })),
..Default::default() ..Default::default()
}, },
)]) )]))
}) })
.next() .next()
.await; .await;
@ -3498,7 +3504,7 @@ mod tests {
.unwrap(); .unwrap();
fake_language_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>( fake_language_server.handle_request::<lsp::request::CodeActionResolveRequest, _, _>(
|_, _| async move { |_, _| async move {
lsp::CodeAction { Ok(lsp::CodeAction {
title: "Inline into all callers".to_string(), title: "Inline into all callers".to_string(),
edit: Some(lsp::WorkspaceEdit { edit: Some(lsp::WorkspaceEdit {
changes: Some( changes: Some(
@ -3530,7 +3536,7 @@ mod tests {
..Default::default() ..Default::default()
}), }),
..Default::default() ..Default::default()
} })
}, },
); );
@ -3637,7 +3643,7 @@ mod tests {
.unwrap() .unwrap()
.downcast::<Editor>() .downcast::<Editor>()
.unwrap(); .unwrap();
let mut fake_language_server = fake_language_servers.next().await.unwrap(); let fake_language_server = fake_language_servers.next().await.unwrap();
// Move cursor to a location that can be renamed. // Move cursor to a location that can be renamed.
let prepare_rename = editor_b.update(cx_b, |editor, cx| { let prepare_rename = editor_b.update(cx_b, |editor, cx| {
@ -3649,10 +3655,10 @@ mod tests {
.handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move { .handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs"); assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
assert_eq!(params.position, lsp::Position::new(0, 7)); assert_eq!(params.position, lsp::Position::new(0, 7));
Some(lsp::PrepareRenameResponse::Range(lsp::Range::new( Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
lsp::Position::new(0, 6), lsp::Position::new(0, 6),
lsp::Position::new(0, 9), lsp::Position::new(0, 9),
))) ))))
}) })
.next() .next()
.await .await
@ -3686,7 +3692,7 @@ mod tests {
lsp::Position::new(0, 6) lsp::Position::new(0, 6)
); );
assert_eq!(params.new_name, "THREE"); assert_eq!(params.new_name, "THREE");
Some(lsp::WorkspaceEdit { Ok(Some(lsp::WorkspaceEdit {
changes: Some( changes: Some(
[ [
( (
@ -3723,7 +3729,7 @@ mod tests {
.collect(), .collect(),
), ),
..Default::default() ..Default::default()
}) }))
}) })
.next() .next()
.await .await
@ -4894,36 +4900,38 @@ mod tests {
move |fake_server: &mut FakeLanguageServer| { move |fake_server: &mut FakeLanguageServer| {
fake_server.handle_request::<lsp::request::Completion, _, _>( fake_server.handle_request::<lsp::request::Completion, _, _>(
|_, _| async move { |_, _| async move {
Some(lsp::CompletionResponse::Array(vec![lsp::CompletionItem { Ok(Some(lsp::CompletionResponse::Array(vec![
text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit { lsp::CompletionItem {
range: lsp::Range::new( text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
lsp::Position::new(0, 0), range: lsp::Range::new(
lsp::Position::new(0, 0), lsp::Position::new(0, 0),
), lsp::Position::new(0, 0),
new_text: "the-new-text".to_string(), ),
})), new_text: "the-new-text".to_string(),
..Default::default() })),
}])) ..Default::default()
},
])))
}, },
); );
fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>( fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>(
|_, _| async move { |_, _| async move {
Some(vec![lsp::CodeActionOrCommand::CodeAction( Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
lsp::CodeAction { lsp::CodeAction {
title: "the-code-action".to_string(), title: "the-code-action".to_string(),
..Default::default() ..Default::default()
}, },
)]) )]))
}, },
); );
fake_server.handle_request::<lsp::request::PrepareRenameRequest, _, _>( fake_server.handle_request::<lsp::request::PrepareRenameRequest, _, _>(
|params, _| async move { |params, _| async move {
Some(lsp::PrepareRenameResponse::Range(lsp::Range::new( Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
params.position, params.position,
params.position, params.position,
))) ))))
}, },
); );
@ -4941,7 +4949,7 @@ mod tests {
.map(|_| files.choose(&mut *rng).unwrap()) .map(|_| files.choose(&mut *rng).unwrap())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
log::info!("LSP: Returning definitions in files {:?}", &files); log::info!("LSP: Returning definitions in files {:?}", &files);
Some(lsp::GotoDefinitionResponse::Array( Ok(Some(lsp::GotoDefinitionResponse::Array(
files files
.into_iter() .into_iter()
.map(|file| lsp::Location { .map(|file| lsp::Location {
@ -4949,7 +4957,7 @@ mod tests {
range: Default::default(), range: Default::default(),
}) })
.collect(), .collect(),
)) )))
} }
} }
}); });
@ -4991,7 +4999,7 @@ mod tests {
} else { } else {
None None
}; };
async move { highlights } async move { Ok(highlights) }
} }
}); });
} }