From 72a3722470e5297c72dcaccaf2f113e7b758606d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 30 Aug 2019 17:24:11 +0300 Subject: move lsp-server to a separate repository --- crates/ra_lsp_server/Cargo.toml | 2 +- crates/ra_lsp_server/src/main.rs | 14 +- crates/ra_lsp_server/src/main_loop.rs | 141 +++++++++++---------- crates/ra_lsp_server/src/main_loop/handlers.rs | 2 +- .../src/main_loop/pending_requests.rs | 19 +-- crates/ra_lsp_server/src/world.rs | 2 +- crates/ra_lsp_server/tests/heavy_tests/support.rs | 57 ++++----- 7 files changed, 127 insertions(+), 110 deletions(-) (limited to 'crates/ra_lsp_server') diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml index e271c257b..084a20cd9 100644 --- a/crates/ra_lsp_server/Cargo.toml +++ b/crates/ra_lsp_server/Cargo.toml @@ -21,7 +21,7 @@ thread_worker = { path = "../thread_worker" } ra_syntax = { path = "../ra_syntax" } ra_text_edit = { path = "../ra_text_edit" } ra_ide_api = { path = "../ra_ide_api" } -gen_lsp_server = { path = "../gen_lsp_server" } +lsp-server = "0.1.0" ra_project_model = { path = "../ra_project_model" } ra_prof = { path = "../ra_prof" } ra_vfs_glob = { path = "../ra_vfs_glob" } diff --git a/crates/ra_lsp_server/src/main.rs b/crates/ra_lsp_server/src/main.rs index ae1392cb5..1debe7660 100644 --- a/crates/ra_lsp_server/src/main.rs +++ b/crates/ra_lsp_server/src/main.rs @@ -1,5 +1,5 @@ use flexi_logger::{Duplicate, Logger}; -use gen_lsp_server::{run_server, stdio_transport}; +use lsp_server::{run_server, stdio_transport, LspServerError}; use ra_lsp_server::{show_message, Result, ServerConfig}; use ra_prof; @@ -29,9 +29,11 @@ fn main() -> Result<()> { } fn main_inner() -> Result<()> { - let (receiver, sender, threads) = stdio_transport(); + let (sender, receiver, io_threads) = stdio_transport(); let cwd = std::env::current_dir()?; - run_server(ra_lsp_server::server_capabilities(), receiver, sender, |params, r, s| { + let caps = serde_json::to_value(ra_lsp_server::server_capabilities()).unwrap(); + run_server(caps, sender, receiver, |params, s, r| { + let params: lsp_types::InitializeParams = serde_json::from_value(params)?; let root = params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd); let workspace_roots = params @@ -62,9 +64,13 @@ fn main_inner() -> Result<()> { .unwrap_or_default(); ra_lsp_server::main_loop(workspace_roots, params.capabilities, server_config, r, s) + }) + .map_err(|err| match err { + LspServerError::ProtocolError(err) => err.into(), + LspServerError::ServerError(err) => err, })?; log::info!("shutting down IO..."); - threads.join()?; + io_threads.join()?; log::info!("... IO is down"); Ok(()) } diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index 45bd52769..fb357b36b 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs @@ -5,9 +5,7 @@ pub(crate) mod pending_requests; use std::{error::Error, fmt, path::PathBuf, sync::Arc, time::Instant}; use crossbeam_channel::{select, unbounded, Receiver, RecvError, Sender}; -use gen_lsp_server::{ - handle_shutdown, ErrorCode, RawMessage, RawNotification, RawRequest, RawResponse, -}; +use lsp_server::{handle_shutdown, ErrorCode, Message, Notification, Request, RequestId, Response}; use lsp_types::{ClientCapabilities, NumberOrString}; use ra_ide_api::{Canceled, FeatureFlags, FileId, LibraryData}; use ra_prof::profile; @@ -53,8 +51,8 @@ pub fn main_loop( ws_roots: Vec, client_caps: ClientCapabilities, config: ServerConfig, - msg_receiver: &Receiver, - msg_sender: &Sender, + msg_receiver: &Receiver, + msg_sender: &Sender, ) -> Result<()> { log::info!("server_config: {:#?}", config); // FIXME: support dynamic workspace loading. @@ -146,12 +144,12 @@ pub fn main_loop( #[derive(Debug)] enum Task { - Respond(RawResponse), - Notify(RawNotification), + Respond(Response), + Notify(Notification), } enum Event { - Msg(RawMessage), + Msg(Message), Task(Task), Vfs(VfsTask), Lib(LibraryData), @@ -159,24 +157,28 @@ enum Event { impl fmt::Debug for Event { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let debug_verbose_not = |not: &RawNotification, f: &mut fmt::Formatter| { - f.debug_struct("RawNotification").field("method", ¬.method).finish() + let debug_verbose_not = |not: &Notification, f: &mut fmt::Formatter| { + f.debug_struct("Notification").field("method", ¬.method).finish() }; match self { - Event::Msg(RawMessage::Notification(not)) => { - if not.is::() || not.is::() { + Event::Msg(Message::Notification(not)) => { + if notification_is::(not) + || notification_is::(not) + { return debug_verbose_not(not, f); } } Event::Task(Task::Notify(not)) => { - if not.is::() || not.is::() { + if notification_is::(not) + || notification_is::(not) + { return debug_verbose_not(not, f); } } Event::Task(Task::Respond(resp)) => { return f - .debug_struct("RawResponse") + .debug_struct("Response") .field("id", &resp.id) .field("error", &resp.error) .finish(); @@ -194,8 +196,8 @@ impl fmt::Debug for Event { fn main_loop_inner( pool: &ThreadPool, - msg_sender: &Sender, - msg_receiver: &Receiver, + msg_sender: &Sender, + msg_receiver: &Receiver, task_sender: Sender, task_receiver: Receiver, state: &mut WorldState, @@ -249,10 +251,9 @@ fn main_loop_inner( in_flight_libraries -= 1; } Event::Msg(msg) => match msg { - RawMessage::Request(req) => { - let req = match handle_shutdown(req, msg_sender) { - Some(req) => req, - None => return Ok(()), + Message::Request(req) => { + if handle_shutdown(&req, msg_sender) { + return Ok(()); }; on_request( state, @@ -264,11 +265,11 @@ fn main_loop_inner( req, )? } - RawMessage::Notification(not) => { + Message::Notification(not) => { on_notification(msg_sender, state, pending_requests, &mut subs, not)?; state_changed = true; } - RawMessage::Response(resp) => log::error!("unexpected response: {:?}", resp), + Message::Response(resp) => log::error!("unexpected response: {:?}", resp), }, }; @@ -313,13 +314,13 @@ fn main_loop_inner( fn on_task( task: Task, - msg_sender: &Sender, + msg_sender: &Sender, pending_requests: &mut PendingRequests, state: &mut WorldState, ) { match task { Task::Respond(response) => { - if let Some(completed) = pending_requests.finish(response.id) { + if let Some(completed) = pending_requests.finish(&response.id) { log::info!("handled req#{} in {:?}", completed.id, completed.duration); state.complete_request(completed); msg_sender.send(response.into()).unwrap(); @@ -336,9 +337,9 @@ fn on_request( pending_requests: &mut PendingRequests, pool: &ThreadPool, sender: &Sender, - msg_sender: &Sender, + msg_sender: &Sender, request_received: Instant, - req: RawRequest, + req: Request, ) -> Result<()> { let mut pool_dispatcher = PoolDispatcher { req: Some(req), @@ -388,22 +389,20 @@ fn on_request( } fn on_notification( - msg_sender: &Sender, + msg_sender: &Sender, state: &mut WorldState, pending_requests: &mut PendingRequests, subs: &mut Subscriptions, - not: RawNotification, + not: Notification, ) -> Result<()> { - let not = match not.cast::() { + let not = match notification_cast::(not) { Ok(params) => { - let id = match params.id { - NumberOrString::Number(id) => id, - NumberOrString::String(id) => { - panic!("string id's not supported: {:?}", id); - } + let id: RequestId = match params.id { + NumberOrString::Number(id) => id.into(), + NumberOrString::String(id) => id.into(), }; - if pending_requests.cancel(id) { - let response = RawResponse::err( + if pending_requests.cancel(&id) { + let response = Response::new_err( id, ErrorCode::RequestCanceled as i32, "canceled by client".to_string(), @@ -414,7 +413,7 @@ fn on_notification( } Err(not) => not, }; - let not = match not.cast::() { + let not = match notification_cast::(not) { Ok(params) => { let uri = params.text_document.uri; let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; @@ -427,7 +426,7 @@ fn on_notification( } Err(not) => not, }; - let not = match not.cast::() { + let not = match notification_cast::(not) { Ok(mut params) => { let uri = params.text_document.uri; let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; @@ -438,7 +437,7 @@ fn on_notification( } Err(not) => not, }; - let not = match not.cast::() { + let not = match notification_cast::(not) { Ok(params) => { let uri = params.text_document.uri; let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; @@ -446,13 +445,13 @@ fn on_notification( subs.remove_sub(FileId(file_id.0)); } let params = req::PublishDiagnosticsParams { uri, diagnostics: Vec::new() }; - let not = RawNotification::new::(¶ms); + let not = notification_new::(params); msg_sender.send(not.into()).unwrap(); return Ok(()); } Err(not) => not, }; - let not = match not.cast::() { + let not = match notification_cast::(not) { Ok(_params) => { return Ok(()); } @@ -463,11 +462,11 @@ fn on_notification( } struct PoolDispatcher<'a> { - req: Option, + req: Option, pool: &'a ThreadPool, world: &'a mut WorldState, pending_requests: &'a mut PendingRequests, - msg_sender: &'a Sender, + msg_sender: &'a Sender, sender: &'a Sender, request_received: Instant, } @@ -522,13 +521,13 @@ impl<'a> PoolDispatcher<'a> { Ok(self) } - fn parse(&mut self) -> Option<(u64, R::Params)> + fn parse(&mut self) -> Option<(RequestId, R::Params)> where R: req::Request + 'static, R::Params: DeserializeOwned + Send + 'static, { let req = self.req.take()?; - let (id, params) = match req.cast::() { + let (id, params) = match req.extract::(R::METHOD) { Ok(it) => it, Err(req) => { self.req = Some(req); @@ -536,7 +535,7 @@ impl<'a> PoolDispatcher<'a> { } }; self.pending_requests.start(PendingRequest { - id, + id: id.clone(), method: R::METHOD.to_string(), received: self.request_received, }); @@ -548,7 +547,7 @@ impl<'a> PoolDispatcher<'a> { None => (), Some(req) => { log::error!("unknown request: {:?}", req); - let resp = RawResponse::err( + let resp = Response::new_err( req.id, ErrorCode::MethodNotFound as i32, "unknown request".to_string(), @@ -559,34 +558,30 @@ impl<'a> PoolDispatcher<'a> { } } -fn result_to_task(id: u64, result: Result) -> Task +fn result_to_task(id: RequestId, result: Result) -> Task where R: req::Request + 'static, R::Params: DeserializeOwned + Send + 'static, R::Result: Serialize + 'static, { let response = match result { - Ok(resp) => RawResponse::ok::(id, &resp), + Ok(resp) => Response::new_ok(id, &resp), Err(e) => match e.downcast::() { - Ok(lsp_error) => RawResponse::err(id, lsp_error.code, lsp_error.message), + Ok(lsp_error) => Response::new_err(id, lsp_error.code, lsp_error.message), Err(e) => { if is_canceled(&e) { // FIXME: When https://github.com/Microsoft/vscode-languageserver-node/issues/457 // gets fixed, we can return the proper response. // This works around the issue where "content modified" error would continuously // show an message pop-up in VsCode - // RawResponse::err( + // Response::err( // id, // ErrorCode::ContentModified as i32, // "content modified".to_string(), // ) - RawResponse { - id, - result: Some(serde_json::to_value(&()).unwrap()), - error: None, - } + Response::new_ok(id, ()) } else { - RawResponse::err(id, ErrorCode::InternalError as i32, e.to_string()) + Response::new_err(id, ErrorCode::InternalError as i32, e.to_string()) } } }, @@ -613,7 +608,7 @@ fn update_file_notifications_on_threadpool( } } Ok(params) => { - let not = RawNotification::new::(¶ms); + let not = notification_new::(params); sender.send(Task::Notify(not)).unwrap(); } } @@ -626,7 +621,7 @@ fn update_file_notifications_on_threadpool( } } Ok(params) => { - let not = RawNotification::new::(¶ms); + let not = notification_new::(params); sender.send(Task::Notify(not)).unwrap(); } } @@ -635,17 +630,33 @@ fn update_file_notifications_on_threadpool( }); } -pub fn show_message( - typ: req::MessageType, - message: impl Into, - sender: &Sender, -) { +pub fn show_message(typ: req::MessageType, message: impl Into, sender: &Sender) { let message = message.into(); let params = req::ShowMessageParams { typ, message }; - let not = RawNotification::new::(¶ms); + let not = notification_new::(params); sender.send(not.into()).unwrap(); } fn is_canceled(e: &Box) -> bool { e.downcast_ref::().is_some() } + +fn notification_is(notification: &Notification) -> bool { + notification.method == N::METHOD +} + +fn notification_cast(notification: Notification) -> std::result::Result +where + N: lsp_types::notification::Notification, + N::Params: DeserializeOwned, +{ + notification.extract(N::METHOD) +} + +fn notification_new(params: N::Params) -> Notification +where + N: lsp_types::notification::Notification, + N::Params: Serialize, +{ + Notification::new(N::METHOD.to_string(), params) +} diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 3a559e845..eb805a6d3 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs @@ -1,6 +1,6 @@ use std::{fmt::Write as _, io::Write as _}; -use gen_lsp_server::ErrorCode; +use lsp_server::ErrorCode; use lsp_types::{ CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeKind, diff --git a/crates/ra_lsp_server/src/main_loop/pending_requests.rs b/crates/ra_lsp_server/src/main_loop/pending_requests.rs index 741770e45..7a99fc679 100644 --- a/crates/ra_lsp_server/src/main_loop/pending_requests.rs +++ b/crates/ra_lsp_server/src/main_loop/pending_requests.rs @@ -1,17 +1,18 @@ use std::time::{Duration, Instant}; +use lsp_server::RequestId; use rustc_hash::FxHashMap; #[derive(Debug)] pub struct CompletedRequest { - pub id: u64, + pub id: RequestId, pub method: String, pub duration: Duration, } #[derive(Debug)] pub(crate) struct PendingRequest { - pub(crate) id: u64, + pub(crate) id: RequestId, pub(crate) method: String, pub(crate) received: Instant, } @@ -28,20 +29,20 @@ impl From for CompletedRequest { #[derive(Debug, Default)] pub(crate) struct PendingRequests { - map: FxHashMap, + map: FxHashMap, } impl PendingRequests { pub(crate) fn start(&mut self, request: PendingRequest) { - let id = request.id; - let prev = self.map.insert(id, request); + let id = request.id.clone(); + let prev = self.map.insert(id.clone(), request); assert!(prev.is_none(), "duplicate request with id {}", id); } - pub(crate) fn cancel(&mut self, id: u64) -> bool { - self.map.remove(&id).is_some() + pub(crate) fn cancel(&mut self, id: &RequestId) -> bool { + self.map.remove(id).is_some() } - pub(crate) fn finish(&mut self, id: u64) -> Option { - self.map.remove(&id).map(CompletedRequest::from) + pub(crate) fn finish(&mut self, id: &RequestId) -> Option { + self.map.remove(id).map(CompletedRequest::from) } } diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index cc7964469..73d7f8fb9 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs @@ -4,7 +4,7 @@ use std::{ }; use crossbeam_channel::{unbounded, Receiver}; -use gen_lsp_server::ErrorCode; +use lsp_server::ErrorCode; use lsp_types::Url; use parking_lot::RwLock; use ra_ide_api::{ diff --git a/crates/ra_lsp_server/tests/heavy_tests/support.rs b/crates/ra_lsp_server/tests/heavy_tests/support.rs index 055c8fff2..45b4cacf6 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/support.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/support.rs @@ -8,13 +8,10 @@ use std::{ use crossbeam_channel::{after, select, Receiver}; use flexi_logger::Logger; -use gen_lsp_server::{RawMessage, RawNotification, RawRequest}; +use lsp_server::{Message, Notification, Request}; use lsp_types::{ - notification::DidOpenTextDocument, - notification::{Notification, ShowMessage}, - request::{Request, Shutdown}, - ClientCapabilities, DidOpenTextDocumentParams, GotoCapability, TextDocumentClientCapabilities, - TextDocumentIdentifier, TextDocumentItem, Url, + request::Shutdown, ClientCapabilities, DidOpenTextDocumentParams, GotoCapability, + TextDocumentClientCapabilities, TextDocumentIdentifier, TextDocumentItem, Url, }; use serde::Serialize; use serde_json::{to_string_pretty, Value}; @@ -84,9 +81,9 @@ pub fn project(fixture: &str) -> Server { pub struct Server { req_id: Cell, - messages: RefCell>, + messages: RefCell>, dir: TempDir, - worker: Worker, + worker: Worker, } impl Server { @@ -100,7 +97,7 @@ impl Server { let roots = if roots.is_empty() { vec![path] } else { roots }; - let worker = Worker::::spawn( + let worker = Worker::::spawn( "test server", 128, move |msg_receiver, msg_sender| { @@ -128,7 +125,8 @@ impl Server { let res = Server { req_id: Cell::new(1), dir, messages: Default::default(), worker }; for (path, text) in files { - res.send_notification(RawNotification::new::( + res.send_notification(Notification::new( + "textDocument/didOpen".to_string(), &DidOpenTextDocumentParams { text_document: TextDocumentItem { uri: Url::from_file_path(path).unwrap(), @@ -149,16 +147,16 @@ impl Server { pub fn notification(&self, params: N::Params) where - N: Notification, + N: lsp_types::notification::Notification, N::Params: Serialize, { - let r = RawNotification::new::(¶ms); + let r = Notification::new(N::METHOD.to_string(), params); self.send_notification(r) } pub fn request(&self, params: R::Params, expected_resp: Value) where - R: Request, + R: lsp_types::request::Request, R::Params: Serialize, { let actual = self.send_request::(params); @@ -175,23 +173,23 @@ impl Server { pub fn send_request(&self, params: R::Params) -> Value where - R: Request, + R: lsp_types::request::Request, R::Params: Serialize, { let id = self.req_id.get(); self.req_id.set(id + 1); - let r = RawRequest::new::(id, ¶ms); + let r = Request::new(id.into(), R::METHOD.to_string(), params); self.send_request_(r) } - fn send_request_(&self, r: RawRequest) -> Value { - let id = r.id; - self.worker.sender().send(RawMessage::Request(r)).unwrap(); + fn send_request_(&self, r: Request) -> Value { + let id = r.id.clone(); + self.worker.sender().send(r.into()).unwrap(); while let Some(msg) = self.recv() { match msg { - RawMessage::Request(req) => panic!("unexpected request: {:?}", req), - RawMessage::Notification(_) => (), - RawMessage::Response(res) => { + Message::Request(req) => panic!("unexpected request: {:?}", req), + Message::Notification(_) => (), + Message::Response(res) => { assert_eq!(res.id, id); if let Some(err) = res.error { panic!("error response: {:#?}", err); @@ -203,15 +201,16 @@ impl Server { panic!("no response"); } pub fn wait_until_workspace_is_loaded(&self) { - self.wait_for_message_cond(1, &|msg: &RawMessage| match msg { - RawMessage::Notification(n) if n.method == ShowMessage::METHOD => { - let msg = n.clone().cast::().unwrap(); + self.wait_for_message_cond(1, &|msg: &Message| match msg { + Message::Notification(n) if n.method == "window/showMessage" => { + let msg = + n.clone().extract::("window/showMessage").unwrap(); msg.message.starts_with("workspace loaded") } _ => false, }) } - fn wait_for_message_cond(&self, n: usize, cond: &dyn Fn(&RawMessage) -> bool) { + fn wait_for_message_cond(&self, n: usize, cond: &dyn Fn(&Message) -> bool) { let mut total = 0; for msg in self.messages.borrow().iter() { if cond(msg) { @@ -225,14 +224,14 @@ impl Server { } } } - fn recv(&self) -> Option { + fn recv(&self) -> Option { recv_timeout(&self.worker.receiver()).map(|msg| { self.messages.borrow_mut().push(msg.clone()); msg }) } - fn send_notification(&self, not: RawNotification) { - self.worker.sender().send(RawMessage::Notification(not)).unwrap(); + fn send_notification(&self, not: Notification) { + self.worker.sender().send(Message::Notification(not)).unwrap(); } pub fn path(&self) -> &Path { @@ -246,7 +245,7 @@ impl Drop for Server { } } -fn recv_timeout(receiver: &Receiver) -> Option { +fn recv_timeout(receiver: &Receiver) -> Option { let timeout = Duration::from_secs(120); select! { recv(receiver) -> msg => msg.ok(), -- cgit v1.2.3