From 083035fd06d27487a976953ede615c4fa1755988 Mon Sep 17 00:00:00 2001 From: Steffen Lyngbaek Date: Fri, 13 Mar 2020 16:03:02 -0700 Subject: Use WorkDoneProgress LSP API for initial load Addresses #3283 Rather than using custom UI for showing the loaded state. Rely on the WorkDoneProgress API in 3.15.0 https://microsoft.github.io/language-server-protocol/specification#workDoneProgress. No client-side work was necessary. The UI is not exactly what is described in the issue but afaict that's how VS Code implements the LSP API. - The WorkDoneProgressEnd does not appear to display its message contents (controlled by vscode) --- crates/rust-analyzer/src/main_loop.rs | 71 ++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 2b3b16d35..eb41e3cda 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -16,7 +16,10 @@ use std::{ use crossbeam_channel::{select, unbounded, RecvError, Sender}; use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; -use lsp_types::{ClientCapabilities, NumberOrString}; +use lsp_types::{ + ClientCapabilities, NumberOrString, WorkDoneProgress, WorkDoneProgressBegin, + WorkDoneProgressCreateParams, WorkDoneProgressEnd, WorkDoneProgressReport, +}; use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckTask}; use ra_ide::{Canceled, FileId, InlayHintsOptions, LibraryData, SourceRootId}; use ra_prof::profile; @@ -329,6 +332,7 @@ struct LoopState { in_flight_libraries: usize, pending_libraries: Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc)>)>, workspace_loaded: bool, + roots_scanned_progress: Option, } impl LoopState { @@ -428,11 +432,6 @@ fn loop_turn( && loop_state.in_flight_libraries == 0 { loop_state.workspace_loaded = true; - let n_packages: usize = world_state.workspaces.iter().map(|it| it.n_packages()).sum(); - if world_state.feature_flags.get("notifications.workspace-loaded") { - let msg = format!("workspace loaded, {} rust packages", n_packages); - show_message(req::MessageType::Info, msg, &connection.sender); - } world_state.check_watcher.update(); pool.execute({ let subs = loop_state.subscriptions.subscriptions(); @@ -440,6 +439,7 @@ fn loop_turn( move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ()) }); } + send_startup_progress(&connection.sender, loop_state, world_state); if state_changed { update_file_notifications_on_threadpool( @@ -703,6 +703,65 @@ fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender, state: } } +fn send_startup_progress( + sender: &Sender, + loop_state: &mut LoopState, + world_state: &WorldState, +) { + if !world_state.feature_flags.get("notifications.workspace-loaded") { + return; + } + let total: usize = world_state.workspaces.iter().map(|it| it.n_packages()).sum(); + let progress = total - world_state.roots_to_scan; + if loop_state.roots_scanned_progress == Some(progress) { + return; + } + loop_state.roots_scanned_progress = Some(progress); + + match (progress, loop_state.workspace_loaded) { + (0, false) => { + let work_done_progress_create = request_new::( + loop_state.next_request_id(), + WorkDoneProgressCreateParams { + token: req::ProgressToken::String("rustAnalyzer/startup".into()), + }, + ); + sender.send(work_done_progress_create.into()).unwrap(); + send_startup_progress_notif( + sender, + WorkDoneProgress::Begin(WorkDoneProgressBegin { + title: "rust-analyzer".into(), + cancellable: None, + message: Some(format!("{}/{} packages", progress, total)), + percentage: Some(100 as f64 * progress as f64 / total as f64), + }), + ); + } + (_, false) => send_startup_progress_notif( + sender, + WorkDoneProgress::Report(WorkDoneProgressReport { + cancellable: None, + message: Some(format!("{}/{} packages", progress, total)), + percentage: Some(100 as f64 * progress as f64 / total as f64), + }), + ), + (_, true) => send_startup_progress_notif( + sender, + WorkDoneProgress::End(WorkDoneProgressEnd { + message: Some(format!("rust-analyzer loaded, {} packages", progress)), + }), + ), + } +} + +fn send_startup_progress_notif(sender: &Sender, work_done_progress: WorkDoneProgress) { + let notif = notification_new::(req::ProgressParams { + token: req::ProgressToken::String("rustAnalyzer/startup".into()), + value: req::ProgressParamsValue::WorkDone(work_done_progress), + }); + sender.send(notif.into()).unwrap(); +} + struct PoolDispatcher<'a> { req: Option, pool: &'a ThreadPool, -- cgit v1.2.3 From f0b58fd1fa93bf8d9c201e891528be1b1cc54512 Mon Sep 17 00:00:00 2001 From: Steffen Lyngbaek Date: Fri, 13 Mar 2020 16:27:39 -0700 Subject: Use idiomatic way of defining floats --- crates/rust-analyzer/src/main_loop.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index eb41e3cda..c38a25a44 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -733,7 +733,7 @@ fn send_startup_progress( title: "rust-analyzer".into(), cancellable: None, message: Some(format!("{}/{} packages", progress, total)), - percentage: Some(100 as f64 * progress as f64 / total as f64), + percentage: Some(100.0 * progress as f64 / total as f64), }), ); } @@ -742,7 +742,7 @@ fn send_startup_progress( WorkDoneProgress::Report(WorkDoneProgressReport { cancellable: None, message: Some(format!("{}/{} packages", progress, total)), - percentage: Some(100 as f64 * progress as f64 / total as f64), + percentage: Some(100.0 * progress as f64 / total as f64), }), ), (_, true) => send_startup_progress_notif( -- cgit v1.2.3 From 5a1e531176bf6dab75e6c33cc30be6c5bb3ff345 Mon Sep 17 00:00:00 2001 From: Steffen Lyngbaek Date: Fri, 13 Mar 2020 22:06:49 -0700 Subject: Fix broken tests - Properly wait for workspace loading to be done --- crates/rust-analyzer/tests/heavy_tests/support.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs index e28ae61fe..1d7062bdf 100644 --- a/crates/rust-analyzer/tests/heavy_tests/support.rs +++ b/crates/rust-analyzer/tests/heavy_tests/support.rs @@ -12,13 +12,14 @@ use lsp_types::{ notification::{DidOpenTextDocument, Exit}, request::Shutdown, ClientCapabilities, DidOpenTextDocumentParams, GotoCapability, TextDocumentClientCapabilities, - TextDocumentIdentifier, TextDocumentItem, Url, + TextDocumentIdentifier, TextDocumentItem, Url, WorkDoneProgress, }; use serde::Serialize; use serde_json::{to_string_pretty, Value}; use tempfile::TempDir; use test_utils::{find_mismatch, parse_fixture}; +use req::{ProgressParams, ProgressParamsValue}; use rust_analyzer::{main_loop, req, ServerConfig}; pub struct Project<'a> { @@ -201,10 +202,14 @@ impl Server { } pub fn wait_until_workspace_is_loaded(&self) { 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") + Message::Notification(n) if n.method == "$/progress" => { + match n.clone().extract::("$/progress").unwrap() { + ProgressParams { + token: req::ProgressToken::String(ref token), + value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(_)), + } if token == "rustAnalyzer/startup" => true, + _ => false, + } } _ => false, }) -- cgit v1.2.3 From e3db4f3716cb67f75d818207b7698cf3464aad57 Mon Sep 17 00:00:00 2001 From: Steffen Lyngbaek Date: Sun, 15 Mar 2020 20:48:39 -0700 Subject: Rely on the safer workspace_loaded check --- crates/rust-analyzer/src/main_loop.rs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index c38a25a44..7e00d58ec 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -426,6 +426,12 @@ fn loop_turn( }); } + if !loop_state.workspace_loaded + && world_state.feature_flags.get("notifications.workspace-loaded") + { + send_startup_progress(&connection.sender, loop_state, world_state); + } + if !loop_state.workspace_loaded && world_state.roots_to_scan == 0 && loop_state.pending_libraries.is_empty() @@ -439,7 +445,6 @@ fn loop_turn( move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ()) }); } - send_startup_progress(&connection.sender, loop_state, world_state); if state_changed { update_file_notifications_on_threadpool( @@ -708,9 +713,6 @@ fn send_startup_progress( loop_state: &mut LoopState, world_state: &WorldState, ) { - if !world_state.feature_flags.get("notifications.workspace-loaded") { - return; - } let total: usize = world_state.workspaces.iter().map(|it| it.n_packages()).sum(); let progress = total - world_state.roots_to_scan; if loop_state.roots_scanned_progress == Some(progress) { @@ -718,8 +720,8 @@ fn send_startup_progress( } loop_state.roots_scanned_progress = Some(progress); - match (progress, loop_state.workspace_loaded) { - (0, false) => { + match progress { + 0 => { let work_done_progress_create = request_new::( loop_state.next_request_id(), WorkDoneProgressCreateParams { @@ -737,7 +739,13 @@ fn send_startup_progress( }), ); } - (_, false) => send_startup_progress_notif( + progress if progress == total => send_startup_progress_notif( + sender, + WorkDoneProgress::End(WorkDoneProgressEnd { + message: Some(format!("rust-analyzer loaded, {} packages", progress)), + }), + ), + progress => send_startup_progress_notif( sender, WorkDoneProgress::Report(WorkDoneProgressReport { cancellable: None, @@ -745,12 +753,6 @@ fn send_startup_progress( percentage: Some(100.0 * progress as f64 / total as f64), }), ), - (_, true) => send_startup_progress_notif( - sender, - WorkDoneProgress::End(WorkDoneProgressEnd { - message: Some(format!("rust-analyzer loaded, {} packages", progress)), - }), - ), } } -- cgit v1.2.3 From 70ccda39414afa5bd5a92d3edec005e7830c6420 Mon Sep 17 00:00:00 2001 From: Steffen Lyngbaek Date: Sun, 15 Mar 2020 21:44:27 -0700 Subject: Fix broken tests - Handle case of no projects. The notification still needs to be posted --- crates/rust-analyzer/src/main_loop.rs | 55 ++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 7e00d58ec..917dfad78 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -714,45 +714,48 @@ fn send_startup_progress( world_state: &WorldState, ) { let total: usize = world_state.workspaces.iter().map(|it| it.n_packages()).sum(); + let prev_progress = loop_state.roots_scanned_progress; let progress = total - world_state.roots_to_scan; - if loop_state.roots_scanned_progress == Some(progress) { + if prev_progress == Some(progress) { return; } loop_state.roots_scanned_progress = Some(progress); - match progress { - 0 => { - let work_done_progress_create = request_new::( - loop_state.next_request_id(), - WorkDoneProgressCreateParams { - token: req::ProgressToken::String("rustAnalyzer/startup".into()), - }, - ); - sender.send(work_done_progress_create.into()).unwrap(); - send_startup_progress_notif( - sender, - WorkDoneProgress::Begin(WorkDoneProgressBegin { - title: "rust-analyzer".into(), - cancellable: None, - message: Some(format!("{}/{} packages", progress, total)), - percentage: Some(100.0 * progress as f64 / total as f64), - }), - ); - } - progress if progress == total => send_startup_progress_notif( + if prev_progress.is_none() { + let work_done_progress_create = request_new::( + loop_state.next_request_id(), + WorkDoneProgressCreateParams { + token: req::ProgressToken::String("rustAnalyzer/startup".into()), + }, + ); + sender.send(work_done_progress_create.into()).unwrap(); + send_startup_progress_notif( sender, - WorkDoneProgress::End(WorkDoneProgressEnd { - message: Some(format!("rust-analyzer loaded, {} packages", progress)), + WorkDoneProgress::Begin(WorkDoneProgressBegin { + title: "rust-analyzer".into(), + cancellable: None, + message: Some(format!("{}/{} packages", progress, total)), + percentage: Some(100.0 * progress as f64 / total as f64), }), - ), - progress => send_startup_progress_notif( + ); + } else if progress < total { + send_startup_progress_notif( sender, WorkDoneProgress::Report(WorkDoneProgressReport { cancellable: None, message: Some(format!("{}/{} packages", progress, total)), percentage: Some(100.0 * progress as f64 / total as f64), }), - ), + ) + } + + if progress == total { + send_startup_progress_notif( + sender, + WorkDoneProgress::End(WorkDoneProgressEnd { + message: Some(format!("rust-analyzer loaded, {} packages", progress)), + }), + ) } } -- cgit v1.2.3 From b6dec2eb1f8b84cbf892d43c7d4afc00e747328a Mon Sep 17 00:00:00 2001 From: Steffen Lyngbaek Date: Sun, 15 Mar 2020 23:32:28 -0700 Subject: Fix tests part 2... --- crates/rust-analyzer/src/main_loop.rs | 65 +++++++++++++++++------------------ 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 917dfad78..b410d3927 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -426,12 +426,6 @@ fn loop_turn( }); } - if !loop_state.workspace_loaded - && world_state.feature_flags.get("notifications.workspace-loaded") - { - send_startup_progress(&connection.sender, loop_state, world_state); - } - if !loop_state.workspace_loaded && world_state.roots_to_scan == 0 && loop_state.pending_libraries.is_empty() @@ -444,6 +438,9 @@ fn loop_turn( let snap = world_state.snapshot(); move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ()) }); + send_startup_progress(&connection.sender, loop_state, world_state); + } else if !loop_state.workspace_loaded { + send_startup_progress(&connection.sender, loop_state, world_state); } if state_changed { @@ -713,49 +710,49 @@ fn send_startup_progress( loop_state: &mut LoopState, world_state: &WorldState, ) { + if !world_state.feature_flags.get("notifications.workspace-loaded") { + return; + } + let total: usize = world_state.workspaces.iter().map(|it| it.n_packages()).sum(); let prev_progress = loop_state.roots_scanned_progress; let progress = total - world_state.roots_to_scan; - if prev_progress == Some(progress) { - return; - } loop_state.roots_scanned_progress = Some(progress); - if prev_progress.is_none() { - let work_done_progress_create = request_new::( - loop_state.next_request_id(), - WorkDoneProgressCreateParams { - token: req::ProgressToken::String("rustAnalyzer/startup".into()), - }, - ); - sender.send(work_done_progress_create.into()).unwrap(); - send_startup_progress_notif( - sender, - WorkDoneProgress::Begin(WorkDoneProgressBegin { - title: "rust-analyzer".into(), - cancellable: None, - message: Some(format!("{}/{} packages", progress, total)), - percentage: Some(100.0 * progress as f64 / total as f64), - }), - ); - } else if progress < total { - send_startup_progress_notif( + match (prev_progress, loop_state.workspace_loaded) { + (None, false) => { + let work_done_progress_create = request_new::( + loop_state.next_request_id(), + WorkDoneProgressCreateParams { + token: req::ProgressToken::String("rustAnalyzer/startup".into()), + }, + ); + sender.send(work_done_progress_create.into()).unwrap(); + send_startup_progress_notif( + sender, + WorkDoneProgress::Begin(WorkDoneProgressBegin { + title: "rust-analyzer".into(), + cancellable: None, + message: Some(format!("{}/{} packages", progress, total)), + percentage: Some(100.0 * progress as f64 / total as f64), + }), + ); + } + (Some(prev), false) if progress != prev => send_startup_progress_notif( sender, WorkDoneProgress::Report(WorkDoneProgressReport { cancellable: None, message: Some(format!("{}/{} packages", progress, total)), percentage: Some(100.0 * progress as f64 / total as f64), }), - ) - } - - if progress == total { - send_startup_progress_notif( + ), + (_, true) => send_startup_progress_notif( sender, WorkDoneProgress::End(WorkDoneProgressEnd { message: Some(format!("rust-analyzer loaded, {} packages", progress)), }), - ) + ), + _ => {} } } -- cgit v1.2.3