aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorveetaha <[email protected]>2020-05-10 04:44:02 +0100
committerVeetaha <[email protected]>2020-06-18 12:50:17 +0100
commit2f8126fcace3c5e7db01c755b91eb45a9c632cfd (patch)
tree1908ce82c4ef5229e81ecbaeff8ea4f1a0ef7bb6 /crates
parent0262dba97ef114bd7664a4e32be21caef2d63f0a (diff)
Migrate flycheck to fully-lsp-compatible progress reports (introduce ra_progress crate)
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_ide/src/lib.rs7
-rw-r--r--crates/ra_ide/src/prime_caches.rs9
-rw-r--r--crates/rust-analyzer/src/main_loop.rs133
-rw-r--r--crates/rust-analyzer/src/main_loop/lsp_utils.rs46
-rw-r--r--crates/rust-analyzer/src/main_loop/progress.rs129
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/support.rs2
6 files changed, 225 insertions, 101 deletions
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 {
241 self.with_db(|db| status::status(&*db)) 241 self.with_db(|db| status::status(&*db))
242 } 242 }
243 243
244 pub fn prime_caches(&self, files: Vec<FileId>) -> Cancelable<()> { 244 pub fn prime_caches<P>(&self, files: Vec<FileId>, report_progress: P) -> Cancelable<()>
245 self.with_db(|db| prime_caches::prime_caches(db, files)) 245 where
246 P: FnMut(usize) + std::panic::UnwindSafe,
247 {
248 self.with_db(|db| prime_caches::prime_caches(db, files, report_progress))
246 } 249 }
247 250
248 /// Gets the text of the source file. 251 /// 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 @@
5 5
6use crate::{FileId, RootDatabase}; 6use crate::{FileId, RootDatabase};
7 7
8pub(crate) fn prime_caches(db: &RootDatabase, files: Vec<FileId>) { 8pub(crate) fn prime_caches(
9 for file in files { 9 db: &RootDatabase,
10 files: Vec<FileId>,
11 mut report_progress: impl FnMut(usize),
12) {
13 for (i, file) in files.into_iter().enumerate() {
10 let _ = crate::syntax_highlighting::highlight(db, file, None, false); 14 let _ = crate::syntax_highlighting::highlight(db, file, None, false);
15 report_progress(i);
11 } 16 }
12} 17}
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 @@
4mod handlers; 4mod handlers;
5mod subscriptions; 5mod subscriptions;
6pub(crate) mod pending_requests; 6pub(crate) mod pending_requests;
7mod progress;
8mod lsp_utils;
7 9
8use std::{ 10use std::{
9 borrow::Cow, 11 borrow::Cow,
@@ -44,6 +46,9 @@ use crate::{
44 }, 46 },
45 Result, 47 Result,
46}; 48};
49pub use lsp_utils::show_message;
50use lsp_utils::{is_canceled, notification_cast, notification_is, notification_new, request_new};
51use progress::{IsDone, PrimeCachesProgressNotifier, WorkspaceAnalysisProgressNotifier};
47 52
48#[derive(Debug)] 53#[derive(Debug)]
49pub struct LspError { 54pub struct LspError {
@@ -90,6 +95,7 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
90 } 95 }
91 96
92 let mut loop_state = LoopState::default(); 97 let mut loop_state = LoopState::default();
98
93 let mut global_state = { 99 let mut global_state = {
94 let workspaces = { 100 let workspaces = {
95 if config.linked_projects.is_empty() && config.notifications.cargo_toml_not_found { 101 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<()> {
164 }; 170 };
165 171
166 loop_state.roots_total = global_state.vfs.read().n_roots(); 172 loop_state.roots_total = global_state.vfs.read().n_roots();
173 loop_state.roots_scanned = 0;
174 loop_state.roots_progress = Some(WorkspaceAnalysisProgressNotifier::begin(
175 connection.sender.clone(),
176 loop_state.next_request_id(),
177 loop_state.roots_total,
178 ));
167 179
168 let pool = ThreadPool::default(); 180 let pool = ThreadPool::default();
169 let (task_sender, task_receiver) = unbounded::<Task>(); 181 let (task_sender, task_receiver) = unbounded::<Task>();
@@ -271,7 +283,7 @@ struct LoopState {
271 pending_requests: PendingRequests, 283 pending_requests: PendingRequests,
272 subscriptions: Subscriptions, 284 subscriptions: Subscriptions,
273 workspace_loaded: bool, 285 workspace_loaded: bool,
274 roots_progress_reported: Option<usize>, 286 roots_progress: Option<WorkspaceAnalysisProgressNotifier>,
275 roots_scanned: usize, 287 roots_scanned: usize,
276 roots_total: usize, 288 roots_total: usize,
277 configuration_request_id: Option<RequestId>, 289 configuration_request_id: Option<RequestId>,
@@ -372,7 +384,7 @@ fn loop_turn(
372 } 384 }
373 385
374 if show_progress { 386 if show_progress {
375 send_startup_progress(&connection.sender, loop_state); 387 send_workspace_analisys_progress(loop_state);
376 } 388 }
377 389
378 if state_changed && loop_state.workspace_loaded { 390 if state_changed && loop_state.workspace_loaded {
@@ -385,7 +397,22 @@ fn loop_turn(
385 pool.execute({ 397 pool.execute({
386 let subs = loop_state.subscriptions.subscriptions(); 398 let subs = loop_state.subscriptions.subscriptions();
387 let snap = global_state.snapshot(); 399 let snap = global_state.snapshot();
388 move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ()) 400
401 let total = subs.len();
402
403 let mut progress = PrimeCachesProgressNotifier::begin(
404 connection.sender.clone(),
405 loop_state.next_request_id(),
406 total,
407 );
408
409 move || {
410 snap.analysis()
411 .prime_caches(subs, move |i| {
412 progress.report(i + 1);
413 })
414 .unwrap_or_else(|_: Canceled| ());
415 }
389 }); 416 });
390 } 417 }
391 418
@@ -744,55 +771,12 @@ fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state:
744 } 771 }
745} 772}
746 773
747fn send_startup_progress(sender: &Sender<Message>, loop_state: &mut LoopState) { 774fn send_workspace_analisys_progress(loop_state: &mut LoopState) {
748 let total: usize = loop_state.roots_total; 775 if let Some(progress) = &mut loop_state.roots_progress {
749 let prev = loop_state.roots_progress_reported; 776 if loop_state.workspace_loaded || progress.report(loop_state.roots_scanned) == IsDone(true)
750 let progress = loop_state.roots_scanned; 777 {
751 loop_state.roots_progress_reported = Some(progress); 778 loop_state.roots_progress = None;
752
753 match (prev, loop_state.workspace_loaded) {
754 (None, false) => {
755 let work_done_progress_create = request_new::<lsp_types::request::WorkDoneProgressCreate>(
756 loop_state.next_request_id(),
757 WorkDoneProgressCreateParams {
758 token: lsp_types::ProgressToken::String("rustAnalyzer/startup".into()),
759 },
760 );
761 sender.send(work_done_progress_create.into()).unwrap();
762 send_startup_progress_notif(
763 sender,
764 WorkDoneProgress::Begin(WorkDoneProgressBegin {
765 title: "rust-analyzer".into(),
766 cancellable: None,
767 message: Some(format!("{}/{} packages", progress, total)),
768 percentage: Some(100.0 * progress as f64 / total as f64),
769 }),
770 );
771 } 779 }
772 (Some(prev), false) if progress != prev => send_startup_progress_notif(
773 sender,
774 WorkDoneProgress::Report(WorkDoneProgressReport {
775 cancellable: None,
776 message: Some(format!("{}/{} packages", progress, total)),
777 percentage: Some(100.0 * progress as f64 / total as f64),
778 }),
779 ),
780 (_, true) => send_startup_progress_notif(
781 sender,
782 WorkDoneProgress::End(WorkDoneProgressEnd {
783 message: Some(format!("rust-analyzer loaded, {} packages", progress)),
784 }),
785 ),
786 _ => {}
787 }
788
789 fn send_startup_progress_notif(sender: &Sender<Message>, work_done_progress: WorkDoneProgress) {
790 let notif =
791 notification_new::<lsp_types::notification::Progress>(lsp_types::ProgressParams {
792 token: lsp_types::ProgressToken::String("rustAnalyzer/startup".into()),
793 value: lsp_types::ProgressParamsValue::WorkDone(work_done_progress),
794 });
795 sender.send(notif.into()).unwrap();
796 } 780 }
797} 781}
798 782
@@ -918,7 +902,7 @@ where
918 } 902 }
919 } 903 }
920 Err(e) => { 904 Err(e) => {
921 if is_canceled(&e) { 905 if is_canceled(&*e) {
922 Response::new_err( 906 Response::new_err(
923 id, 907 id,
924 ErrorCode::ContentModified as i32, 908 ErrorCode::ContentModified as i32,
@@ -945,7 +929,7 @@ fn update_file_notifications_on_threadpool(
945 for file_id in subscriptions { 929 for file_id in subscriptions {
946 match handlers::publish_diagnostics(&world, file_id) { 930 match handlers::publish_diagnostics(&world, file_id) {
947 Err(e) => { 931 Err(e) => {
948 if !is_canceled(&e) { 932 if !is_canceled(&*e) {
949 log::error!("failed to compute diagnostics: {:?}", e); 933 log::error!("failed to compute diagnostics: {:?}", e);
950 } 934 }
951 } 935 }
@@ -958,49 +942,6 @@ fn update_file_notifications_on_threadpool(
958 } 942 }
959} 943}
960 944
961pub fn show_message(
962 typ: lsp_types::MessageType,
963 message: impl Into<String>,
964 sender: &Sender<Message>,
965) {
966 let message = message.into();
967 let params = lsp_types::ShowMessageParams { typ, message };
968 let not = notification_new::<lsp_types::notification::ShowMessage>(params);
969 sender.send(not.into()).unwrap();
970}
971
972fn is_canceled(e: &Box<dyn std::error::Error + Send + Sync>) -> bool {
973 e.downcast_ref::<Canceled>().is_some()
974}
975
976fn notification_is<N: lsp_types::notification::Notification>(notification: &Notification) -> bool {
977 notification.method == N::METHOD
978}
979
980fn notification_cast<N>(notification: Notification) -> std::result::Result<N::Params, Notification>
981where
982 N: lsp_types::notification::Notification,
983 N::Params: DeserializeOwned,
984{
985 notification.extract(N::METHOD)
986}
987
988fn notification_new<N>(params: N::Params) -> Notification
989where
990 N: lsp_types::notification::Notification,
991 N::Params: Serialize,
992{
993 Notification::new(N::METHOD.to_string(), params)
994}
995
996fn request_new<R>(id: RequestId, params: R::Params) -> Request
997where
998 R: lsp_types::request::Request,
999 R::Params: Serialize,
1000{
1001 Request::new(id, R::METHOD.to_string(), params)
1002}
1003
1004#[cfg(test)] 945#[cfg(test)]
1005mod tests { 946mod tests {
1006 use std::borrow::Cow; 947 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 @@
1use crossbeam_channel::Sender;
2use lsp_server::{Message, Notification, Request, RequestId};
3use ra_db::Canceled;
4use serde::{de::DeserializeOwned, Serialize};
5use std::error::Error;
6
7pub fn show_message(typ: lsp_types::MessageType, message: impl Into<String>, sender: &Sender<Message>) {
8 let message = message.into();
9 let params = lsp_types::ShowMessageParams { typ, message };
10 let not = notification_new::<lsp_types::notification::ShowMessage>(params);
11 sender.send(not.into()).unwrap();
12}
13
14pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool {
15 e.downcast_ref::<Canceled>().is_some()
16}
17
18pub(crate) fn notification_is<N: lsp_types::notification::Notification>(
19 notification: &Notification,
20) -> bool {
21 notification.method == N::METHOD
22}
23
24pub(crate) fn notification_cast<N>(notification: Notification) -> Result<N::Params, Notification>
25where
26 N: lsp_types::notification::Notification,
27 N::Params: DeserializeOwned,
28{
29 notification.extract(N::METHOD)
30}
31
32pub(crate) fn notification_new<N>(params: N::Params) -> Notification
33where
34 N: lsp_types::notification::Notification,
35 N::Params: Serialize,
36{
37 Notification::new(N::METHOD.to_string(), params)
38}
39
40pub(crate) fn request_new<R>(id: RequestId, params: R::Params) -> Request
41where
42 R: lsp_types::request::Request,
43 R::Params: Serialize,
44{
45 Request::new(id, R::METHOD.to_string(), params)
46}
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 @@
1use super::lsp_utils::{notification_new, request_new};
2use crossbeam_channel::Sender;
3use lsp_server::{Message, RequestId};
4use lsp_types::{
5 WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd,
6 WorkDoneProgressReport,
7};
8
9const PRIME_CACHES_PROGRESS_TOKEN: &str = "rustAnalyzer/primeCaches";
10const WORKSPACE_ANALYSIS_PROGRESS_TOKEN: &str = "rustAnalyzer/workspaceAnalysis";
11
12#[derive(Debug)]
13pub(crate) struct PrimeCachesProgressNotifier(ProgressNotifier);
14
15impl Drop for PrimeCachesProgressNotifier {
16 fn drop(&mut self) {
17 self.0.end("done priming caches".to_owned());
18 }
19}
20
21impl PrimeCachesProgressNotifier {
22 pub(crate) fn begin(sender: Sender<Message>, req_id: RequestId, total: usize) -> Self {
23 let me = Self(ProgressNotifier {
24 sender,
25 processed: 0,
26 total,
27 token: PRIME_CACHES_PROGRESS_TOKEN,
28 label: "priming caches",
29 });
30 me.0.begin(req_id);
31 me
32 }
33
34 pub(crate) fn report(&mut self, processed: usize) -> IsDone {
35 self.0.report(processed)
36 }
37}
38
39#[derive(Debug)]
40pub(crate) struct WorkspaceAnalysisProgressNotifier(ProgressNotifier);
41
42impl Drop for WorkspaceAnalysisProgressNotifier {
43 fn drop(&mut self) {
44 self.0.end("done analyzing workspace".to_owned());
45 }
46}
47
48impl WorkspaceAnalysisProgressNotifier {
49 pub(crate) fn begin(sender: Sender<Message>, req_id: RequestId, total: usize) -> Self {
50 let me = Self(ProgressNotifier {
51 sender,
52 total,
53 processed: 0,
54 token: WORKSPACE_ANALYSIS_PROGRESS_TOKEN,
55 label: "analyzing packages",
56 });
57 me.0.begin(req_id);
58 me
59 }
60
61 pub(crate) fn report(&mut self, processed: usize) -> IsDone {
62 self.0.report(processed)
63 }
64}
65
66#[derive(Debug, PartialEq, Eq)]
67pub struct IsDone(pub bool);
68
69#[derive(Debug)]
70struct ProgressNotifier {
71 sender: Sender<Message>,
72 token: &'static str,
73 label: &'static str,
74 processed: usize,
75 total: usize,
76}
77
78impl ProgressNotifier {
79 fn begin(&self, req_id: RequestId) {
80 let create_req = request_new::<lsp_types::request::WorkDoneProgressCreate>(
81 req_id,
82 WorkDoneProgressCreateParams {
83 token: lsp_types::ProgressToken::String(self.token.to_owned()),
84 },
85 );
86 self.sender.send(create_req.into()).unwrap();
87 self.send_notification(WorkDoneProgress::Begin(WorkDoneProgressBegin {
88 cancellable: None,
89 title: "rust-analyzer".to_owned(),
90 percentage: Some(self.percentage()),
91 message: Some(self.create_progress_message()),
92 }));
93 }
94
95 fn report(&mut self, processed: usize) -> IsDone {
96 if self.processed != processed {
97 self.processed = processed;
98
99 self.send_notification(WorkDoneProgress::Report(WorkDoneProgressReport {
100 cancellable: None,
101 percentage: Some(self.percentage()),
102 message: Some(self.create_progress_message()),
103 }));
104 }
105 IsDone(processed >= self.total)
106 }
107
108 fn end(&mut self, message: String) {
109 self.send_notification(WorkDoneProgress::End(WorkDoneProgressEnd {
110 message: Some(message),
111 }));
112 }
113
114 fn send_notification(&self, progress: WorkDoneProgress) {
115 let notif = notification_new::<lsp_types::notification::Progress>(lsp_types::ProgressParams {
116 token: lsp_types::ProgressToken::String(self.token.to_owned()),
117 value: lsp_types::ProgressParamsValue::WorkDone(progress),
118 });
119 self.sender.send(notif.into()).unwrap();
120 }
121
122 fn create_progress_message(&self) -> String {
123 format!("{} ({}/{})", self.label, self.processed, self.total)
124 }
125
126 fn percentage(&self) -> f64 {
127 (100 * self.processed) as f64 / self.total as f64
128 }
129}
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 {
212 ProgressParams { 212 ProgressParams {
213 token: lsp_types::ProgressToken::String(ref token), 213 token: lsp_types::ProgressToken::String(ref token),
214 value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(_)), 214 value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(_)),
215 } if token == "rustAnalyzer/startup" => true, 215 } if token == "rustAnalyzer/workspaceAnalysis" => true,
216 _ => false, 216 _ => false,
217 } 217 }
218 } 218 }