From 2f8126fcace3c5e7db01c755b91eb45a9c632cfd Mon Sep 17 00:00:00 2001 From: veetaha Date: Sun, 10 May 2020 06:44:02 +0300 Subject: 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 ++++++---------------- crates/rust-analyzer/src/main_loop/lsp_utils.rs | 46 ++++++++ crates/rust-analyzer/src/main_loop/progress.rs | 129 +++++++++++++++++++++ crates/rust-analyzer/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 51dc1f041..6704467d9 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 c5ab5a1d8..f60595989 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 f0aaaa21e..590836c1e 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 000000000..fc008cba5 --- /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 000000000..610e026ca --- /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 30d03b622..e5f69835a 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, } } -- cgit v1.2.3 From 76c1160ffa626fc5f07b309420e6666eb79a3311 Mon Sep 17 00:00:00 2001 From: veetaha Date: Sun, 10 May 2020 18:35:33 +0300 Subject: Migrate flycheck to fully-lsp-compatible progress reports (introduce ra_progress crate) --- Cargo.lock | 10 + crates/ra_flycheck/Cargo.toml | 1 + crates/ra_flycheck/src/lib.rs | 62 +++--- crates/ra_ide/Cargo.toml | 1 + crates/ra_ide/src/lib.rs | 7 +- crates/ra_ide/src/prime_caches.rs | 9 +- crates/ra_progress/Cargo.toml | 8 + crates/ra_progress/src/lib.rs | 129 +++++++++++++ crates/rust-analyzer/Cargo.toml | 1 + crates/rust-analyzer/src/global_state.rs | 30 ++- crates/rust-analyzer/src/main_loop.rs | 223 +++++++++++++--------- crates/rust-analyzer/src/main_loop/lsp_utils.rs | 8 +- crates/rust-analyzer/src/main_loop/progress.rs | 129 ------------- crates/rust-analyzer/tests/heavy_tests/support.rs | 2 +- editors/code/src/main.ts | 3 - editors/code/src/status_display.ts | 100 ---------- 16 files changed, 361 insertions(+), 362 deletions(-) create mode 100644 crates/ra_progress/Cargo.toml create mode 100644 crates/ra_progress/src/lib.rs delete mode 100644 crates/rust-analyzer/src/main_loop/progress.rs delete mode 100644 editors/code/src/status_display.ts diff --git a/Cargo.lock b/Cargo.lock index f6d5b900f..08e9c10d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -963,6 +963,7 @@ dependencies = [ "crossbeam-channel", "jod-thread", "log", + "ra_progress", "ra_toolchain", "serde_json", ] @@ -1075,6 +1076,7 @@ dependencies = [ "ra_hir", "ra_ide_db", "ra_prof", + "ra_progress", "ra_syntax", "ra_text_edit", "rand", @@ -1162,6 +1164,13 @@ dependencies = [ "ra_arena", ] +[[package]] +name = "ra_progress" +version = "0.1.0" +dependencies = [ + "crossbeam-channel", +] + [[package]] name = "ra_project_model" version = "0.1.0" @@ -1388,6 +1397,7 @@ dependencies = [ "ra_mbe", "ra_proc_macro_srv", "ra_prof", + "ra_progress", "ra_project_model", "ra_syntax", "ra_text_edit", diff --git a/crates/ra_flycheck/Cargo.toml b/crates/ra_flycheck/Cargo.toml index 1aa39bade..838973963 100644 --- a/crates/ra_flycheck/Cargo.toml +++ b/crates/ra_flycheck/Cargo.toml @@ -14,3 +14,4 @@ cargo_metadata = "0.10.0" serde_json = "1.0.48" jod-thread = "0.1.1" ra_toolchain = { path = "../ra_toolchain" } +ra_progress = { path = "../ra_progress" } diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs index 6c4170529..7b9f48eb0 100644 --- a/crates/ra_flycheck/src/lib.rs +++ b/crates/ra_flycheck/src/lib.rs @@ -3,6 +3,7 @@ //! LSP diagnostics based on the output of the command. use std::{ + fmt, io::{self, BufReader}, path::PathBuf, process::{Command, Stdio}, @@ -16,6 +17,9 @@ pub use cargo_metadata::diagnostic::{ Applicability, Diagnostic, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion, }; +type Progress = ra_progress::Progress<(), String>; +type ProgressSource = ra_progress::ProgressSource<(), String>; + #[derive(Clone, Debug, PartialEq, Eq)] pub enum FlycheckConfig { CargoCommand { @@ -31,6 +35,17 @@ pub enum FlycheckConfig { }, } +impl fmt::Display for FlycheckConfig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {}", command), + FlycheckConfig::CustomCommand { command, args } => { + write!(f, "{} {}", command, args.join(" ")) + } + } + } +} + /// Flycheck wraps the shared state and communication machinery used for /// running `cargo check` (or other compatible command) and providing /// diagnostics based on the output. @@ -44,11 +59,15 @@ pub struct Flycheck { } impl Flycheck { - pub fn new(config: FlycheckConfig, workspace_root: PathBuf) -> Flycheck { + pub fn new( + config: FlycheckConfig, + workspace_root: PathBuf, + progress_src: ProgressSource, + ) -> Flycheck { let (task_send, task_recv) = unbounded::(); let (cmd_send, cmd_recv) = unbounded::(); let handle = jod_thread::spawn(move || { - FlycheckThread::new(config, workspace_root).run(&task_send, &cmd_recv); + FlycheckThread::new(config, workspace_root, progress_src).run(&task_send, &cmd_recv); }); Flycheck { task_recv, cmd_send, handle } } @@ -66,16 +85,6 @@ pub enum CheckTask { /// Request adding a diagnostic with fixes included to a file AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic }, - - /// Request check progress notification to client - Status(Status), -} - -#[derive(Debug)] -pub enum Status { - Being, - Progress(String), - End, } pub enum CheckCommand { @@ -87,6 +96,8 @@ struct FlycheckThread { config: FlycheckConfig, workspace_root: PathBuf, last_update_req: Option, + progress_src: ProgressSource, + progress: Option, // XXX: drop order is significant message_recv: Receiver, /// WatchThread exists to wrap around the communication needed to be able to @@ -98,11 +109,17 @@ struct FlycheckThread { } impl FlycheckThread { - fn new(config: FlycheckConfig, workspace_root: PathBuf) -> FlycheckThread { + fn new( + config: FlycheckConfig, + workspace_root: PathBuf, + progress_src: ProgressSource, + ) -> FlycheckThread { FlycheckThread { config, workspace_root, + progress_src, last_update_req: None, + progress: None, message_recv: never(), check_process: None, } @@ -140,9 +157,9 @@ impl FlycheckThread { } } - fn clean_previous_results(&self, task_send: &Sender) { + fn clean_previous_results(&mut self, task_send: &Sender) { task_send.send(CheckTask::ClearDiagnostics).unwrap(); - task_send.send(CheckTask::Status(Status::End)).unwrap(); + self.progress = None; } fn should_recheck(&mut self) -> bool { @@ -161,18 +178,17 @@ impl FlycheckThread { } } - fn handle_message(&self, msg: CheckEvent, task_send: &Sender) { + fn handle_message(&mut self, msg: CheckEvent, task_send: &Sender) { match msg { CheckEvent::Begin => { - task_send.send(CheckTask::Status(Status::Being)).unwrap(); + self.progress = Some(self.progress_src.begin(())); } - - CheckEvent::End => { - task_send.send(CheckTask::Status(Status::End)).unwrap(); - } - + CheckEvent::End => self.progress = None, CheckEvent::Msg(Message::CompilerArtifact(msg)) => { - task_send.send(CheckTask::Status(Status::Progress(msg.target.name))).unwrap(); + self.progress + .as_mut() + .expect("check process reported progress without the 'Begin' notification") + .report(msg.target.name); } CheckEvent::Msg(Message::CompilerMessage(msg)) => { diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml index 05c940605..722652fb2 100644 --- a/crates/ra_ide/Cargo.toml +++ b/crates/ra_ide/Cargo.toml @@ -29,6 +29,7 @@ ra_fmt = { path = "../ra_fmt" } ra_prof = { path = "../ra_prof" } test_utils = { path = "../test_utils" } ra_assists = { path = "../ra_assists" } +ra_progress = { path = "../ra_progress" } # ra_ide should depend only on the top-level `hir` package. if you need # something from some `hir_xxx` subpackage, reexport the API via `hir`. diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 6704467d9..51dc1f041 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -241,11 +241,8 @@ impl Analysis { self.with_db(|db| status::status(&*db)) } - 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)) + pub fn prime_caches(&self, files: Vec) -> Cancelable<()> { + self.with_db(|db| prime_caches::prime_caches(db, files)) } /// 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 f60595989..c5ab5a1d8 100644 --- a/crates/ra_ide/src/prime_caches.rs +++ b/crates/ra_ide/src/prime_caches.rs @@ -5,13 +5,8 @@ use crate::{FileId, RootDatabase}; -pub(crate) fn prime_caches( - db: &RootDatabase, - files: Vec, - mut report_progress: impl FnMut(usize), -) { - for (i, file) in files.into_iter().enumerate() { +pub(crate) fn prime_caches(db: &RootDatabase, files: Vec) { + for file in files { let _ = crate::syntax_highlighting::highlight(db, file, None, false); - report_progress(i); } } diff --git a/crates/ra_progress/Cargo.toml b/crates/ra_progress/Cargo.toml new file mode 100644 index 000000000..c7f7c6dd3 --- /dev/null +++ b/crates/ra_progress/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "ra_progress" +version = "0.1.0" +authors = ["rust-analyzer developers"] +edition = "2018" + +[dependencies] +crossbeam-channel = { version = "0.4" } diff --git a/crates/ra_progress/src/lib.rs b/crates/ra_progress/src/lib.rs new file mode 100644 index 000000000..0ff1f846c --- /dev/null +++ b/crates/ra_progress/src/lib.rs @@ -0,0 +1,129 @@ +//! General-purpose instrumentation for progress reporting. +//! +//! Note: +//! Most of the methods accept `&mut self` just to be more restrictive (for forward compat) +//! even tho for some of them we can weaken this requirement to shared reference (`&self`). + +use crossbeam_channel::Receiver; +use std::fmt; + +#[derive(Debug)] +pub enum ProgressStatus { + Begin(B), + Progress(P), + End, +} + +pub struct Progress(Option>>); +impl Progress { + pub fn report(&mut self, payload: P) { + self.report_with(|| payload); + } + + pub fn report_with(&mut self, payload: impl FnOnce() -> P) { + self.send_status(|| ProgressStatus::Progress(payload())); + } + + fn send_status(&self, status: impl FnOnce() -> ProgressStatus) { + if let Some(sender) = &self.0 { + sender.try_send(status()).expect("progress report must not block"); + } + } +} + +impl Drop for Progress { + fn drop(&mut self) { + self.send_status(|| ProgressStatus::End); + } +} + +pub struct ProgressSource(Option>>); +impl ProgressSource { + pub fn real_if(real: bool) -> (Receiver>, Self) { + if real { + let (sender, receiver) = crossbeam_channel::unbounded(); + (receiver, Self(Some(sender))) + } else { + (crossbeam_channel::never(), Self(None)) + } + } + + pub fn begin(&mut self, payload: B) -> Progress { + self.begin_with(|| payload) + } + + pub fn begin_with(&mut self, payload: impl FnOnce() -> B) -> Progress { + let progress = Progress(self.0.clone()); + progress.send_status(|| ProgressStatus::Begin(payload())); + progress + } +} + +impl Clone for ProgressSource { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl fmt::Debug for ProgressSource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("ProgressSource").field(&self.0).finish() + } +} + +pub type U32ProgressStatus = ProgressStatus; + +#[derive(Debug)] +pub struct U32ProgressReport { + pub processed: u32, + pub total: u32, +} +impl U32ProgressReport { + pub fn percentage(&self) -> f64 { + f64::from(100 * self.processed) / f64::from(self.total) + } + pub fn to_message(&self, prefix: &str, unit: &str) -> String { + format!("{} ({}/{} {})", prefix, self.processed, self.total, unit) + } +} + +pub struct U32Progress { + inner: Progress, + processed: u32, + total: u32, +} + +#[derive(Debug, Eq, PartialEq)] +pub struct IsDone(pub bool); + +impl U32Progress { + pub fn report(&mut self, new_processed: u32) -> IsDone { + if self.processed < new_processed { + self.processed = new_processed; + self.inner.report(U32ProgressReport { processed: new_processed, total: self.total }); + } + IsDone(self.processed >= self.total) + } +} + +#[derive(Clone)] +pub struct U32ProgressSource { + inner: ProgressSource, +} + +impl U32ProgressSource { + pub fn real_if( + real: bool, + ) -> (Receiver>, Self) { + let (recv, inner) = ProgressSource::real_if(real); + (recv, Self { inner }) + } + + pub fn begin(&mut self, initial: u32, total: u32) -> U32Progress { + U32Progress { + inner: self.inner.begin(U32ProgressReport { processed: initial, total }), + processed: initial, + total, + } + } +} diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 458089e53..22f6b45dd 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -48,6 +48,7 @@ hir = { path = "../ra_hir", package = "ra_hir" } hir_def = { path = "../ra_hir_def", package = "ra_hir_def" } hir_ty = { path = "../ra_hir_ty", package = "ra_hir_ty" } ra_proc_macro_srv = { path = "../ra_proc_macro_srv" } +ra_progress = { path = "../ra_progress" } [target.'cfg(windows)'.dependencies] winapi = "0.3.8" diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 1527c9947..2d854cecf 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -26,14 +26,19 @@ use crate::{ LspError, Result, }; use ra_db::{CrateId, ExternSourceId}; +use ra_progress::{ProgressSource, ProgressStatus}; use rustc_hash::{FxHashMap, FxHashSet}; -fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) -> Option { +fn create_flycheck( + workspaces: &[ProjectWorkspace], + config: &FlycheckConfig, + progress_src: &ProgressSource<(), String>, +) -> Option { // FIXME: Figure out the multi-workspace situation - workspaces.iter().find_map(|w| match w { + workspaces.iter().find_map(move |w| match w { ProjectWorkspace::Cargo { cargo, .. } => { let cargo_project_root = cargo.workspace_root().to_path_buf(); - Some(Flycheck::new(config.clone(), cargo_project_root)) + Some(Flycheck::new(config.clone(), cargo_project_root, progress_src.clone())) } ProjectWorkspace::Json { .. } => { log::warn!("Cargo check watching only supported for cargo workspaces, disabling"); @@ -59,6 +64,8 @@ pub struct GlobalState { pub flycheck: Option, pub diagnostics: DiagnosticCollection, pub proc_macro_client: ProcMacroClient, + pub flycheck_progress_src: ProgressSource<(), String>, + pub flycheck_progress_receiver: Receiver>, } /// An immutable snapshot of the world's state at a point in time. @@ -158,7 +165,12 @@ impl GlobalState { } change.set_crate_graph(crate_graph); - let flycheck = config.check.as_ref().and_then(|c| create_flycheck(&workspaces, c)); + let (flycheck_progress_receiver, flycheck_progress_src) = + ProgressSource::real_if(config.client_caps.work_done_progress); + let flycheck = config + .check + .as_ref() + .and_then(|c| create_flycheck(&workspaces, c, &flycheck_progress_src)); let mut analysis_host = AnalysisHost::new(lru_capacity); analysis_host.apply_change(change); @@ -171,6 +183,8 @@ impl GlobalState { task_receiver, latest_requests: Default::default(), flycheck, + flycheck_progress_src, + flycheck_progress_receiver, diagnostics: Default::default(), proc_macro_client, } @@ -179,8 +193,10 @@ impl GlobalState { pub fn update_configuration(&mut self, config: Config) { self.analysis_host.update_lru_capacity(config.lru_capacity); if config.check != self.config.check { - self.flycheck = - config.check.as_ref().and_then(|it| create_flycheck(&self.workspaces, it)); + self.flycheck = config + .check + .as_ref() + .and_then(|it| create_flycheck(&self.workspaces, it, &self.flycheck_progress_src)); } self.config = config; @@ -188,7 +204,7 @@ impl GlobalState { /// Returns a vec of libraries /// FIXME: better API here - pub fn process_changes(&mut self, roots_scanned: &mut usize) -> bool { + pub fn process_changes(&mut self, roots_scanned: &mut u32) -> bool { let changes = self.vfs.write().commit_changes(); if changes.is_empty() { return false; diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 590836c1e..740c52e21 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -4,11 +4,11 @@ mod handlers; mod subscriptions; pub(crate) mod pending_requests; -mod progress; mod lsp_utils; use std::{ borrow::Cow, + convert::TryFrom, env, error::Error, fmt, @@ -20,12 +20,8 @@ use std::{ use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; -use lsp_types::{ - DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress, - WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd, - WorkDoneProgressReport, -}; -use ra_flycheck::{CheckTask, Status}; +use lsp_types::{DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent}; +use ra_flycheck::CheckTask; use ra_ide::{Canceled, FileId, LineIndex}; use ra_prof::profile; use ra_project_model::{PackageRoot, ProjectWorkspace}; @@ -48,7 +44,12 @@ use crate::{ }; pub use lsp_utils::show_message; use lsp_utils::{is_canceled, notification_cast, notification_is, notification_new, request_new}; -use progress::{IsDone, PrimeCachesProgressNotifier, WorkspaceAnalysisProgressNotifier}; +use ra_progress::{ + IsDone, ProgressStatus, U32Progress, U32ProgressReport, U32ProgressSource, U32ProgressStatus, +}; + +const FLYCHECK_PROGRESS_TOKEN: &str = "rustAnalyzer/flycheck"; +const ROOTS_SCANNED_PROGRESS_TOKEN: &str = "rustAnalyzer/rootsScanned"; #[derive(Debug)] pub struct LspError { @@ -95,7 +96,6 @@ 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 { @@ -169,13 +169,16 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { GlobalState::new(workspaces, config.lru_capacity, &globs, config) }; - loop_state.roots_total = global_state.vfs.read().n_roots(); + loop_state.roots_total = u32::try_from(global_state.vfs.read().n_roots()) + .expect("Wow, your project is so huge, that it cannot fit into u32..."); + 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 mut roots_scanned_progress_receiver = { + let (recv, mut progress_src) = + U32ProgressSource::real_if(global_state.config.client_caps.work_done_progress); + loop_state.roots_progress = Some(progress_src.begin(0, loop_state.roots_total)); + recv + }; let pool = ThreadPool::default(); let (task_sender, task_receiver) = unbounded::(); @@ -198,6 +201,18 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { recv(global_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task { Ok(task) => Event::CheckWatcher(task), Err(RecvError) => return Err("check watcher died".into()), + }, + recv(global_state.flycheck_progress_receiver) -> status => match status { + Ok(status) => Event::ProgressReport(ProgressReport::Flycheck(status)), + Err(RecvError) => return Err("check watcher died".into()), + }, + recv(roots_scanned_progress_receiver) -> status => match status { + Ok(status) => Event::ProgressReport(ProgressReport::RootsScanned(status)), + Err(RecvError) => { + // Roots analysis has finished, we no longer need this receiver + roots_scanned_progress_receiver = never(); + continue; + } } }; if let Event::Msg(Message::Request(req)) = &event { @@ -228,7 +243,7 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { #[derive(Debug)] enum Task { Respond(Response), - Notify(Notification), + SendMessage(Message), Diagnostic(DiagnosticTask), } @@ -237,6 +252,13 @@ enum Event { Task(Task), Vfs(VfsTask), CheckWatcher(CheckTask), + ProgressReport(ProgressReport), +} + +#[derive(Debug)] +enum ProgressReport { + Flycheck(ProgressStatus<(), String>), + RootsScanned(U32ProgressStatus), } impl fmt::Debug for Event { @@ -253,7 +275,7 @@ impl fmt::Debug for Event { return debug_verbose_not(not, f); } } - Event::Task(Task::Notify(not)) => { + Event::Task(Task::SendMessage(Message::Notification(not))) => { if notification_is::(not) { return debug_verbose_not(not, f); } @@ -272,20 +294,21 @@ impl fmt::Debug for Event { Event::Task(it) => fmt::Debug::fmt(it, f), Event::Vfs(it) => fmt::Debug::fmt(it, f), Event::CheckWatcher(it) => fmt::Debug::fmt(it, f), + Event::ProgressReport(it) => fmt::Debug::fmt(it, f), } } } -#[derive(Debug, Default)] +#[derive(Default)] struct LoopState { next_request_id: u64, pending_responses: FxHashSet, pending_requests: PendingRequests, subscriptions: Subscriptions, workspace_loaded: bool, - roots_progress: Option, - roots_scanned: usize, - roots_total: usize, + roots_progress: Option, + roots_scanned: u32, + roots_total: u32, configuration_request_id: Option, } @@ -326,6 +349,9 @@ fn loop_turn( global_state.vfs.write().handle_task(task); } Event::CheckWatcher(task) => on_check_task(task, global_state, task_sender)?, + Event::ProgressReport(report) => { + on_progress_report(report, task_sender, loop_state, global_state) + } Event::Msg(msg) => match msg { Message::Request(req) => on_request( global_state, @@ -384,7 +410,13 @@ fn loop_turn( } if show_progress { - send_workspace_analisys_progress(loop_state); + 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; + } + } } if state_changed && loop_state.workspace_loaded { @@ -397,22 +429,7 @@ fn loop_turn( pool.execute({ let subs = loop_state.subscriptions.subscriptions(); let snap = global_state.snapshot(); - - 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| ()); - } + move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ()) }); } @@ -431,6 +448,87 @@ fn loop_turn( Ok(()) } +fn on_progress_report( + report: ProgressReport, + task_sender: &Sender, + loop_state: &mut LoopState, + global_state: &GlobalState, +) { + let end_report = + || lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message: None }); + let mut create_progress = |token: &'static str| { + let create_progress_req = request_new::( + loop_state.next_request_id(), + lsp_types::WorkDoneProgressCreateParams { + token: lsp_types::ProgressToken::String(token.to_string()), + }, + ); + task_sender.send(Task::SendMessage(create_progress_req.into())).unwrap(); + }; + + let (token, progress) = match report { + ProgressReport::Flycheck(status) => { + let command = global_state + .config + .check + .as_ref() + .expect("There should be config, since flycheck is active"); + + let progress = match status { + ProgressStatus::Begin(()) => { + create_progress(FLYCHECK_PROGRESS_TOKEN); + lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { + title: "".to_string(), + cancellable: Some(false), + message: Some(command.to_string()), + percentage: None, + }) + } + ProgressStatus::Progress(target) => { + lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport { + cancellable: Some(false), + message: Some(format!("{} [{}]", command, target)), + percentage: None, + }) + } + ProgressStatus::End => end_report(), + }; + (FLYCHECK_PROGRESS_TOKEN, progress) + } + ProgressReport::RootsScanned(status) => { + fn to_message(report: &U32ProgressReport) -> String { + report.to_message("analyzing the workspace", "packages") + } + let progress = match status { + ProgressStatus::Begin(report) => { + create_progress(ROOTS_SCANNED_PROGRESS_TOKEN); + lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { + title: "rust-analyzer".to_string(), + cancellable: Some(false), + message: Some(to_message(&report)), + percentage: Some(report.percentage()), + }) + } + ProgressStatus::Progress(report) => { + lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport { + cancellable: Some(false), + message: Some(to_message(&report)), + percentage: Some(report.percentage()), + }) + } + ProgressStatus::End => end_report(), + }; + (ROOTS_SCANNED_PROGRESS_TOKEN, progress) + } + }; + let params = lsp_types::ProgressParams { + token: lsp_types::ProgressToken::String(token.to_string()), + value: lsp_types::ProgressParamsValue::WorkDone(progress), + }; + let not = notification_new::(params); + task_sender.send(Task::SendMessage(not.into())).unwrap() +} + fn on_task( task: Task, msg_sender: &Sender, @@ -445,9 +543,7 @@ fn on_task( msg_sender.send(response.into()).unwrap(); } } - Task::Notify(n) => { - msg_sender.send(n.into()).unwrap(); - } + Task::SendMessage(msg) => msg_sender.send(msg).unwrap(), Task::Diagnostic(task) => on_diagnostic_task(task, msg_sender, state), } } @@ -718,42 +814,6 @@ fn on_check_task( )))?; } } - - CheckTask::Status(status) => { - if global_state.config.client_caps.work_done_progress { - let progress = match status { - Status::Being => { - lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { - title: "Running `cargo check`".to_string(), - cancellable: Some(false), - message: None, - percentage: None, - }) - } - Status::Progress(target) => { - lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport { - cancellable: Some(false), - message: Some(target), - percentage: None, - }) - } - Status::End => { - lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { - message: None, - }) - } - }; - - let params = lsp_types::ProgressParams { - token: lsp_types::ProgressToken::String( - "rustAnalyzer/cargoWatcher".to_string(), - ), - value: lsp_types::ProgressParamsValue::WorkDone(progress), - }; - let not = notification_new::(params); - task_sender.send(Task::Notify(not)).unwrap(); - } - } }; Ok(()) @@ -771,15 +831,6 @@ fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender, state: } } -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; - } - } -} - struct PoolDispatcher<'a> { req: Option, pool: &'a ThreadPool, diff --git a/crates/rust-analyzer/src/main_loop/lsp_utils.rs b/crates/rust-analyzer/src/main_loop/lsp_utils.rs index fc008cba5..c79022797 100644 --- a/crates/rust-analyzer/src/main_loop/lsp_utils.rs +++ b/crates/rust-analyzer/src/main_loop/lsp_utils.rs @@ -1,10 +1,16 @@ +//! Utilities for LSP-related boilerplate code. + 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) { +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); diff --git a/crates/rust-analyzer/src/main_loop/progress.rs b/crates/rust-analyzer/src/main_loop/progress.rs deleted file mode 100644 index 610e026ca..000000000 --- a/crates/rust-analyzer/src/main_loop/progress.rs +++ /dev/null @@ -1,129 +0,0 @@ -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 e5f69835a..c68cdf862 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/workspaceAnalysis" => true, + } if token == "rustAnalyzer/rootsScanned" => true, _ => false, } } diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index a92c676fa..16a94fceb 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -5,7 +5,6 @@ import { promises as fs, PathLike } from "fs"; import * as commands from './commands'; import { activateInlayHints } from './inlay_hints'; -import { activateStatusDisplay } from './status_display'; import { Ctx } from './ctx'; import { Config, NIGHTLY_TAG } from './config'; import { log, assert, isValidExecutable } from './util'; @@ -103,8 +102,6 @@ export async function activate(context: vscode.ExtensionContext) { ctx.pushCleanup(activateTaskProvider(workspaceFolder)); - activateStatusDisplay(ctx); - activateInlayHints(ctx); vscode.workspace.onDidChangeConfiguration( diff --git a/editors/code/src/status_display.ts b/editors/code/src/status_display.ts deleted file mode 100644 index f9cadc8a2..000000000 --- a/editors/code/src/status_display.ts +++ /dev/null @@ -1,100 +0,0 @@ -import * as vscode from 'vscode'; - -import { WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressReport, WorkDoneProgressEnd, Disposable } from 'vscode-languageclient'; - -import { Ctx } from './ctx'; - -const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; - -export function activateStatusDisplay(ctx: Ctx) { - const statusDisplay = new StatusDisplay(ctx.config.checkOnSave.command); - ctx.pushCleanup(statusDisplay); - const client = ctx.client; - if (client != null) { - ctx.pushCleanup(client.onProgress( - WorkDoneProgress.type, - 'rustAnalyzer/cargoWatcher', - params => statusDisplay.handleProgressNotification(params) - )); - } -} - -class StatusDisplay implements Disposable { - packageName?: string; - - private i: number = 0; - private statusBarItem: vscode.StatusBarItem; - private command: string; - private timer?: NodeJS.Timeout; - - constructor(command: string) { - this.statusBarItem = vscode.window.createStatusBarItem( - vscode.StatusBarAlignment.Left, - 10, - ); - this.command = command; - this.statusBarItem.hide(); - } - - show() { - this.packageName = undefined; - - this.timer = - this.timer || - setInterval(() => { - this.tick(); - this.refreshLabel(); - }, 300); - - this.statusBarItem.show(); - } - - hide() { - if (this.timer) { - clearInterval(this.timer); - this.timer = undefined; - } - - this.statusBarItem.hide(); - } - - dispose() { - if (this.timer) { - clearInterval(this.timer); - this.timer = undefined; - } - - this.statusBarItem.dispose(); - } - - refreshLabel() { - if (this.packageName) { - this.statusBarItem.text = `${spinnerFrames[this.i]} cargo ${this.command} [${this.packageName}]`; - } else { - this.statusBarItem.text = `${spinnerFrames[this.i]} cargo ${this.command}`; - } - } - - handleProgressNotification(params: WorkDoneProgressBegin | WorkDoneProgressReport | WorkDoneProgressEnd) { - switch (params.kind) { - case 'begin': - this.show(); - break; - - case 'report': - if (params.message) { - this.packageName = params.message; - this.refreshLabel(); - } - break; - - case 'end': - this.hide(); - break; - } - } - - private tick() { - this.i = (this.i + 1) % spinnerFrames.length; - } -} -- cgit v1.2.3 From 6e81c9a921b975be7f2efb927dab4f3cfd505ebc Mon Sep 17 00:00:00 2001 From: Veetaha Date: Thu, 18 Jun 2020 15:02:36 +0300 Subject: =?UTF-8?q?Flatten=20Task=20enum=20=C2=AF\=5F(=E3=83=84)=5F/=C2=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/rust-analyzer/src/main_loop.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 740c52e21..7a81db3d9 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -243,7 +243,8 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { #[derive(Debug)] enum Task { Respond(Response), - SendMessage(Message), + Notify(Notification), + SendRequest(Request), Diagnostic(DiagnosticTask), } @@ -275,7 +276,7 @@ impl fmt::Debug for Event { return debug_verbose_not(not, f); } } - Event::Task(Task::SendMessage(Message::Notification(not))) => { + Event::Task(Task::Notify(not)) => { if notification_is::(not) { return debug_verbose_not(not, f); } @@ -463,7 +464,7 @@ fn on_progress_report( token: lsp_types::ProgressToken::String(token.to_string()), }, ); - task_sender.send(Task::SendMessage(create_progress_req.into())).unwrap(); + task_sender.send(Task::SendRequest(create_progress_req.into())).unwrap(); }; let (token, progress) = match report { @@ -526,7 +527,7 @@ fn on_progress_report( value: lsp_types::ProgressParamsValue::WorkDone(progress), }; let not = notification_new::(params); - task_sender.send(Task::SendMessage(not.into())).unwrap() + task_sender.send(Task::Notify(not.into())).unwrap() } fn on_task( @@ -543,7 +544,8 @@ fn on_task( msg_sender.send(response.into()).unwrap(); } } - Task::SendMessage(msg) => msg_sender.send(msg).unwrap(), + Task::Notify(not) => msg_sender.send(not.into()).unwrap(), + Task::SendRequest(req) => msg_sender.send(req.into()).unwrap(), Task::Diagnostic(task) => on_diagnostic_task(task, msg_sender, state), } } -- cgit v1.2.3 From 874a5f80c74851aa142a196be49b73f55bd1c619 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 25 Jun 2020 08:01:03 +0200 Subject: Scale progress down There are two reasons why we don't want a generic ra_progress crate just yet: *First*, it introduces a common interface between separate components, and that is usually undesirable (b/c components start to fit the interface, rather than doing what makes most sense for each particular component). *Second*, it introduces a separate async channel for progress, which makes it harder to correlate progress reports with the work done. Ie, when we see 100% progress, it's not blindly obvious that the work has actually finished, we might have some pending messages still. --- Cargo.lock | 13 -- crates/ra_flycheck/Cargo.toml | 1 - crates/ra_flycheck/src/lib.rs | 50 +++-- crates/ra_progress/Cargo.toml | 8 - crates/ra_progress/src/lib.rs | 129 ------------- crates/rust-analyzer/Cargo.toml | 1 - crates/rust-analyzer/src/global_state.rs | 21 +-- crates/rust-analyzer/src/lib.rs | 6 +- crates/rust-analyzer/src/lsp_utils.rs | 12 +- crates/rust-analyzer/src/main_loop.rs | 211 ++++++++-------------- crates/rust-analyzer/tests/heavy_tests/support.rs | 4 - 11 files changed, 102 insertions(+), 354 deletions(-) delete mode 100644 crates/ra_progress/Cargo.toml delete mode 100644 crates/ra_progress/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index bed9acf8f..c2d00adeb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -967,7 +967,6 @@ dependencies = [ "crossbeam-channel", "jod-thread", "log", - "ra_progress", "ra_toolchain", "serde_json", ] @@ -1081,11 +1080,7 @@ dependencies = [ "ra_hir", "ra_ide_db", "ra_prof", -<<<<<<< HEAD "ra_ssr", -======= - "ra_progress", ->>>>>>> Veetaha-feat/sync-branch "ra_syntax", "ra_text_edit", "rand", @@ -1173,13 +1168,6 @@ dependencies = [ "ra_arena", ] -[[package]] -name = "ra_progress" -version = "0.1.0" -dependencies = [ - "crossbeam-channel", -] - [[package]] name = "ra_project_model" version = "0.1.0" @@ -1404,7 +1392,6 @@ dependencies = [ "ra_mbe", "ra_proc_macro_srv", "ra_prof", - "ra_progress", "ra_project_model", "ra_syntax", "ra_text_edit", diff --git a/crates/ra_flycheck/Cargo.toml b/crates/ra_flycheck/Cargo.toml index 838973963..1aa39bade 100644 --- a/crates/ra_flycheck/Cargo.toml +++ b/crates/ra_flycheck/Cargo.toml @@ -14,4 +14,3 @@ cargo_metadata = "0.10.0" serde_json = "1.0.48" jod-thread = "0.1.1" ra_toolchain = { path = "../ra_toolchain" } -ra_progress = { path = "../ra_progress" } diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs index 7b9f48eb0..0e2ee8698 100644 --- a/crates/ra_flycheck/src/lib.rs +++ b/crates/ra_flycheck/src/lib.rs @@ -17,9 +17,6 @@ pub use cargo_metadata::diagnostic::{ Applicability, Diagnostic, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion, }; -type Progress = ra_progress::Progress<(), String>; -type ProgressSource = ra_progress::ProgressSource<(), String>; - #[derive(Clone, Debug, PartialEq, Eq)] pub enum FlycheckConfig { CargoCommand { @@ -59,15 +56,11 @@ pub struct Flycheck { } impl Flycheck { - pub fn new( - config: FlycheckConfig, - workspace_root: PathBuf, - progress_src: ProgressSource, - ) -> Flycheck { + pub fn new(config: FlycheckConfig, workspace_root: PathBuf) -> Flycheck { let (task_send, task_recv) = unbounded::(); let (cmd_send, cmd_recv) = unbounded::(); let handle = jod_thread::spawn(move || { - FlycheckThread::new(config, workspace_root, progress_src).run(&task_send, &cmd_recv); + FlycheckThread::new(config, workspace_root).run(&task_send, &cmd_recv); }); Flycheck { task_recv, cmd_send, handle } } @@ -85,6 +78,16 @@ pub enum CheckTask { /// Request adding a diagnostic with fixes included to a file AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic }, + + /// Request check progress notification to client + Status(Status), +} + +#[derive(Debug)] +pub enum Status { + Being, + Progress(String), + End, } pub enum CheckCommand { @@ -96,8 +99,6 @@ struct FlycheckThread { config: FlycheckConfig, workspace_root: PathBuf, last_update_req: Option, - progress_src: ProgressSource, - progress: Option, // XXX: drop order is significant message_recv: Receiver, /// WatchThread exists to wrap around the communication needed to be able to @@ -109,17 +110,11 @@ struct FlycheckThread { } impl FlycheckThread { - fn new( - config: FlycheckConfig, - workspace_root: PathBuf, - progress_src: ProgressSource, - ) -> FlycheckThread { + fn new(config: FlycheckConfig, workspace_root: PathBuf) -> FlycheckThread { FlycheckThread { config, workspace_root, - progress_src, last_update_req: None, - progress: None, message_recv: never(), check_process: None, } @@ -157,9 +152,9 @@ impl FlycheckThread { } } - fn clean_previous_results(&mut self, task_send: &Sender) { + fn clean_previous_results(&self, task_send: &Sender) { task_send.send(CheckTask::ClearDiagnostics).unwrap(); - self.progress = None; + task_send.send(CheckTask::Status(Status::End)).unwrap(); } fn should_recheck(&mut self) -> bool { @@ -178,17 +173,18 @@ impl FlycheckThread { } } - fn handle_message(&mut self, msg: CheckEvent, task_send: &Sender) { + fn handle_message(&self, msg: CheckEvent, task_send: &Sender) { match msg { CheckEvent::Begin => { - self.progress = Some(self.progress_src.begin(())); + task_send.send(CheckTask::Status(Status::Being)).unwrap(); + } + + CheckEvent::End => { + task_send.send(CheckTask::Status(Status::End)).unwrap(); } - CheckEvent::End => self.progress = None, + CheckEvent::Msg(Message::CompilerArtifact(msg)) => { - self.progress - .as_mut() - .expect("check process reported progress without the 'Begin' notification") - .report(msg.target.name); + task_send.send(CheckTask::Status(Status::Progress(msg.target.name))).unwrap(); } CheckEvent::Msg(Message::CompilerMessage(msg)) => { diff --git a/crates/ra_progress/Cargo.toml b/crates/ra_progress/Cargo.toml deleted file mode 100644 index c7f7c6dd3..000000000 --- a/crates/ra_progress/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "ra_progress" -version = "0.1.0" -authors = ["rust-analyzer developers"] -edition = "2018" - -[dependencies] -crossbeam-channel = { version = "0.4" } diff --git a/crates/ra_progress/src/lib.rs b/crates/ra_progress/src/lib.rs deleted file mode 100644 index 0ff1f846c..000000000 --- a/crates/ra_progress/src/lib.rs +++ /dev/null @@ -1,129 +0,0 @@ -//! General-purpose instrumentation for progress reporting. -//! -//! Note: -//! Most of the methods accept `&mut self` just to be more restrictive (for forward compat) -//! even tho for some of them we can weaken this requirement to shared reference (`&self`). - -use crossbeam_channel::Receiver; -use std::fmt; - -#[derive(Debug)] -pub enum ProgressStatus { - Begin(B), - Progress(P), - End, -} - -pub struct Progress(Option>>); -impl Progress { - pub fn report(&mut self, payload: P) { - self.report_with(|| payload); - } - - pub fn report_with(&mut self, payload: impl FnOnce() -> P) { - self.send_status(|| ProgressStatus::Progress(payload())); - } - - fn send_status(&self, status: impl FnOnce() -> ProgressStatus) { - if let Some(sender) = &self.0 { - sender.try_send(status()).expect("progress report must not block"); - } - } -} - -impl Drop for Progress { - fn drop(&mut self) { - self.send_status(|| ProgressStatus::End); - } -} - -pub struct ProgressSource(Option>>); -impl ProgressSource { - pub fn real_if(real: bool) -> (Receiver>, Self) { - if real { - let (sender, receiver) = crossbeam_channel::unbounded(); - (receiver, Self(Some(sender))) - } else { - (crossbeam_channel::never(), Self(None)) - } - } - - pub fn begin(&mut self, payload: B) -> Progress { - self.begin_with(|| payload) - } - - pub fn begin_with(&mut self, payload: impl FnOnce() -> B) -> Progress { - let progress = Progress(self.0.clone()); - progress.send_status(|| ProgressStatus::Begin(payload())); - progress - } -} - -impl Clone for ProgressSource { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} - -impl fmt::Debug for ProgressSource { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("ProgressSource").field(&self.0).finish() - } -} - -pub type U32ProgressStatus = ProgressStatus; - -#[derive(Debug)] -pub struct U32ProgressReport { - pub processed: u32, - pub total: u32, -} -impl U32ProgressReport { - pub fn percentage(&self) -> f64 { - f64::from(100 * self.processed) / f64::from(self.total) - } - pub fn to_message(&self, prefix: &str, unit: &str) -> String { - format!("{} ({}/{} {})", prefix, self.processed, self.total, unit) - } -} - -pub struct U32Progress { - inner: Progress, - processed: u32, - total: u32, -} - -#[derive(Debug, Eq, PartialEq)] -pub struct IsDone(pub bool); - -impl U32Progress { - pub fn report(&mut self, new_processed: u32) -> IsDone { - if self.processed < new_processed { - self.processed = new_processed; - self.inner.report(U32ProgressReport { processed: new_processed, total: self.total }); - } - IsDone(self.processed >= self.total) - } -} - -#[derive(Clone)] -pub struct U32ProgressSource { - inner: ProgressSource, -} - -impl U32ProgressSource { - pub fn real_if( - real: bool, - ) -> (Receiver>, Self) { - let (recv, inner) = ProgressSource::real_if(real); - (recv, Self { inner }) - } - - pub fn begin(&mut self, initial: u32, total: u32) -> U32Progress { - U32Progress { - inner: self.inner.begin(U32ProgressReport { processed: initial, total }), - processed: initial, - total, - } - } -} diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 2bbed395f..68d04f3e3 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -48,7 +48,6 @@ hir = { path = "../ra_hir", package = "ra_hir" } hir_def = { path = "../ra_hir_def", package = "ra_hir_def" } hir_ty = { path = "../ra_hir_ty", package = "ra_hir_ty" } ra_proc_macro_srv = { path = "../ra_proc_macro_srv" } -ra_progress = { path = "../ra_progress" } [target.'cfg(windows)'.dependencies] winapi = "0.3.8" diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 7759c0ae3..64d4e2787 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -27,11 +27,7 @@ use crate::{ }; use rustc_hash::{FxHashMap, FxHashSet}; -fn create_flycheck( - workspaces: &[ProjectWorkspace], - config: &FlycheckConfig, - progress_src: &ProgressSource<(), String>, -) -> Option { +fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) -> Option { // FIXME: Figure out the multi-workspace situation workspaces.iter().find_map(move |w| match w { ProjectWorkspace::Cargo { cargo, .. } => { @@ -147,12 +143,7 @@ impl GlobalState { } change.set_crate_graph(crate_graph); - let (flycheck_progress_receiver, flycheck_progress_src) = - ProgressSource::real_if(config.client_caps.work_done_progress); - let flycheck = config - .check - .as_ref() - .and_then(|c| create_flycheck(&workspaces, c, &flycheck_progress_src)); + let flycheck = config.check.as_ref().and_then(|c| create_flycheck(&workspaces, c)); let mut analysis_host = AnalysisHost::new(lru_capacity); analysis_host.apply_change(change); @@ -162,8 +153,6 @@ impl GlobalState { loader, task_receiver, flycheck, - flycheck_progress_src, - flycheck_progress_receiver, diagnostics: Default::default(), mem_docs: FxHashSet::default(), vfs: Arc::new(RwLock::new((vfs, FxHashMap::default()))), @@ -181,10 +170,8 @@ impl GlobalState { pub(crate) fn update_configuration(&mut self, config: Config) { self.analysis_host.update_lru_capacity(config.lru_capacity); if config.check != self.config.check { - self.flycheck = config - .check - .as_ref() - .and_then(|it| create_flycheck(&self.workspaces, it, &self.flycheck_progress_src)); + self.flycheck = + config.check.as_ref().and_then(|it| create_flycheck(&self.workspaces, it)); } self.config = config; diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index d6cd04303..794286672 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs @@ -29,16 +29,14 @@ mod markdown; mod diagnostics; mod line_endings; mod request_metrics; +mod lsp_utils; pub mod lsp_ext; pub mod config; use serde::de::DeserializeOwned; pub type Result> = std::result::Result; -pub use crate::{ - caps::server_capabilities, - main_loop::{main_loop, show_message}, -}; +pub use crate::{caps::server_capabilities, lsp_utils::show_message, main_loop::main_loop}; use std::fmt; pub fn from_json(what: &'static str, json: serde_json::Value) -> Result { diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs index c79022797..078f8778e 100644 --- a/crates/rust-analyzer/src/lsp_utils.rs +++ b/crates/rust-analyzer/src/lsp_utils.rs @@ -1,10 +1,10 @@ //! Utilities for LSP-related boilerplate code. +use std::error::Error; use crossbeam_channel::Sender; -use lsp_server::{Message, Notification, Request, RequestId}; +use lsp_server::{Message, Notification}; use ra_db::Canceled; use serde::{de::DeserializeOwned, Serialize}; -use std::error::Error; pub fn show_message( typ: lsp_types::MessageType, @@ -42,11 +42,3 @@ where { 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.rs b/crates/rust-analyzer/src/main_loop.rs index 7ccdbd29c..03569086a 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -25,17 +25,10 @@ use crate::{ from_proto, global_state::{file_id_to_url, GlobalState, GlobalStateSnapshot, Status}, handlers, lsp_ext, + lsp_utils::{is_canceled, notification_cast, notification_is, notification_new, show_message}, request_metrics::RequestMetrics, LspError, Result, }; -pub use lsp_utils::show_message; -use lsp_utils::{is_canceled, notification_cast, notification_is, notification_new, request_new}; -use ra_progress::{ - IsDone, ProgressStatus, U32Progress, U32ProgressReport, U32ProgressSource, U32ProgressStatus, -}; - -const FLYCHECK_PROGRESS_TOKEN: &str = "rustAnalyzer/flycheck"; -const ROOTS_SCANNED_PROGRESS_TOKEN: &str = "rustAnalyzer/rootsScanned"; pub fn main_loop(config: Config, connection: Connection) -> Result<()> { log::info!("initial config: {:#?}", config); @@ -147,18 +140,6 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { Ok(task) => Event::CheckWatcher(task), Err(RecvError) => return Err("check watcher died".into()), }, - recv(global_state.flycheck_progress_receiver) -> status => match status { - Ok(status) => Event::ProgressReport(ProgressReport::Flycheck(status)), - Err(RecvError) => return Err("check watcher died".into()), - }, - recv(roots_scanned_progress_receiver) -> status => match status { - Ok(status) => Event::ProgressReport(ProgressReport::RootsScanned(status)), - Err(RecvError) => { - // Roots analysis has finished, we no longer need this receiver - roots_scanned_progress_receiver = never(); - continue; - } - } }; if let Event::Msg(Message::Request(req)) = &event { if connection.handle_shutdown(&req)? { @@ -188,8 +169,6 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { #[derive(Debug)] enum Task { Respond(Response), - Notify(Notification), - SendRequest(Request), Diagnostic(DiagnosticTask), } @@ -198,13 +177,6 @@ enum Event { Task(Task), Vfs(vfs::loader::Message), CheckWatcher(CheckTask), - ProgressReport(ProgressReport), -} - -#[derive(Debug)] -enum ProgressReport { - Flycheck(ProgressStatus<(), String>), - RootsScanned(U32ProgressStatus), } impl fmt::Debug for Event { @@ -221,11 +193,6 @@ impl fmt::Debug for Event { return debug_verbose_not(not, f); } } - Event::Task(Task::Notify(not)) => { - if notification_is::(not) { - return debug_verbose_not(not, f); - } - } Event::Task(Task::Respond(resp)) => { return f .debug_struct("Response") @@ -240,7 +207,6 @@ impl fmt::Debug for Event { Event::Task(it) => fmt::Debug::fmt(it, f), Event::Vfs(it) => fmt::Debug::fmt(it, f), Event::CheckWatcher(it) => fmt::Debug::fmt(it, f), - Event::ProgressReport(it) => fmt::Debug::fmt(it, f), } } } @@ -283,16 +249,28 @@ fn loop_turn( } } vfs::loader::Message::Progress { n_total, n_done } => { - if n_done == n_total { + let state = if n_done == 0 { + ProgressState::Start + } else if n_done < n_total { + ProgressState::Report + } else { + assert_eq!(n_done, n_total); global_state.status = Status::Ready; became_ready = true; - } - report_progress(global_state, &connection.sender, n_done, n_total, "roots scanned") + ProgressState::End + }; + report_progress( + global_state, + &connection.sender, + "roots scanned", + state, + Some(format!("{}/{}", n_done, n_total)), + Some(percentage(n_done, n_total)), + ) } }, - Event::CheckWatcher(task) => on_check_task(task, global_state, task_sender)?, - Event::ProgressReport(report) => { - on_progress_report(report, task_sender, loop_state, global_state) + Event::CheckWatcher(task) => { + on_check_task(task, global_state, task_sender, &connection.sender)? } Event::Msg(msg) => match msg { Message::Request(req) => { @@ -367,9 +345,6 @@ fn on_task(task: Task, msg_sender: &Sender, global_state: &mut GlobalSt msg_sender.send(response.into()).unwrap(); } } - Task::Notify(n) => { - msg_sender.send(n.into()).unwrap(); - } Task::Diagnostic(task) => on_diagnostic_task(task, msg_sender, global_state), } } @@ -621,6 +596,7 @@ fn on_check_task( task: CheckTask, global_state: &mut GlobalState, task_sender: &Sender, + msg_sender: &Sender, ) -> Result<()> { match task { CheckTask::ClearDiagnostics => { @@ -652,39 +628,13 @@ fn on_check_task( } CheckTask::Status(status) => { - if global_state.config.client_caps.work_done_progress { - let progress = match status { - ra_flycheck::Status::Being => { - lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { - title: "Running `cargo check`".to_string(), - cancellable: Some(false), - message: None, - percentage: None, - }) - } - ra_flycheck::Status::Progress(target) => { - lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport { - cancellable: Some(false), - message: Some(target), - percentage: None, - }) - } - ra_flycheck::Status::End => { - lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { - message: None, - }) - } - }; + let (state, message) = match status { + ra_flycheck::Status::Being => (ProgressState::Start, None), + ra_flycheck::Status::Progress(target) => (ProgressState::Report, Some(target)), + ra_flycheck::Status::End => (ProgressState::End, None), + }; - let params = lsp_types::ProgressParams { - token: lsp_types::ProgressToken::String( - "rustAnalyzer/cargoWatcher".to_string(), - ), - value: lsp_types::ProgressParamsValue::WorkDone(progress), - }; - let not = notification_new::(params); - task_sender.send(Task::Notify(not)).unwrap(); - } + report_progress(global_state, msg_sender, "cargo check", state, message, None); } }; @@ -703,39 +653,55 @@ fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender, state: } } +#[derive(Eq, PartialEq)] +enum ProgressState { + Start, + Report, + End, +} + +fn percentage(done: usize, total: usize) -> f64 { + (done as f64 / total.max(1) as f64) * 100.0 +} + fn report_progress( global_state: &mut GlobalState, sender: &Sender, - done: usize, - total: usize, - message: &str, + title: &str, + state: ProgressState, + message: Option, + percentage: Option, ) { - let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", message)); - let message = Some(format!("{}/{} {}", done, total, message)); - let percentage = Some(100.0 * done as f64 / total.max(1) as f64); - let work_done_progress = if done == 0 { - let work_done_progress_create = global_state.req_queue.outgoing.register( - lsp_types::request::WorkDoneProgressCreate::METHOD.to_string(), - lsp_types::WorkDoneProgressCreateParams { token: token.clone() }, - DO_NOTHING, - ); - sender.send(work_done_progress_create.into()).unwrap(); - - lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { - title: "rust-analyzer".into(), - cancellable: None, - message, - percentage, - }) - } else if done < total { - lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport { - cancellable: None, - message, - percentage, - }) - } else { - assert!(done == total); - lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message }) + if !global_state.config.client_caps.work_done_progress { + return; + } + let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title)); + let work_done_progress = match state { + ProgressState::Start => { + let work_done_progress_create = global_state.req_queue.outgoing.register( + lsp_types::request::WorkDoneProgressCreate::METHOD.to_string(), + lsp_types::WorkDoneProgressCreateParams { token: token.clone() }, + DO_NOTHING, + ); + sender.send(work_done_progress_create.into()).unwrap(); + + lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { + title: title.into(), + cancellable: None, + message, + percentage, + }) + } + ProgressState::Report => { + lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport { + cancellable: None, + message, + percentage, + }) + } + ProgressState::End => { + lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message }) + } }; let notification = notification_new::(lsp_types::ProgressParams { @@ -898,41 +864,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) -} - #[cfg(test)] mod tests { use lsp_types::{Position, Range, TextDocumentContentChangeEvent}; diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs index 15d2a05a4..49f194f7e 100644 --- a/crates/rust-analyzer/tests/heavy_tests/support.rs +++ b/crates/rust-analyzer/tests/heavy_tests/support.rs @@ -202,11 +202,7 @@ impl Server { ProgressParams { token: lsp_types::ProgressToken::String(ref token), value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(_)), -<<<<<<< HEAD } if token == "rustAnalyzer/roots scanned" => true, -======= - } if token == "rustAnalyzer/rootsScanned" => true, ->>>>>>> Veetaha-feat/sync-branch _ => false, } } -- cgit v1.2.3