diff --git a/Cargo.lock b/Cargo.lock index 8d9304dfaf31..b2dc4ceeddca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1455,7 +1455,7 @@ dependencies = [ [[package]] name = "lsp-server" -version = "0.9.0" +version = "0.10.0" dependencies = [ "anyhow", "crossbeam-channel", diff --git a/lib/lsp-server/Cargo.toml b/lib/lsp-server/Cargo.toml index 06a452984fc3..1a179ef0812a 100644 --- a/lib/lsp-server/Cargo.toml +++ b/lib/lsp-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lsp-server" -version = "0.9.0" +version = "0.10.0" description = "Generic LSP server scaffold." license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/lsp-server" diff --git a/lib/lsp-server/examples/minimal_lsp.rs b/lib/lsp-server/examples/minimal_lsp.rs index eb0832745560..7efe06355169 100644 --- a/lib/lsp-server/examples/minimal_lsp.rs +++ b/lib/lsp-server/examples/minimal_lsp.rs @@ -82,9 +82,7 @@ use toolchain::command; // clippy-approved wrapper #[allow(clippy::print_stderr, clippy::disallowed_types, clippy::disallowed_methods)] use anyhow::{Context, Result, anyhow, bail}; -use lsp_server::{ - Connection, Message, Request as ServerRequest, RequestId, Response, ResponseKind, -}; +use lsp_server::{Connection, Message, Request as ServerRequest, RequestId, Response}; // ===================================================================== // main @@ -306,8 +304,7 @@ fn full_range(text: &str) -> Range { } fn send_ok(conn: &Connection, id: RequestId, result: &T) -> Result<()> { - let resp = - Response { id, response_kind: ResponseKind::Ok { result: serde_json::to_value(result)? } }; + let resp = Response { id, response_result: Ok(serde_json::to_value(result)?) }; conn.sender.send(Message::Response(resp))?; Ok(()) } @@ -320,9 +317,11 @@ fn send_err( ) -> Result<()> { let resp = Response { id, - response_kind: ResponseKind::Err { - error: lsp_server::ResponseError { code: code as i32, message: msg.into(), data: None }, - }, + response_result: Err(lsp_server::ResponseError { + code: code as i32, + message: msg.into(), + data: None, + }), }; conn.sender.send(Message::Response(resp))?; Ok(()) diff --git a/lib/lsp-server/src/lib.rs b/lib/lsp-server/src/lib.rs index 5eaedbb6429d..3f29c39c49f0 100644 --- a/lib/lsp-server/src/lib.rs +++ b/lib/lsp-server/src/lib.rs @@ -22,9 +22,7 @@ use crossbeam_channel::{Receiver, RecvError, RecvTimeoutError, Sender}; pub use crate::{ error::{ExtractError, ProtocolError}, - msg::{ - ErrorCode, Message, Notification, Request, RequestId, Response, ResponseError, ResponseKind, - }, + msg::{ErrorCode, Message, Notification, Request, RequestId, Response, ResponseError}, req_queue::{Incoming, Outgoing, ReqQueue}, stdio::IoThreads, }; diff --git a/lib/lsp-server/src/msg.rs b/lib/lsp-server/src/msg.rs index 64e2ba750904..19fc106134ef 100644 --- a/lib/lsp-server/src/msg.rs +++ b/lib/lsp-server/src/msg.rs @@ -84,15 +84,17 @@ pub struct Response { // request id. We fail deserialization in that case, so we just // make this field mandatory. pub id: RequestId, - #[serde(flatten)] - pub response_kind: ResponseKind, + #[serde(flatten, with = "ResponseResult")] + pub response_result: Result, } -#[derive(Debug, Serialize, Deserialize, Clone)] -#[serde(untagged)] -pub enum ResponseKind { - Ok { result: serde_json::Value }, - Err { error: ResponseError }, +#[derive(Serialize, Deserialize)] +#[serde(remote = "Result")] +enum ResponseResult { + #[serde(rename = "result")] + Ok(serde_json::Value), + #[serde(rename = "error")] + Err(ResponseError), } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -203,14 +205,11 @@ impl Message { impl Response { pub fn new_ok(id: RequestId, result: R) -> Response { - Response { - id, - response_kind: ResponseKind::Ok { result: serde_json::to_value(result).unwrap() }, - } + Response { id, response_result: Ok(serde_json::to_value(result).unwrap()) } } pub fn new_err(id: RequestId, code: i32, message: String) -> Response { let error = ResponseError { code, message, data: None }; - Response { id, response_kind: ResponseKind::Err { error } } + Response { id, response_result: Err(error) } } } @@ -305,11 +304,11 @@ fn write_msg_text(out: &mut dyn Write, msg: &str) -> io::Result<()> { #[cfg(test)] mod tests { - use super::{Message, Notification, Request, RequestId}; + use super::{Message, Notification, Request, RequestId, Response}; #[test] fn shutdown_with_explicit_null() { - let text = "{\"jsonrpc\": \"2.0\",\"id\": 3,\"method\": \"shutdown\", \"params\": null }"; + let text = r#"{"jsonrpc": "2.0","id": 3,"method": "shutdown", "params": null }"#; let msg: Message = serde_json::from_str(text).unwrap(); assert!( @@ -319,7 +318,7 @@ mod tests { #[test] fn shutdown_with_no_params() { - let text = "{\"jsonrpc\": \"2.0\",\"id\": 3,\"method\": \"shutdown\"}"; + let text = r#"{"jsonrpc": "2.0","id": 3,"method": "shutdown"}"#; let msg: Message = serde_json::from_str(text).unwrap(); assert!( @@ -329,7 +328,7 @@ mod tests { #[test] fn notification_with_explicit_null() { - let text = "{\"jsonrpc\": \"2.0\",\"method\": \"exit\", \"params\": null }"; + let text = r#"{"jsonrpc": "2.0","method": "exit", "params": null }"#; let msg: Message = serde_json::from_str(text).unwrap(); assert!(matches!(msg, Message::Notification(not) if not.method == "exit")); @@ -337,7 +336,7 @@ mod tests { #[test] fn notification_with_no_params() { - let text = "{\"jsonrpc\": \"2.0\",\"method\": \"exit\"}"; + let text = r#"{"jsonrpc": "2.0","method": "exit"}"#; let msg: Message = serde_json::from_str(text).unwrap(); assert!(matches!(msg, Message::Notification(not) if not.method == "exit")); @@ -352,7 +351,7 @@ mod tests { }); let serialized = serde_json::to_string(&msg).unwrap(); - assert_eq!("{\"id\":3,\"method\":\"shutdown\"}", serialized); + assert_eq!(r#"{"id":3,"method":"shutdown"}"#, serialized); } #[test] @@ -363,6 +362,29 @@ mod tests { }); let serialized = serde_json::to_string(&msg).unwrap(); - assert_eq!("{\"method\":\"exit\"}", serialized); + assert_eq!(r#"{"method":"exit"}"#, serialized); + } + + #[test] + fn serialize_ok_response() { + let msg = Message::Response(Response::new_ok(RequestId::from(3), "success")); + let serialized = serde_json::to_string(&msg).unwrap(); + + assert_eq!(r#"{"id":3,"result":"success"}"#, serialized); + } + + #[test] + fn serialize_err_response() { + let msg = Message::Response(Response::new_err( + RequestId::from(3), + -32600, + String::from("bad response message"), + )); + let serialized = serde_json::to_string(&msg).unwrap(); + + assert_eq!( + r#"{"id":3,"error":{"code":-32600,"message":"bad response message"}}"#, + serialized + ); } } diff --git a/lib/lsp-server/src/req_queue.rs b/lib/lsp-server/src/req_queue.rs index 0c39a1dc7a1f..52493d159854 100644 --- a/lib/lsp-server/src/req_queue.rs +++ b/lib/lsp-server/src/req_queue.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use crate::{ErrorCode, Request, RequestId, Response, ResponseError, msg::ResponseKind}; +use crate::{ErrorCode, Request, RequestId, Response, ResponseError}; /// Manages the set of pending requests, both incoming and outgoing. #[derive(Debug)] @@ -47,7 +47,7 @@ impl Incoming { message: "canceled by client".to_owned(), data: None, }; - Some(Response { id, response_kind: ResponseKind::Err { error } }) + Some(Response { id, response_result: Err(error) }) } pub fn complete(&mut self, id: &RequestId) -> Option {