diff options
-rw-r--r-- | crates/ra_ide/src/lib.rs | 7 | ||||
-rw-r--r-- | crates/ra_ide/src/prime_caches.rs | 9 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 133 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop/lsp_utils.rs | 46 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop/progress.rs | 129 | ||||
-rw-r--r-- | crates/rust-analyzer/tests/heavy_tests/support.rs | 2 |
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 | ||
6 | use crate::{FileId, RootDatabase}; | 6 | use crate::{FileId, RootDatabase}; |
7 | 7 | ||
8 | pub(crate) fn prime_caches(db: &RootDatabase, files: Vec<FileId>) { | 8 | pub(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 @@ | |||
4 | mod handlers; | 4 | mod handlers; |
5 | mod subscriptions; | 5 | mod subscriptions; |
6 | pub(crate) mod pending_requests; | 6 | pub(crate) mod pending_requests; |
7 | mod progress; | ||
8 | mod lsp_utils; | ||
7 | 9 | ||
8 | use std::{ | 10 | use std::{ |
9 | borrow::Cow, | 11 | borrow::Cow, |
@@ -44,6 +46,9 @@ use crate::{ | |||
44 | }, | 46 | }, |
45 | Result, | 47 | Result, |
46 | }; | 48 | }; |
49 | pub use lsp_utils::show_message; | ||
50 | use lsp_utils::{is_canceled, notification_cast, notification_is, notification_new, request_new}; | ||
51 | use progress::{IsDone, PrimeCachesProgressNotifier, WorkspaceAnalysisProgressNotifier}; | ||
47 | 52 | ||
48 | #[derive(Debug)] | 53 | #[derive(Debug)] |
49 | pub struct LspError { | 54 | pub 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 | ||
747 | fn send_startup_progress(sender: &Sender<Message>, loop_state: &mut LoopState) { | 774 | fn 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 | ||
961 | pub 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 | |||
972 | fn is_canceled(e: &Box<dyn std::error::Error + Send + Sync>) -> bool { | ||
973 | e.downcast_ref::<Canceled>().is_some() | ||
974 | } | ||
975 | |||
976 | fn notification_is<N: lsp_types::notification::Notification>(notification: &Notification) -> bool { | ||
977 | notification.method == N::METHOD | ||
978 | } | ||
979 | |||
980 | fn notification_cast<N>(notification: Notification) -> std::result::Result<N::Params, Notification> | ||
981 | where | ||
982 | N: lsp_types::notification::Notification, | ||
983 | N::Params: DeserializeOwned, | ||
984 | { | ||
985 | notification.extract(N::METHOD) | ||
986 | } | ||
987 | |||
988 | fn notification_new<N>(params: N::Params) -> Notification | ||
989 | where | ||
990 | N: lsp_types::notification::Notification, | ||
991 | N::Params: Serialize, | ||
992 | { | ||
993 | Notification::new(N::METHOD.to_string(), params) | ||
994 | } | ||
995 | |||
996 | fn request_new<R>(id: RequestId, params: R::Params) -> Request | ||
997 | where | ||
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)] |
1005 | mod tests { | 946 | mod 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 @@ | |||
1 | use crossbeam_channel::Sender; | ||
2 | use lsp_server::{Message, Notification, Request, RequestId}; | ||
3 | use ra_db::Canceled; | ||
4 | use serde::{de::DeserializeOwned, Serialize}; | ||
5 | use std::error::Error; | ||
6 | |||
7 | pub 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 | |||
14 | pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool { | ||
15 | e.downcast_ref::<Canceled>().is_some() | ||
16 | } | ||
17 | |||
18 | pub(crate) fn notification_is<N: lsp_types::notification::Notification>( | ||
19 | notification: &Notification, | ||
20 | ) -> bool { | ||
21 | notification.method == N::METHOD | ||
22 | } | ||
23 | |||
24 | pub(crate) fn notification_cast<N>(notification: Notification) -> Result<N::Params, Notification> | ||
25 | where | ||
26 | N: lsp_types::notification::Notification, | ||
27 | N::Params: DeserializeOwned, | ||
28 | { | ||
29 | notification.extract(N::METHOD) | ||
30 | } | ||
31 | |||
32 | pub(crate) fn notification_new<N>(params: N::Params) -> Notification | ||
33 | where | ||
34 | N: lsp_types::notification::Notification, | ||
35 | N::Params: Serialize, | ||
36 | { | ||
37 | Notification::new(N::METHOD.to_string(), params) | ||
38 | } | ||
39 | |||
40 | pub(crate) fn request_new<R>(id: RequestId, params: R::Params) -> Request | ||
41 | where | ||
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 @@ | |||
1 | use super::lsp_utils::{notification_new, request_new}; | ||
2 | use crossbeam_channel::Sender; | ||
3 | use lsp_server::{Message, RequestId}; | ||
4 | use lsp_types::{ | ||
5 | WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd, | ||
6 | WorkDoneProgressReport, | ||
7 | }; | ||
8 | |||
9 | const PRIME_CACHES_PROGRESS_TOKEN: &str = "rustAnalyzer/primeCaches"; | ||
10 | const WORKSPACE_ANALYSIS_PROGRESS_TOKEN: &str = "rustAnalyzer/workspaceAnalysis"; | ||
11 | |||
12 | #[derive(Debug)] | ||
13 | pub(crate) struct PrimeCachesProgressNotifier(ProgressNotifier); | ||
14 | |||
15 | impl Drop for PrimeCachesProgressNotifier { | ||
16 | fn drop(&mut self) { | ||
17 | self.0.end("done priming caches".to_owned()); | ||
18 | } | ||
19 | } | ||
20 | |||
21 | impl 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)] | ||
40 | pub(crate) struct WorkspaceAnalysisProgressNotifier(ProgressNotifier); | ||
41 | |||
42 | impl Drop for WorkspaceAnalysisProgressNotifier { | ||
43 | fn drop(&mut self) { | ||
44 | self.0.end("done analyzing workspace".to_owned()); | ||
45 | } | ||
46 | } | ||
47 | |||
48 | impl 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)] | ||
67 | pub struct IsDone(pub bool); | ||
68 | |||
69 | #[derive(Debug)] | ||
70 | struct ProgressNotifier { | ||
71 | sender: Sender<Message>, | ||
72 | token: &'static str, | ||
73 | label: &'static str, | ||
74 | processed: usize, | ||
75 | total: usize, | ||
76 | } | ||
77 | |||
78 | impl 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 | } |