From 9063dabcca0aaaa6d8bb3364cee21fd68023a059 Mon Sep 17 00:00:00 2001 From: Ville Penttinen Date: Tue, 5 Mar 2019 21:59:01 +0200 Subject: [PATCH 1/4] Send an actual ShowMessage instead of InternalFeedback in feedback() This now allows us to send a notification that can be shown in the UI when the workspace has been loaded. Additionally this removes the need for internal_mode flag. --- crates/ra_lsp_server/src/main.rs | 2 +- crates/ra_lsp_server/src/main_loop.rs | 37 +++++++++++-------- .../tests/heavy_tests/support.rs | 8 ++-- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/crates/ra_lsp_server/src/main.rs b/crates/ra_lsp_server/src/main.rs index 03f83c7bef14..9dc2e3f879b1 100644 --- a/crates/ra_lsp_server/src/main.rs +++ b/crates/ra_lsp_server/src/main.rs @@ -43,7 +43,7 @@ fn main_inner() -> Result<()> { .and_then(|v| InitializationOptions::deserialize(v).ok()) .and_then(|it| it.publish_decorations) == Some(true); - ra_lsp_server::main_loop(false, root, supports_decorations, r, s) + ra_lsp_server::main_loop(root, supports_decorations, r, s) })?; log::info!("shutting down IO..."); threads.join()?; diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index 0f8ef10b991b..145f7bf6536b 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs @@ -46,7 +46,6 @@ enum Task { const THREADPOOL_SIZE: usize = 8; pub fn main_loop( - internal_mode: bool, ws_root: PathBuf, supports_decorations: bool, msg_receiver: &Receiver, @@ -63,11 +62,12 @@ pub fn main_loop( Ok(ws) => vec![ws], Err(e) => { log::error!("loading workspace failed: {}", e); - let msg = RawNotification::new::(&req::ShowMessageParams { - typ: req::MessageType::Error, - message: format!("rust-analyzer failed to load workspace: {}", e), - }); - msg_sender.send(msg.into()).unwrap(); + + feedback( + req::MessageType::Error, + format!("rust-analyzer failed to load workspace: {}", e), + msg_sender, + ); Vec::new() } } @@ -80,7 +80,6 @@ pub fn main_loop( let mut pending_requests = FxHashSet::default(); let mut subs = Subscriptions::new(); let main_res = main_loop_inner( - internal_mode, supports_decorations, &pool, msg_sender, @@ -148,7 +147,6 @@ impl fmt::Debug for Event { } fn main_loop_inner( - internal_mode: bool, supports_decorations: bool, pool: &ThreadPool, msg_sender: &Sender, @@ -163,6 +161,7 @@ fn main_loop_inner( // time to always have a thread ready to react to input. let mut in_flight_libraries = 0; let mut pending_libraries = Vec::new(); + let mut send_workspace_notification = true; let (libdata_sender, libdata_receiver) = unbounded(); loop { @@ -190,7 +189,6 @@ fn main_loop_inner( state_changed = true; } Event::Lib(lib) => { - feedback(internal_mode, "library loaded", msg_sender); state.add_lib(lib); in_flight_libraries -= 1; } @@ -244,8 +242,14 @@ fn main_loop_inner( }); } - if state.roots_to_scan == 0 && pending_libraries.is_empty() && in_flight_libraries == 0 { - feedback(internal_mode, "workspace loaded", msg_sender); + if send_workspace_notification + && state.roots_to_scan == 0 + && pending_libraries.is_empty() + && in_flight_libraries == 0 + { + feedback(req::MessageType::Info, "workspace loaded", msg_sender); + // Only send the notification first time + send_workspace_notification = false; } if state_changed { @@ -501,11 +505,12 @@ fn update_file_notifications_on_threadpool( }); } -fn feedback(intrnal_mode: bool, msg: &str, sender: &Sender) { - if !intrnal_mode { - return; - } - let not = RawNotification::new::(&msg.to_string()); +fn feedback>(typ: req::MessageType, msg: M, sender: &Sender) { + let not = RawNotification::new::(&req::ShowMessageParams { + typ, + message: msg.into(), + }); + sender.send(not.into()).unwrap(); } diff --git a/crates/ra_lsp_server/tests/heavy_tests/support.rs b/crates/ra_lsp_server/tests/heavy_tests/support.rs index f4e7eaf75bab..3a7c50309015 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/support.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/support.rs @@ -13,6 +13,7 @@ use lsp_types::{ notification::DidOpenTextDocument, request::{Request, Shutdown}, DidOpenTextDocumentParams, TextDocumentIdentifier, TextDocumentItem, Url, + notification::{Notification, ShowMessage}, }; use serde::Serialize; use serde_json::{to_string_pretty, Value}; @@ -56,7 +57,7 @@ impl Server { "test server", 128, move |mut msg_receiver, mut msg_sender| { - main_loop(true, path, true, &mut msg_receiver, &mut msg_sender).unwrap() + main_loop(path, true, &mut msg_receiver, &mut msg_sender).unwrap() }, ); let res = Server { @@ -138,8 +139,9 @@ impl Server { } pub fn wait_for_feedback_n(&self, feedback: &str, n: usize) { let f = |msg: &RawMessage| match msg { - RawMessage::Notification(n) if n.method == "internalFeedback" => { - return n.clone().cast::().unwrap() == feedback; + RawMessage::Notification(n) if n.method == ShowMessage::METHOD => { + let message = n.clone().cast::().unwrap(); + message.message == feedback } _ => false, }; From 80347b8187e8cc9f062335bc62643c009e7400d8 Mon Sep 17 00:00:00 2001 From: Ville Penttinen Date: Tue, 5 Mar 2019 22:20:11 +0200 Subject: [PATCH 2/4] Remove InternalFeedback --- crates/ra_lsp_server/src/req.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index 484fde7e500f..e0571fd78bf6 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs @@ -172,10 +172,3 @@ pub struct SourceChange { pub workspace_edit: WorkspaceEdit, pub cursor_position: Option, } - -pub enum InternalFeedback {} - -impl Notification for InternalFeedback { - const METHOD: &'static str = "internalFeedback"; - type Params = String; -} From ce118da149a0db1815f188c9914001608a5ac09e Mon Sep 17 00:00:00 2001 From: Ville Penttinen Date: Tue, 5 Mar 2019 22:25:24 +0200 Subject: [PATCH 3/4] Rename feedback to show_message --- crates/ra_lsp_server/src/main_loop.rs | 6 +++--- crates/ra_lsp_server/tests/heavy_tests/main.rs | 10 +++++----- crates/ra_lsp_server/tests/heavy_tests/support.rs | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index 145f7bf6536b..fb2305b26c00 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs @@ -63,7 +63,7 @@ pub fn main_loop( Err(e) => { log::error!("loading workspace failed: {}", e); - feedback( + show_message( req::MessageType::Error, format!("rust-analyzer failed to load workspace: {}", e), msg_sender, @@ -247,7 +247,7 @@ fn main_loop_inner( && pending_libraries.is_empty() && in_flight_libraries == 0 { - feedback(req::MessageType::Info, "workspace loaded", msg_sender); + show_message(req::MessageType::Info, "workspace loaded", msg_sender); // Only send the notification first time send_workspace_notification = false; } @@ -505,7 +505,7 @@ fn update_file_notifications_on_threadpool( }); } -fn feedback>(typ: req::MessageType, msg: M, sender: &Sender) { +fn show_message>(typ: req::MessageType, msg: M, sender: &Sender) { let not = RawNotification::new::(&req::ShowMessageParams { typ, message: msg.into(), diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs index 996bf8e018ba..1c099a78f59f 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/main.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs @@ -31,7 +31,7 @@ version = "0.0.0" use std::collections::Spam; "#, ); - server.wait_for_feedback("workspace loaded"); + server.wait_for_message("workspace loaded"); eprintln!("loading took {:?}", project_start.elapsed()); let completion_start = Instant::now(); let res = server.send_request::(CompletionParams { @@ -53,7 +53,7 @@ fn foo() { } ", ); - server.wait_for_feedback("workspace loaded"); + server.wait_for_message("workspace loaded"); server.request::( RunnablesParams { text_document: server.doc_id("lib.rs"), position: None }, json!([ @@ -107,7 +107,7 @@ pub fn foo() {} fn test_eggs() {} "#, ); - server.wait_for_feedback("workspace loaded"); + server.wait_for_message("workspace loaded"); server.request::( RunnablesParams { text_document: server.doc_id("tests/spam.rs"), @@ -167,7 +167,7 @@ fn main() { pub use std::collections::HashMap; "#, ); - server.wait_for_feedback("workspace loaded"); + server.wait_for_message("workspace loaded"); server.request::( DocumentFormattingParams { @@ -216,7 +216,7 @@ mod bar; fn main() {} "#, ); - server.wait_for_feedback("workspace loaded"); + server.wait_for_message("workspace loaded"); let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None }; server.request::( CodeActionParams { diff --git a/crates/ra_lsp_server/tests/heavy_tests/support.rs b/crates/ra_lsp_server/tests/heavy_tests/support.rs index 3a7c50309015..08f7ad6fdeab 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/support.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/support.rs @@ -134,14 +134,14 @@ impl Server { } panic!("no response"); } - pub fn wait_for_feedback(&self, feedback: &str) { - self.wait_for_feedback_n(feedback, 1) + pub fn wait_for_message(&self, message: &str) { + self.wait_for_message_n(message, 1) } - pub fn wait_for_feedback_n(&self, feedback: &str, n: usize) { + pub fn wait_for_message_n(&self, message: &str, n: usize) { let f = |msg: &RawMessage| match msg { RawMessage::Notification(n) if n.method == ShowMessage::METHOD => { - let message = n.clone().cast::().unwrap(); - message.message == feedback + let msg = n.clone().cast::().unwrap(); + msg.message == message } _ => false, }; From 0dcb1cb569417a17e27a4d8b34813ded41395268 Mon Sep 17 00:00:00 2001 From: Ville Penttinen Date: Wed, 6 Mar 2019 11:34:38 +0200 Subject: [PATCH 4/4] Add showWorkspaceLoadedNotification to vscode client This allows users to control whether or not they want to see the "workspace loaded" notification. This is done on the server side using InitializationOptions which are provided by the client. By default show_workspace_loaded is true, meaning the notification is sent. --- crates/ra_lsp_server/src/init.rs | 39 +++++++++++++++++++ crates/ra_lsp_server/src/lib.rs | 3 +- crates/ra_lsp_server/src/main.rs | 20 +++------- crates/ra_lsp_server/src/main_loop.rs | 13 ++++--- .../tests/heavy_tests/support.rs | 9 ++++- editors/code/package.json | 5 +++ editors/code/src/config.ts | 7 ++++ editors/code/src/server.ts | 4 +- 8 files changed, 78 insertions(+), 22 deletions(-) create mode 100644 crates/ra_lsp_server/src/init.rs diff --git a/crates/ra_lsp_server/src/init.rs b/crates/ra_lsp_server/src/init.rs new file mode 100644 index 000000000000..0b7a47a0b9ee --- /dev/null +++ b/crates/ra_lsp_server/src/init.rs @@ -0,0 +1,39 @@ +use serde::{Deserialize, Deserializer}; + +/// Client provided initialization options +#[derive(Deserialize, Clone, Copy, Debug)] +#[serde(rename_all = "camelCase")] +pub struct InitializationOptions { + /// Whether the client supports our custom highlighting publishing decorations. + /// This is different to the highlightingOn setting, which is whether the user + /// wants our custom highlighting to be used. + /// + /// Defaults to `true` + #[serde(default = "bool_true", deserialize_with = "nullable_bool_true")] + pub publish_decorations: bool, + + /// Whether or not the workspace loaded notification should be sent + /// + /// Defaults to `true` + #[serde(default = "bool_true", deserialize_with = "nullable_bool_true")] + pub show_workspace_loaded: bool, +} + +impl Default for InitializationOptions { + fn default() -> InitializationOptions { + InitializationOptions { publish_decorations: true, show_workspace_loaded: true } + } +} + +fn bool_true() -> bool { + true +} + +/// Deserializes a null value to a bool true by default +fn nullable_bool_true<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let opt = Option::deserialize(deserializer)?; + Ok(opt.unwrap_or(true)) +} diff --git a/crates/ra_lsp_server/src/lib.rs b/crates/ra_lsp_server/src/lib.rs index 5b5f3b948fdd..59e16a47c5e6 100644 --- a/crates/ra_lsp_server/src/lib.rs +++ b/crates/ra_lsp_server/src/lib.rs @@ -5,7 +5,8 @@ mod main_loop; mod markdown; mod project_model; pub mod req; +pub mod init; mod server_world; pub type Result = ::std::result::Result; -pub use crate::{caps::server_capabilities, main_loop::main_loop, main_loop::LspError}; +pub use crate::{caps::server_capabilities, main_loop::main_loop, main_loop::LspError, init::InitializationOptions}; diff --git a/crates/ra_lsp_server/src/main.rs b/crates/ra_lsp_server/src/main.rs index 9dc2e3f879b1..5a29052071c3 100644 --- a/crates/ra_lsp_server/src/main.rs +++ b/crates/ra_lsp_server/src/main.rs @@ -2,7 +2,7 @@ use serde::Deserialize; use flexi_logger::{Duplicate, Logger}; use gen_lsp_server::{run_server, stdio_transport}; -use ra_lsp_server::Result; +use ra_lsp_server::{Result, InitializationOptions}; fn main() -> Result<()> { ::std::env::set_var("RUST_BACKTRACE", "short"); @@ -24,26 +24,18 @@ fn main() -> Result<()> { } } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct InitializationOptions { - // Whether the client supports our custom highlighting publishing decorations. - // This is different to the highlightingOn setting, which is whether the user - // wants our custom highlighting to be used. - publish_decorations: Option, -} - fn main_inner() -> Result<()> { let (receiver, sender, threads) = stdio_transport(); let cwd = ::std::env::current_dir()?; run_server(ra_lsp_server::server_capabilities(), receiver, sender, |params, r, s| { let root = params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd); - let supports_decorations = params + + let opts = params .initialization_options .and_then(|v| InitializationOptions::deserialize(v).ok()) - .and_then(|it| it.publish_decorations) - == Some(true); - ra_lsp_server::main_loop(root, supports_decorations, r, s) + .unwrap_or(InitializationOptions::default()); + + ra_lsp_server::main_loop(root, opts, r, s) })?; log::info!("shutting down IO..."); threads.join()?; diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index fb2305b26c00..d0c2a95ef70f 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs @@ -22,6 +22,7 @@ use crate::{ req, server_world::{ServerWorld, ServerWorldState}, Result, + InitializationOptions, }; #[derive(Debug, Fail)] @@ -47,7 +48,7 @@ const THREADPOOL_SIZE: usize = 8; pub fn main_loop( ws_root: PathBuf, - supports_decorations: bool, + options: InitializationOptions, msg_receiver: &Receiver, msg_sender: &Sender, ) -> Result<()> { @@ -80,7 +81,7 @@ pub fn main_loop( let mut pending_requests = FxHashSet::default(); let mut subs = Subscriptions::new(); let main_res = main_loop_inner( - supports_decorations, + options, &pool, msg_sender, msg_receiver, @@ -147,7 +148,7 @@ impl fmt::Debug for Event { } fn main_loop_inner( - supports_decorations: bool, + options: InitializationOptions, pool: &ThreadPool, msg_sender: &Sender, msg_receiver: &Receiver, @@ -247,7 +248,9 @@ fn main_loop_inner( && pending_libraries.is_empty() && in_flight_libraries == 0 { - show_message(req::MessageType::Info, "workspace loaded", msg_sender); + if options.show_workspace_loaded { + show_message(req::MessageType::Info, "workspace loaded", msg_sender); + } // Only send the notification first time send_workspace_notification = false; } @@ -256,7 +259,7 @@ fn main_loop_inner( update_file_notifications_on_threadpool( pool, state.snapshot(), - supports_decorations, + options.publish_decorations, task_sender.clone(), subs.subscriptions(), ) diff --git a/crates/ra_lsp_server/tests/heavy_tests/support.rs b/crates/ra_lsp_server/tests/heavy_tests/support.rs index 08f7ad6fdeab..8bfc8d62206c 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/support.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/support.rs @@ -23,6 +23,7 @@ use test_utils::{parse_fixture, find_mismatch}; use ra_lsp_server::{ main_loop, req, + InitializationOptions, }; pub fn project(fixture: &str) -> Server { @@ -57,7 +58,13 @@ impl Server { "test server", 128, move |mut msg_receiver, mut msg_sender| { - main_loop(path, true, &mut msg_receiver, &mut msg_sender).unwrap() + main_loop( + path, + InitializationOptions::default(), + &mut msg_receiver, + &mut msg_sender, + ) + .unwrap() }, ); let res = Server { diff --git a/editors/code/package.json b/editors/code/package.json index fda411810a36..47eaac878a69 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -150,6 +150,11 @@ "default": false, "description": "Highlight Rust code (overrides built-in syntax highlighting)" }, + "rust-analyzer.showWorkspaceLoadedNotification": { + "type": "boolean", + "default": true, + "description": "Show notification when workspace was loaded" + }, "rust-analyzer.enableEnhancedTyping": { "type": "boolean", "default": true, diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 4e353798cf97..afc5cc6aff68 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -8,6 +8,7 @@ export class Config { public highlightingOn = true; public enableEnhancedTyping = true; public raLspServerPath = RA_LSP_DEBUG || 'ra_lsp_server'; + public showWorkspaceLoadedNotification = true; private prevEnhancedTyping: null | boolean = null; @@ -24,6 +25,12 @@ export class Config { this.highlightingOn = config.get('highlightingOn') as boolean; } + if (config.has('showWorkspaceLoadedNotification')) { + this.showWorkspaceLoadedNotification = config.get( + 'showWorkspaceLoadedNotification' + ) as boolean; + } + if (!this.highlightingOn && Server) { Server.highlighter.removeHighlights(); } diff --git a/editors/code/src/server.ts b/editors/code/src/server.ts index 9ead87fae6db..50461b0c6eab 100644 --- a/editors/code/src/server.ts +++ b/editors/code/src/server.ts @@ -26,7 +26,9 @@ export class Server { const clientOptions: lc.LanguageClientOptions = { documentSelector: [{ scheme: 'file', language: 'rust' }], initializationOptions: { - publishDecorations: true + publishDecorations: true, + showWorkspaceLoaded: + Server.config.showWorkspaceLoadedNotification }, traceOutputChannel };