From 2f8126fcace3c5e7db01c755b91eb45a9c632cfd Mon Sep 17 00:00:00 2001 From: veetaha Date: Sun, 10 May 2020 06:44:02 +0300 Subject: [PATCH] Migrate flycheck to fully-lsp-compatible progress reports (introduce ra_progress crate) --- crates/ra_ide/src/lib.rs | 7 +- crates/ra_ide/src/prime_caches.rs | 9 +- crates/rust-analyzer/src/main_loop.rs | 133 +++++------------- .../rust-analyzer/src/main_loop/lsp_utils.rs | 46 ++++++ .../rust-analyzer/src/main_loop/progress.rs | 129 +++++++++++++++++ .../tests/heavy_tests/support.rs | 2 +- 6 files changed, 225 insertions(+), 101 deletions(-) create mode 100644 crates/rust-analyzer/src/main_loop/lsp_utils.rs create mode 100644 crates/rust-analyzer/src/main_loop/progress.rs diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 51dc1f041ca5..6704467d9338 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -241,8 +241,11 @@ impl Analysis { self.with_db(|db| status::status(&*db)) } - pub fn prime_caches(&self, files: Vec) -> Cancelable<()> { - self.with_db(|db| prime_caches::prime_caches(db, files)) + pub fn prime_caches

(&self, files: Vec, report_progress: P) -> Cancelable<()> + where + P: FnMut(usize) + std::panic::UnwindSafe, + { + self.with_db(|db| prime_caches::prime_caches(db, files, report_progress)) } /// Gets the text of the source file. diff --git a/crates/ra_ide/src/prime_caches.rs b/crates/ra_ide/src/prime_caches.rs index c5ab5a1d87ba..f605959898b6 100644 --- a/crates/ra_ide/src/prime_caches.rs +++ b/crates/ra_ide/src/prime_caches.rs @@ -5,8 +5,13 @@ use crate::{FileId, RootDatabase}; -pub(crate) fn prime_caches(db: &RootDatabase, files: Vec) { - for file in files { +pub(crate) fn prime_caches( + db: &RootDatabase, + files: Vec, + mut report_progress: impl FnMut(usize), +) { + for (i, file) in files.into_iter().enumerate() { let _ = crate::syntax_highlighting::highlight(db, file, None, false); + report_progress(i); } } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index f0aaaa21ead8..590836c1eef1 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -4,6 +4,8 @@ mod handlers; mod subscriptions; pub(crate) mod pending_requests; +mod progress; +mod lsp_utils; use std::{ borrow::Cow, @@ -44,6 +46,9 @@ use crate::{ }, Result, }; +pub use lsp_utils::show_message; +use lsp_utils::{is_canceled, notification_cast, notification_is, notification_new, request_new}; +use progress::{IsDone, PrimeCachesProgressNotifier, WorkspaceAnalysisProgressNotifier}; #[derive(Debug)] pub struct LspError { @@ -90,6 +95,7 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { } let mut loop_state = LoopState::default(); + let mut global_state = { let workspaces = { if config.linked_projects.is_empty() && config.notifications.cargo_toml_not_found { @@ -164,6 +170,12 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { }; loop_state.roots_total = global_state.vfs.read().n_roots(); + loop_state.roots_scanned = 0; + loop_state.roots_progress = Some(WorkspaceAnalysisProgressNotifier::begin( + connection.sender.clone(), + loop_state.next_request_id(), + loop_state.roots_total, + )); let pool = ThreadPool::default(); let (task_sender, task_receiver) = unbounded::(); @@ -271,7 +283,7 @@ struct LoopState { pending_requests: PendingRequests, subscriptions: Subscriptions, workspace_loaded: bool, - roots_progress_reported: Option, + roots_progress: Option, roots_scanned: usize, roots_total: usize, configuration_request_id: Option, @@ -372,7 +384,7 @@ fn loop_turn( } if show_progress { - send_startup_progress(&connection.sender, loop_state); + send_workspace_analisys_progress(loop_state); } if state_changed && loop_state.workspace_loaded { @@ -385,7 +397,22 @@ fn loop_turn( pool.execute({ let subs = loop_state.subscriptions.subscriptions(); let snap = global_state.snapshot(); - move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ()) + + let total = subs.len(); + + let mut progress = PrimeCachesProgressNotifier::begin( + connection.sender.clone(), + loop_state.next_request_id(), + total, + ); + + move || { + snap.analysis() + .prime_caches(subs, move |i| { + progress.report(i + 1); + }) + .unwrap_or_else(|_: Canceled| ()); + } }); } @@ -744,55 +771,12 @@ fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender, state: } } -fn send_startup_progress(sender: &Sender, loop_state: &mut LoopState) { - let total: usize = loop_state.roots_total; - let prev = loop_state.roots_progress_reported; - let progress = loop_state.roots_scanned; - loop_state.roots_progress_reported = Some(progress); - - match (prev, loop_state.workspace_loaded) { - (None, false) => { - let work_done_progress_create = request_new::( - loop_state.next_request_id(), - WorkDoneProgressCreateParams { - token: lsp_types::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), - }), - ); +fn send_workspace_analisys_progress(loop_state: &mut LoopState) { + if let Some(progress) = &mut loop_state.roots_progress { + if loop_state.workspace_loaded || progress.report(loop_state.roots_scanned) == IsDone(true) + { + loop_state.roots_progress = None; } - (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), - }), - ), - (_, 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::(lsp_types::ProgressParams { - token: lsp_types::ProgressToken::String("rustAnalyzer/startup".into()), - value: lsp_types::ProgressParamsValue::WorkDone(work_done_progress), - }); - sender.send(notif.into()).unwrap(); } } @@ -918,7 +902,7 @@ where } } Err(e) => { - if is_canceled(&e) { + if is_canceled(&*e) { Response::new_err( id, ErrorCode::ContentModified as i32, @@ -945,7 +929,7 @@ fn update_file_notifications_on_threadpool( for file_id in subscriptions { match handlers::publish_diagnostics(&world, file_id) { Err(e) => { - if !is_canceled(&e) { + if !is_canceled(&*e) { log::error!("failed to compute diagnostics: {:?}", e); } } @@ -958,49 +942,6 @@ fn update_file_notifications_on_threadpool( } } -pub fn show_message( - typ: lsp_types::MessageType, - message: impl Into, - sender: &Sender, -) { - let message = message.into(); - let params = lsp_types::ShowMessageParams { typ, message }; - 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) -} - -fn request_new(id: RequestId, params: R::Params) -> Request -where - R: lsp_types::request::Request, - R::Params: Serialize, -{ - Request::new(id, R::METHOD.to_string(), params) -} - #[cfg(test)] mod tests { use std::borrow::Cow; diff --git a/crates/rust-analyzer/src/main_loop/lsp_utils.rs b/crates/rust-analyzer/src/main_loop/lsp_utils.rs new file mode 100644 index 000000000000..fc008cba5060 --- /dev/null +++ b/crates/rust-analyzer/src/main_loop/lsp_utils.rs @@ -0,0 +1,46 @@ +use crossbeam_channel::Sender; +use lsp_server::{Message, Notification, Request, RequestId}; +use ra_db::Canceled; +use serde::{de::DeserializeOwned, Serialize}; +use std::error::Error; + +pub fn show_message(typ: lsp_types::MessageType, message: impl Into, sender: &Sender) { + let message = message.into(); + let params = lsp_types::ShowMessageParams { typ, message }; + let not = notification_new::(params); + sender.send(not.into()).unwrap(); +} + +pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool { + e.downcast_ref::().is_some() +} + +pub(crate) fn notification_is( + notification: &Notification, +) -> bool { + notification.method == N::METHOD +} + +pub(crate) fn notification_cast(notification: Notification) -> Result +where + N: lsp_types::notification::Notification, + N::Params: DeserializeOwned, +{ + notification.extract(N::METHOD) +} + +pub(crate) fn notification_new(params: N::Params) -> Notification +where + N: lsp_types::notification::Notification, + N::Params: Serialize, +{ + Notification::new(N::METHOD.to_string(), params) +} + +pub(crate) fn request_new(id: RequestId, params: R::Params) -> Request +where + R: lsp_types::request::Request, + R::Params: Serialize, +{ + Request::new(id, R::METHOD.to_string(), params) +} diff --git a/crates/rust-analyzer/src/main_loop/progress.rs b/crates/rust-analyzer/src/main_loop/progress.rs new file mode 100644 index 000000000000..610e026ca286 --- /dev/null +++ b/crates/rust-analyzer/src/main_loop/progress.rs @@ -0,0 +1,129 @@ +use super::lsp_utils::{notification_new, request_new}; +use crossbeam_channel::Sender; +use lsp_server::{Message, RequestId}; +use lsp_types::{ + WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd, + WorkDoneProgressReport, +}; + +const PRIME_CACHES_PROGRESS_TOKEN: &str = "rustAnalyzer/primeCaches"; +const WORKSPACE_ANALYSIS_PROGRESS_TOKEN: &str = "rustAnalyzer/workspaceAnalysis"; + +#[derive(Debug)] +pub(crate) struct PrimeCachesProgressNotifier(ProgressNotifier); + +impl Drop for PrimeCachesProgressNotifier { + fn drop(&mut self) { + self.0.end("done priming caches".to_owned()); + } +} + +impl PrimeCachesProgressNotifier { + pub(crate) fn begin(sender: Sender, req_id: RequestId, total: usize) -> Self { + let me = Self(ProgressNotifier { + sender, + processed: 0, + total, + token: PRIME_CACHES_PROGRESS_TOKEN, + label: "priming caches", + }); + me.0.begin(req_id); + me + } + + pub(crate) fn report(&mut self, processed: usize) -> IsDone { + self.0.report(processed) + } +} + +#[derive(Debug)] +pub(crate) struct WorkspaceAnalysisProgressNotifier(ProgressNotifier); + +impl Drop for WorkspaceAnalysisProgressNotifier { + fn drop(&mut self) { + self.0.end("done analyzing workspace".to_owned()); + } +} + +impl WorkspaceAnalysisProgressNotifier { + pub(crate) fn begin(sender: Sender, req_id: RequestId, total: usize) -> Self { + let me = Self(ProgressNotifier { + sender, + total, + processed: 0, + token: WORKSPACE_ANALYSIS_PROGRESS_TOKEN, + label: "analyzing packages", + }); + me.0.begin(req_id); + me + } + + pub(crate) fn report(&mut self, processed: usize) -> IsDone { + self.0.report(processed) + } +} + +#[derive(Debug, PartialEq, Eq)] +pub struct IsDone(pub bool); + +#[derive(Debug)] +struct ProgressNotifier { + sender: Sender, + token: &'static str, + label: &'static str, + processed: usize, + total: usize, +} + +impl ProgressNotifier { + fn begin(&self, req_id: RequestId) { + let create_req = request_new::( + req_id, + WorkDoneProgressCreateParams { + token: lsp_types::ProgressToken::String(self.token.to_owned()), + }, + ); + self.sender.send(create_req.into()).unwrap(); + self.send_notification(WorkDoneProgress::Begin(WorkDoneProgressBegin { + cancellable: None, + title: "rust-analyzer".to_owned(), + percentage: Some(self.percentage()), + message: Some(self.create_progress_message()), + })); + } + + fn report(&mut self, processed: usize) -> IsDone { + if self.processed != processed { + self.processed = processed; + + self.send_notification(WorkDoneProgress::Report(WorkDoneProgressReport { + cancellable: None, + percentage: Some(self.percentage()), + message: Some(self.create_progress_message()), + })); + } + IsDone(processed >= self.total) + } + + fn end(&mut self, message: String) { + self.send_notification(WorkDoneProgress::End(WorkDoneProgressEnd { + message: Some(message), + })); + } + + fn send_notification(&self, progress: WorkDoneProgress) { + let notif = notification_new::(lsp_types::ProgressParams { + token: lsp_types::ProgressToken::String(self.token.to_owned()), + value: lsp_types::ProgressParamsValue::WorkDone(progress), + }); + self.sender.send(notif.into()).unwrap(); + } + + fn create_progress_message(&self) -> String { + format!("{} ({}/{})", self.label, self.processed, self.total) + } + + fn percentage(&self) -> f64 { + (100 * self.processed) as f64 / self.total as f64 + } +} diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs index 30d03b622b91..e5f69835aaf8 100644 --- a/crates/rust-analyzer/tests/heavy_tests/support.rs +++ b/crates/rust-analyzer/tests/heavy_tests/support.rs @@ -212,7 +212,7 @@ impl Server { ProgressParams { token: lsp_types::ProgressToken::String(ref token), value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(_)), - } if token == "rustAnalyzer/startup" => true, + } if token == "rustAnalyzer/workspaceAnalysis" => true, _ => false, } }