diff options
author | veetaha <[email protected]> | 2020-05-10 16:35:33 +0100 |
---|---|---|
committer | Veetaha <[email protected]> | 2020-06-18 12:50:56 +0100 |
commit | 76c1160ffa626fc5f07b309420e6666eb79a3311 (patch) | |
tree | faa1e1bab885988042fb735f89c7bc5c59127a12 /crates | |
parent | 2f8126fcace3c5e7db01c755b91eb45a9c632cfd (diff) |
Migrate flycheck to fully-lsp-compatible progress reports (introduce ra_progress crate)
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_flycheck/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_flycheck/src/lib.rs | 62 | ||||
-rw-r--r-- | crates/ra_ide/Cargo.toml | 1 | ||||
-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/ra_progress/Cargo.toml | 8 | ||||
-rw-r--r-- | crates/ra_progress/src/lib.rs | 129 | ||||
-rw-r--r-- | crates/rust-analyzer/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/rust-analyzer/src/global_state.rs | 30 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 223 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop/lsp_utils.rs | 8 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop/progress.rs | 129 | ||||
-rw-r--r-- | crates/rust-analyzer/tests/heavy_tests/support.rs | 2 |
13 files changed, 351 insertions, 259 deletions
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" | |||
14 | serde_json = "1.0.48" | 14 | serde_json = "1.0.48" |
15 | jod-thread = "0.1.1" | 15 | jod-thread = "0.1.1" |
16 | ra_toolchain = { path = "../ra_toolchain" } | 16 | ra_toolchain = { path = "../ra_toolchain" } |
17 | 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 @@ | |||
3 | //! LSP diagnostics based on the output of the command. | 3 | //! LSP diagnostics based on the output of the command. |
4 | 4 | ||
5 | use std::{ | 5 | use std::{ |
6 | fmt, | ||
6 | io::{self, BufReader}, | 7 | io::{self, BufReader}, |
7 | path::PathBuf, | 8 | path::PathBuf, |
8 | process::{Command, Stdio}, | 9 | process::{Command, Stdio}, |
@@ -16,6 +17,9 @@ pub use cargo_metadata::diagnostic::{ | |||
16 | Applicability, Diagnostic, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion, | 17 | Applicability, Diagnostic, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion, |
17 | }; | 18 | }; |
18 | 19 | ||
20 | type Progress = ra_progress::Progress<(), String>; | ||
21 | type ProgressSource = ra_progress::ProgressSource<(), String>; | ||
22 | |||
19 | #[derive(Clone, Debug, PartialEq, Eq)] | 23 | #[derive(Clone, Debug, PartialEq, Eq)] |
20 | pub enum FlycheckConfig { | 24 | pub enum FlycheckConfig { |
21 | CargoCommand { | 25 | CargoCommand { |
@@ -31,6 +35,17 @@ pub enum FlycheckConfig { | |||
31 | }, | 35 | }, |
32 | } | 36 | } |
33 | 37 | ||
38 | impl fmt::Display for FlycheckConfig { | ||
39 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
40 | match self { | ||
41 | FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {}", command), | ||
42 | FlycheckConfig::CustomCommand { command, args } => { | ||
43 | write!(f, "{} {}", command, args.join(" ")) | ||
44 | } | ||
45 | } | ||
46 | } | ||
47 | } | ||
48 | |||
34 | /// Flycheck wraps the shared state and communication machinery used for | 49 | /// Flycheck wraps the shared state and communication machinery used for |
35 | /// running `cargo check` (or other compatible command) and providing | 50 | /// running `cargo check` (or other compatible command) and providing |
36 | /// diagnostics based on the output. | 51 | /// diagnostics based on the output. |
@@ -44,11 +59,15 @@ pub struct Flycheck { | |||
44 | } | 59 | } |
45 | 60 | ||
46 | impl Flycheck { | 61 | impl Flycheck { |
47 | pub fn new(config: FlycheckConfig, workspace_root: PathBuf) -> Flycheck { | 62 | pub fn new( |
63 | config: FlycheckConfig, | ||
64 | workspace_root: PathBuf, | ||
65 | progress_src: ProgressSource, | ||
66 | ) -> Flycheck { | ||
48 | let (task_send, task_recv) = unbounded::<CheckTask>(); | 67 | let (task_send, task_recv) = unbounded::<CheckTask>(); |
49 | let (cmd_send, cmd_recv) = unbounded::<CheckCommand>(); | 68 | let (cmd_send, cmd_recv) = unbounded::<CheckCommand>(); |
50 | let handle = jod_thread::spawn(move || { | 69 | let handle = jod_thread::spawn(move || { |
51 | FlycheckThread::new(config, workspace_root).run(&task_send, &cmd_recv); | 70 | FlycheckThread::new(config, workspace_root, progress_src).run(&task_send, &cmd_recv); |
52 | }); | 71 | }); |
53 | Flycheck { task_recv, cmd_send, handle } | 72 | Flycheck { task_recv, cmd_send, handle } |
54 | } | 73 | } |
@@ -66,16 +85,6 @@ pub enum CheckTask { | |||
66 | 85 | ||
67 | /// Request adding a diagnostic with fixes included to a file | 86 | /// Request adding a diagnostic with fixes included to a file |
68 | AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic }, | 87 | AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic }, |
69 | |||
70 | /// Request check progress notification to client | ||
71 | Status(Status), | ||
72 | } | ||
73 | |||
74 | #[derive(Debug)] | ||
75 | pub enum Status { | ||
76 | Being, | ||
77 | Progress(String), | ||
78 | End, | ||
79 | } | 88 | } |
80 | 89 | ||
81 | pub enum CheckCommand { | 90 | pub enum CheckCommand { |
@@ -87,6 +96,8 @@ struct FlycheckThread { | |||
87 | config: FlycheckConfig, | 96 | config: FlycheckConfig, |
88 | workspace_root: PathBuf, | 97 | workspace_root: PathBuf, |
89 | last_update_req: Option<Instant>, | 98 | last_update_req: Option<Instant>, |
99 | progress_src: ProgressSource, | ||
100 | progress: Option<Progress>, | ||
90 | // XXX: drop order is significant | 101 | // XXX: drop order is significant |
91 | message_recv: Receiver<CheckEvent>, | 102 | message_recv: Receiver<CheckEvent>, |
92 | /// WatchThread exists to wrap around the communication needed to be able to | 103 | /// WatchThread exists to wrap around the communication needed to be able to |
@@ -98,11 +109,17 @@ struct FlycheckThread { | |||
98 | } | 109 | } |
99 | 110 | ||
100 | impl FlycheckThread { | 111 | impl FlycheckThread { |
101 | fn new(config: FlycheckConfig, workspace_root: PathBuf) -> FlycheckThread { | 112 | fn new( |
113 | config: FlycheckConfig, | ||
114 | workspace_root: PathBuf, | ||
115 | progress_src: ProgressSource, | ||
116 | ) -> FlycheckThread { | ||
102 | FlycheckThread { | 117 | FlycheckThread { |
103 | config, | 118 | config, |
104 | workspace_root, | 119 | workspace_root, |
120 | progress_src, | ||
105 | last_update_req: None, | 121 | last_update_req: None, |
122 | progress: None, | ||
106 | message_recv: never(), | 123 | message_recv: never(), |
107 | check_process: None, | 124 | check_process: None, |
108 | } | 125 | } |
@@ -140,9 +157,9 @@ impl FlycheckThread { | |||
140 | } | 157 | } |
141 | } | 158 | } |
142 | 159 | ||
143 | fn clean_previous_results(&self, task_send: &Sender<CheckTask>) { | 160 | fn clean_previous_results(&mut self, task_send: &Sender<CheckTask>) { |
144 | task_send.send(CheckTask::ClearDiagnostics).unwrap(); | 161 | task_send.send(CheckTask::ClearDiagnostics).unwrap(); |
145 | task_send.send(CheckTask::Status(Status::End)).unwrap(); | 162 | self.progress = None; |
146 | } | 163 | } |
147 | 164 | ||
148 | fn should_recheck(&mut self) -> bool { | 165 | fn should_recheck(&mut self) -> bool { |
@@ -161,18 +178,17 @@ impl FlycheckThread { | |||
161 | } | 178 | } |
162 | } | 179 | } |
163 | 180 | ||
164 | fn handle_message(&self, msg: CheckEvent, task_send: &Sender<CheckTask>) { | 181 | fn handle_message(&mut self, msg: CheckEvent, task_send: &Sender<CheckTask>) { |
165 | match msg { | 182 | match msg { |
166 | CheckEvent::Begin => { | 183 | CheckEvent::Begin => { |
167 | task_send.send(CheckTask::Status(Status::Being)).unwrap(); | 184 | self.progress = Some(self.progress_src.begin(())); |
168 | } | 185 | } |
169 | 186 | CheckEvent::End => self.progress = None, | |
170 | CheckEvent::End => { | ||
171 | task_send.send(CheckTask::Status(Status::End)).unwrap(); | ||
172 | } | ||
173 | |||
174 | CheckEvent::Msg(Message::CompilerArtifact(msg)) => { | 187 | CheckEvent::Msg(Message::CompilerArtifact(msg)) => { |
175 | task_send.send(CheckTask::Status(Status::Progress(msg.target.name))).unwrap(); | 188 | self.progress |
189 | .as_mut() | ||
190 | .expect("check process reported progress without the 'Begin' notification") | ||
191 | .report(msg.target.name); | ||
176 | } | 192 | } |
177 | 193 | ||
178 | CheckEvent::Msg(Message::CompilerMessage(msg)) => { | 194 | 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" } | |||
29 | ra_prof = { path = "../ra_prof" } | 29 | ra_prof = { path = "../ra_prof" } |
30 | test_utils = { path = "../test_utils" } | 30 | test_utils = { path = "../test_utils" } |
31 | ra_assists = { path = "../ra_assists" } | 31 | ra_assists = { path = "../ra_assists" } |
32 | ra_progress = { path = "../ra_progress" } | ||
32 | 33 | ||
33 | # ra_ide should depend only on the top-level `hir` package. if you need | 34 | # ra_ide should depend only on the top-level `hir` package. if you need |
34 | # something from some `hir_xxx` subpackage, reexport the API via `hir`. | 35 | # 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 { | |||
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<P>(&self, files: Vec<FileId>, report_progress: P) -> Cancelable<()> | 244 | pub fn prime_caches(&self, files: Vec<FileId>) -> Cancelable<()> { |
245 | where | 245 | self.with_db(|db| prime_caches::prime_caches(db, files)) |
246 | P: FnMut(usize) + std::panic::UnwindSafe, | ||
247 | { | ||
248 | self.with_db(|db| prime_caches::prime_caches(db, files, report_progress)) | ||
249 | } | 246 | } |
250 | 247 | ||
251 | /// Gets the text of the source file. | 248 | /// 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 @@ | |||
5 | 5 | ||
6 | use crate::{FileId, RootDatabase}; | 6 | use crate::{FileId, RootDatabase}; |
7 | 7 | ||
8 | pub(crate) fn prime_caches( | 8 | pub(crate) fn prime_caches(db: &RootDatabase, files: Vec<FileId>) { |
9 | db: &RootDatabase, | 9 | for file in files { |
10 | files: Vec<FileId>, | ||
11 | mut report_progress: impl FnMut(usize), | ||
12 | ) { | ||
13 | for (i, file) in files.into_iter().enumerate() { | ||
14 | let _ = crate::syntax_highlighting::highlight(db, file, None, false); | 10 | let _ = crate::syntax_highlighting::highlight(db, file, None, false); |
15 | report_progress(i); | ||
16 | } | 11 | } |
17 | } | 12 | } |
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 @@ | |||
1 | [package] | ||
2 | name = "ra_progress" | ||
3 | version = "0.1.0" | ||
4 | authors = ["rust-analyzer developers"] | ||
5 | edition = "2018" | ||
6 | |||
7 | [dependencies] | ||
8 | 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 @@ | |||
1 | //! General-purpose instrumentation for progress reporting. | ||
2 | //! | ||
3 | //! Note: | ||
4 | //! Most of the methods accept `&mut self` just to be more restrictive (for forward compat) | ||
5 | //! even tho for some of them we can weaken this requirement to shared reference (`&self`). | ||
6 | |||
7 | use crossbeam_channel::Receiver; | ||
8 | use std::fmt; | ||
9 | |||
10 | #[derive(Debug)] | ||
11 | pub enum ProgressStatus<B, P> { | ||
12 | Begin(B), | ||
13 | Progress(P), | ||
14 | End, | ||
15 | } | ||
16 | |||
17 | pub struct Progress<B, P>(Option<crossbeam_channel::Sender<ProgressStatus<B, P>>>); | ||
18 | impl<B, P> Progress<B, P> { | ||
19 | pub fn report(&mut self, payload: P) { | ||
20 | self.report_with(|| payload); | ||
21 | } | ||
22 | |||
23 | pub fn report_with(&mut self, payload: impl FnOnce() -> P) { | ||
24 | self.send_status(|| ProgressStatus::Progress(payload())); | ||
25 | } | ||
26 | |||
27 | fn send_status(&self, status: impl FnOnce() -> ProgressStatus<B, P>) { | ||
28 | if let Some(sender) = &self.0 { | ||
29 | sender.try_send(status()).expect("progress report must not block"); | ||
30 | } | ||
31 | } | ||
32 | } | ||
33 | |||
34 | impl<B, P> Drop for Progress<B, P> { | ||
35 | fn drop(&mut self) { | ||
36 | self.send_status(|| ProgressStatus::End); | ||
37 | } | ||
38 | } | ||
39 | |||
40 | pub struct ProgressSource<B, P>(Option<crossbeam_channel::Sender<ProgressStatus<B, P>>>); | ||
41 | impl<B, P> ProgressSource<B, P> { | ||
42 | pub fn real_if(real: bool) -> (Receiver<ProgressStatus<B, P>>, Self) { | ||
43 | if real { | ||
44 | let (sender, receiver) = crossbeam_channel::unbounded(); | ||
45 | (receiver, Self(Some(sender))) | ||
46 | } else { | ||
47 | (crossbeam_channel::never(), Self(None)) | ||
48 | } | ||
49 | } | ||
50 | |||
51 | pub fn begin(&mut self, payload: B) -> Progress<B, P> { | ||
52 | self.begin_with(|| payload) | ||
53 | } | ||
54 | |||
55 | pub fn begin_with(&mut self, payload: impl FnOnce() -> B) -> Progress<B, P> { | ||
56 | let progress = Progress(self.0.clone()); | ||
57 | progress.send_status(|| ProgressStatus::Begin(payload())); | ||
58 | progress | ||
59 | } | ||
60 | } | ||
61 | |||
62 | impl<B, P> Clone for ProgressSource<B, P> { | ||
63 | fn clone(&self) -> Self { | ||
64 | Self(self.0.clone()) | ||
65 | } | ||
66 | } | ||
67 | |||
68 | impl<B, P> fmt::Debug for ProgressSource<B, P> { | ||
69 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
70 | f.debug_tuple("ProgressSource").field(&self.0).finish() | ||
71 | } | ||
72 | } | ||
73 | |||
74 | pub type U32ProgressStatus = ProgressStatus<U32ProgressReport, U32ProgressReport>; | ||
75 | |||
76 | #[derive(Debug)] | ||
77 | pub struct U32ProgressReport { | ||
78 | pub processed: u32, | ||
79 | pub total: u32, | ||
80 | } | ||
81 | impl U32ProgressReport { | ||
82 | pub fn percentage(&self) -> f64 { | ||
83 | f64::from(100 * self.processed) / f64::from(self.total) | ||
84 | } | ||
85 | pub fn to_message(&self, prefix: &str, unit: &str) -> String { | ||
86 | format!("{} ({}/{} {})", prefix, self.processed, self.total, unit) | ||
87 | } | ||
88 | } | ||
89 | |||
90 | pub struct U32Progress { | ||
91 | inner: Progress<U32ProgressReport, U32ProgressReport>, | ||
92 | processed: u32, | ||
93 | total: u32, | ||
94 | } | ||
95 | |||
96 | #[derive(Debug, Eq, PartialEq)] | ||
97 | pub struct IsDone(pub bool); | ||
98 | |||
99 | impl U32Progress { | ||
100 | pub fn report(&mut self, new_processed: u32) -> IsDone { | ||
101 | if self.processed < new_processed { | ||
102 | self.processed = new_processed; | ||
103 | self.inner.report(U32ProgressReport { processed: new_processed, total: self.total }); | ||
104 | } | ||
105 | IsDone(self.processed >= self.total) | ||
106 | } | ||
107 | } | ||
108 | |||
109 | #[derive(Clone)] | ||
110 | pub struct U32ProgressSource { | ||
111 | inner: ProgressSource<U32ProgressReport, U32ProgressReport>, | ||
112 | } | ||
113 | |||
114 | impl U32ProgressSource { | ||
115 | pub fn real_if( | ||
116 | real: bool, | ||
117 | ) -> (Receiver<ProgressStatus<U32ProgressReport, U32ProgressReport>>, Self) { | ||
118 | let (recv, inner) = ProgressSource::real_if(real); | ||
119 | (recv, Self { inner }) | ||
120 | } | ||
121 | |||
122 | pub fn begin(&mut self, initial: u32, total: u32) -> U32Progress { | ||
123 | U32Progress { | ||
124 | inner: self.inner.begin(U32ProgressReport { processed: initial, total }), | ||
125 | processed: initial, | ||
126 | total, | ||
127 | } | ||
128 | } | ||
129 | } | ||
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" } | |||
48 | hir_def = { path = "../ra_hir_def", package = "ra_hir_def" } | 48 | hir_def = { path = "../ra_hir_def", package = "ra_hir_def" } |
49 | hir_ty = { path = "../ra_hir_ty", package = "ra_hir_ty" } | 49 | hir_ty = { path = "../ra_hir_ty", package = "ra_hir_ty" } |
50 | ra_proc_macro_srv = { path = "../ra_proc_macro_srv" } | 50 | ra_proc_macro_srv = { path = "../ra_proc_macro_srv" } |
51 | ra_progress = { path = "../ra_progress" } | ||
51 | 52 | ||
52 | [target.'cfg(windows)'.dependencies] | 53 | [target.'cfg(windows)'.dependencies] |
53 | winapi = "0.3.8" | 54 | 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::{ | |||
26 | LspError, Result, | 26 | LspError, Result, |
27 | }; | 27 | }; |
28 | use ra_db::{CrateId, ExternSourceId}; | 28 | use ra_db::{CrateId, ExternSourceId}; |
29 | use ra_progress::{ProgressSource, ProgressStatus}; | ||
29 | use rustc_hash::{FxHashMap, FxHashSet}; | 30 | use rustc_hash::{FxHashMap, FxHashSet}; |
30 | 31 | ||
31 | fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) -> Option<Flycheck> { | 32 | fn create_flycheck( |
33 | workspaces: &[ProjectWorkspace], | ||
34 | config: &FlycheckConfig, | ||
35 | progress_src: &ProgressSource<(), String>, | ||
36 | ) -> Option<Flycheck> { | ||
32 | // FIXME: Figure out the multi-workspace situation | 37 | // FIXME: Figure out the multi-workspace situation |
33 | workspaces.iter().find_map(|w| match w { | 38 | workspaces.iter().find_map(move |w| match w { |
34 | ProjectWorkspace::Cargo { cargo, .. } => { | 39 | ProjectWorkspace::Cargo { cargo, .. } => { |
35 | let cargo_project_root = cargo.workspace_root().to_path_buf(); | 40 | let cargo_project_root = cargo.workspace_root().to_path_buf(); |
36 | Some(Flycheck::new(config.clone(), cargo_project_root)) | 41 | Some(Flycheck::new(config.clone(), cargo_project_root, progress_src.clone())) |
37 | } | 42 | } |
38 | ProjectWorkspace::Json { .. } => { | 43 | ProjectWorkspace::Json { .. } => { |
39 | log::warn!("Cargo check watching only supported for cargo workspaces, disabling"); | 44 | log::warn!("Cargo check watching only supported for cargo workspaces, disabling"); |
@@ -59,6 +64,8 @@ pub struct GlobalState { | |||
59 | pub flycheck: Option<Flycheck>, | 64 | pub flycheck: Option<Flycheck>, |
60 | pub diagnostics: DiagnosticCollection, | 65 | pub diagnostics: DiagnosticCollection, |
61 | pub proc_macro_client: ProcMacroClient, | 66 | pub proc_macro_client: ProcMacroClient, |
67 | pub flycheck_progress_src: ProgressSource<(), String>, | ||
68 | pub flycheck_progress_receiver: Receiver<ProgressStatus<(), String>>, | ||
62 | } | 69 | } |
63 | 70 | ||
64 | /// An immutable snapshot of the world's state at a point in time. | 71 | /// An immutable snapshot of the world's state at a point in time. |
@@ -158,7 +165,12 @@ impl GlobalState { | |||
158 | } | 165 | } |
159 | change.set_crate_graph(crate_graph); | 166 | change.set_crate_graph(crate_graph); |
160 | 167 | ||
161 | let flycheck = config.check.as_ref().and_then(|c| create_flycheck(&workspaces, c)); | 168 | let (flycheck_progress_receiver, flycheck_progress_src) = |
169 | ProgressSource::real_if(config.client_caps.work_done_progress); | ||
170 | let flycheck = config | ||
171 | .check | ||
172 | .as_ref() | ||
173 | .and_then(|c| create_flycheck(&workspaces, c, &flycheck_progress_src)); | ||
162 | 174 | ||
163 | let mut analysis_host = AnalysisHost::new(lru_capacity); | 175 | let mut analysis_host = AnalysisHost::new(lru_capacity); |
164 | analysis_host.apply_change(change); | 176 | analysis_host.apply_change(change); |
@@ -171,6 +183,8 @@ impl GlobalState { | |||
171 | task_receiver, | 183 | task_receiver, |
172 | latest_requests: Default::default(), | 184 | latest_requests: Default::default(), |
173 | flycheck, | 185 | flycheck, |
186 | flycheck_progress_src, | ||
187 | flycheck_progress_receiver, | ||
174 | diagnostics: Default::default(), | 188 | diagnostics: Default::default(), |
175 | proc_macro_client, | 189 | proc_macro_client, |
176 | } | 190 | } |
@@ -179,8 +193,10 @@ impl GlobalState { | |||
179 | pub fn update_configuration(&mut self, config: Config) { | 193 | pub fn update_configuration(&mut self, config: Config) { |
180 | self.analysis_host.update_lru_capacity(config.lru_capacity); | 194 | self.analysis_host.update_lru_capacity(config.lru_capacity); |
181 | if config.check != self.config.check { | 195 | if config.check != self.config.check { |
182 | self.flycheck = | 196 | self.flycheck = config |
183 | config.check.as_ref().and_then(|it| create_flycheck(&self.workspaces, it)); | 197 | .check |
198 | .as_ref() | ||
199 | .and_then(|it| create_flycheck(&self.workspaces, it, &self.flycheck_progress_src)); | ||
184 | } | 200 | } |
185 | 201 | ||
186 | self.config = config; | 202 | self.config = config; |
@@ -188,7 +204,7 @@ impl GlobalState { | |||
188 | 204 | ||
189 | /// Returns a vec of libraries | 205 | /// Returns a vec of libraries |
190 | /// FIXME: better API here | 206 | /// FIXME: better API here |
191 | pub fn process_changes(&mut self, roots_scanned: &mut usize) -> bool { | 207 | pub fn process_changes(&mut self, roots_scanned: &mut u32) -> bool { |
192 | let changes = self.vfs.write().commit_changes(); | 208 | let changes = self.vfs.write().commit_changes(); |
193 | if changes.is_empty() { | 209 | if changes.is_empty() { |
194 | return false; | 210 | 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 @@ | |||
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 | mod lsp_utils; |
9 | 8 | ||
10 | use std::{ | 9 | use std::{ |
11 | borrow::Cow, | 10 | borrow::Cow, |
11 | convert::TryFrom, | ||
12 | env, | 12 | env, |
13 | error::Error, | 13 | error::Error, |
14 | fmt, | 14 | fmt, |
@@ -20,12 +20,8 @@ use std::{ | |||
20 | 20 | ||
21 | use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; | 21 | use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; |
22 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; | 22 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; |
23 | use lsp_types::{ | 23 | use lsp_types::{DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent}; |
24 | DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress, | 24 | use ra_flycheck::CheckTask; |
25 | WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd, | ||
26 | WorkDoneProgressReport, | ||
27 | }; | ||
28 | use ra_flycheck::{CheckTask, Status}; | ||
29 | use ra_ide::{Canceled, FileId, LineIndex}; | 25 | use ra_ide::{Canceled, FileId, LineIndex}; |
30 | use ra_prof::profile; | 26 | use ra_prof::profile; |
31 | use ra_project_model::{PackageRoot, ProjectWorkspace}; | 27 | use ra_project_model::{PackageRoot, ProjectWorkspace}; |
@@ -48,7 +44,12 @@ use crate::{ | |||
48 | }; | 44 | }; |
49 | pub use lsp_utils::show_message; | 45 | pub use lsp_utils::show_message; |
50 | use lsp_utils::{is_canceled, notification_cast, notification_is, notification_new, request_new}; | 46 | use lsp_utils::{is_canceled, notification_cast, notification_is, notification_new, request_new}; |
51 | use progress::{IsDone, PrimeCachesProgressNotifier, WorkspaceAnalysisProgressNotifier}; | 47 | use ra_progress::{ |
48 | IsDone, ProgressStatus, U32Progress, U32ProgressReport, U32ProgressSource, U32ProgressStatus, | ||
49 | }; | ||
50 | |||
51 | const FLYCHECK_PROGRESS_TOKEN: &str = "rustAnalyzer/flycheck"; | ||
52 | const ROOTS_SCANNED_PROGRESS_TOKEN: &str = "rustAnalyzer/rootsScanned"; | ||
52 | 53 | ||
53 | #[derive(Debug)] | 54 | #[derive(Debug)] |
54 | pub struct LspError { | 55 | pub struct LspError { |
@@ -95,7 +96,6 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { | |||
95 | } | 96 | } |
96 | 97 | ||
97 | let mut loop_state = LoopState::default(); | 98 | let mut loop_state = LoopState::default(); |
98 | |||
99 | let mut global_state = { | 99 | let mut global_state = { |
100 | let workspaces = { | 100 | let workspaces = { |
101 | 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 { |
@@ -169,13 +169,16 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { | |||
169 | GlobalState::new(workspaces, config.lru_capacity, &globs, config) | 169 | GlobalState::new(workspaces, config.lru_capacity, &globs, config) |
170 | }; | 170 | }; |
171 | 171 | ||
172 | loop_state.roots_total = global_state.vfs.read().n_roots(); | 172 | loop_state.roots_total = u32::try_from(global_state.vfs.read().n_roots()) |
173 | .expect("Wow, your project is so huge, that it cannot fit into u32..."); | ||
174 | |||
173 | loop_state.roots_scanned = 0; | 175 | loop_state.roots_scanned = 0; |
174 | loop_state.roots_progress = Some(WorkspaceAnalysisProgressNotifier::begin( | 176 | let mut roots_scanned_progress_receiver = { |
175 | connection.sender.clone(), | 177 | let (recv, mut progress_src) = |
176 | loop_state.next_request_id(), | 178 | U32ProgressSource::real_if(global_state.config.client_caps.work_done_progress); |
177 | loop_state.roots_total, | 179 | loop_state.roots_progress = Some(progress_src.begin(0, loop_state.roots_total)); |
178 | )); | 180 | recv |
181 | }; | ||
179 | 182 | ||
180 | let pool = ThreadPool::default(); | 183 | let pool = ThreadPool::default(); |
181 | let (task_sender, task_receiver) = unbounded::<Task>(); | 184 | let (task_sender, task_receiver) = unbounded::<Task>(); |
@@ -198,6 +201,18 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { | |||
198 | recv(global_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task { | 201 | recv(global_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task { |
199 | Ok(task) => Event::CheckWatcher(task), | 202 | Ok(task) => Event::CheckWatcher(task), |
200 | Err(RecvError) => return Err("check watcher died".into()), | 203 | Err(RecvError) => return Err("check watcher died".into()), |
204 | }, | ||
205 | recv(global_state.flycheck_progress_receiver) -> status => match status { | ||
206 | Ok(status) => Event::ProgressReport(ProgressReport::Flycheck(status)), | ||
207 | Err(RecvError) => return Err("check watcher died".into()), | ||
208 | }, | ||
209 | recv(roots_scanned_progress_receiver) -> status => match status { | ||
210 | Ok(status) => Event::ProgressReport(ProgressReport::RootsScanned(status)), | ||
211 | Err(RecvError) => { | ||
212 | // Roots analysis has finished, we no longer need this receiver | ||
213 | roots_scanned_progress_receiver = never(); | ||
214 | continue; | ||
215 | } | ||
201 | } | 216 | } |
202 | }; | 217 | }; |
203 | if let Event::Msg(Message::Request(req)) = &event { | 218 | if let Event::Msg(Message::Request(req)) = &event { |
@@ -228,7 +243,7 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { | |||
228 | #[derive(Debug)] | 243 | #[derive(Debug)] |
229 | enum Task { | 244 | enum Task { |
230 | Respond(Response), | 245 | Respond(Response), |
231 | Notify(Notification), | 246 | SendMessage(Message), |
232 | Diagnostic(DiagnosticTask), | 247 | Diagnostic(DiagnosticTask), |
233 | } | 248 | } |
234 | 249 | ||
@@ -237,6 +252,13 @@ enum Event { | |||
237 | Task(Task), | 252 | Task(Task), |
238 | Vfs(VfsTask), | 253 | Vfs(VfsTask), |
239 | CheckWatcher(CheckTask), | 254 | CheckWatcher(CheckTask), |
255 | ProgressReport(ProgressReport), | ||
256 | } | ||
257 | |||
258 | #[derive(Debug)] | ||
259 | enum ProgressReport { | ||
260 | Flycheck(ProgressStatus<(), String>), | ||
261 | RootsScanned(U32ProgressStatus), | ||
240 | } | 262 | } |
241 | 263 | ||
242 | impl fmt::Debug for Event { | 264 | impl fmt::Debug for Event { |
@@ -253,7 +275,7 @@ impl fmt::Debug for Event { | |||
253 | return debug_verbose_not(not, f); | 275 | return debug_verbose_not(not, f); |
254 | } | 276 | } |
255 | } | 277 | } |
256 | Event::Task(Task::Notify(not)) => { | 278 | Event::Task(Task::SendMessage(Message::Notification(not))) => { |
257 | if notification_is::<lsp_types::notification::PublishDiagnostics>(not) { | 279 | if notification_is::<lsp_types::notification::PublishDiagnostics>(not) { |
258 | return debug_verbose_not(not, f); | 280 | return debug_verbose_not(not, f); |
259 | } | 281 | } |
@@ -272,20 +294,21 @@ impl fmt::Debug for Event { | |||
272 | Event::Task(it) => fmt::Debug::fmt(it, f), | 294 | Event::Task(it) => fmt::Debug::fmt(it, f), |
273 | Event::Vfs(it) => fmt::Debug::fmt(it, f), | 295 | Event::Vfs(it) => fmt::Debug::fmt(it, f), |
274 | Event::CheckWatcher(it) => fmt::Debug::fmt(it, f), | 296 | Event::CheckWatcher(it) => fmt::Debug::fmt(it, f), |
297 | Event::ProgressReport(it) => fmt::Debug::fmt(it, f), | ||
275 | } | 298 | } |
276 | } | 299 | } |
277 | } | 300 | } |
278 | 301 | ||
279 | #[derive(Debug, Default)] | 302 | #[derive(Default)] |
280 | struct LoopState { | 303 | struct LoopState { |
281 | next_request_id: u64, | 304 | next_request_id: u64, |
282 | pending_responses: FxHashSet<RequestId>, | 305 | pending_responses: FxHashSet<RequestId>, |
283 | pending_requests: PendingRequests, | 306 | pending_requests: PendingRequests, |
284 | subscriptions: Subscriptions, | 307 | subscriptions: Subscriptions, |
285 | workspace_loaded: bool, | 308 | workspace_loaded: bool, |
286 | roots_progress: Option<WorkspaceAnalysisProgressNotifier>, | 309 | roots_progress: Option<U32Progress>, |
287 | roots_scanned: usize, | 310 | roots_scanned: u32, |
288 | roots_total: usize, | 311 | roots_total: u32, |
289 | configuration_request_id: Option<RequestId>, | 312 | configuration_request_id: Option<RequestId>, |
290 | } | 313 | } |
291 | 314 | ||
@@ -326,6 +349,9 @@ fn loop_turn( | |||
326 | global_state.vfs.write().handle_task(task); | 349 | global_state.vfs.write().handle_task(task); |
327 | } | 350 | } |
328 | Event::CheckWatcher(task) => on_check_task(task, global_state, task_sender)?, | 351 | Event::CheckWatcher(task) => on_check_task(task, global_state, task_sender)?, |
352 | Event::ProgressReport(report) => { | ||
353 | on_progress_report(report, task_sender, loop_state, global_state) | ||
354 | } | ||
329 | Event::Msg(msg) => match msg { | 355 | Event::Msg(msg) => match msg { |
330 | Message::Request(req) => on_request( | 356 | Message::Request(req) => on_request( |
331 | global_state, | 357 | global_state, |
@@ -384,7 +410,13 @@ fn loop_turn( | |||
384 | } | 410 | } |
385 | 411 | ||
386 | if show_progress { | 412 | if show_progress { |
387 | send_workspace_analisys_progress(loop_state); | 413 | if let Some(progress) = &mut loop_state.roots_progress { |
414 | if loop_state.workspace_loaded | ||
415 | || progress.report(loop_state.roots_scanned) == IsDone(true) | ||
416 | { | ||
417 | loop_state.roots_progress = None; | ||
418 | } | ||
419 | } | ||
388 | } | 420 | } |
389 | 421 | ||
390 | if state_changed && loop_state.workspace_loaded { | 422 | if state_changed && loop_state.workspace_loaded { |
@@ -397,22 +429,7 @@ fn loop_turn( | |||
397 | pool.execute({ | 429 | pool.execute({ |
398 | let subs = loop_state.subscriptions.subscriptions(); | 430 | let subs = loop_state.subscriptions.subscriptions(); |
399 | let snap = global_state.snapshot(); | 431 | let snap = global_state.snapshot(); |
400 | 432 | move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ()) | |
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 | } | ||
416 | }); | 433 | }); |
417 | } | 434 | } |
418 | 435 | ||
@@ -431,6 +448,87 @@ fn loop_turn( | |||
431 | Ok(()) | 448 | Ok(()) |
432 | } | 449 | } |
433 | 450 | ||
451 | fn on_progress_report( | ||
452 | report: ProgressReport, | ||
453 | task_sender: &Sender<Task>, | ||
454 | loop_state: &mut LoopState, | ||
455 | global_state: &GlobalState, | ||
456 | ) { | ||
457 | let end_report = | ||
458 | || lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message: None }); | ||
459 | let mut create_progress = |token: &'static str| { | ||
460 | let create_progress_req = request_new::<lsp_types::request::WorkDoneProgressCreate>( | ||
461 | loop_state.next_request_id(), | ||
462 | lsp_types::WorkDoneProgressCreateParams { | ||
463 | token: lsp_types::ProgressToken::String(token.to_string()), | ||
464 | }, | ||
465 | ); | ||
466 | task_sender.send(Task::SendMessage(create_progress_req.into())).unwrap(); | ||
467 | }; | ||
468 | |||
469 | let (token, progress) = match report { | ||
470 | ProgressReport::Flycheck(status) => { | ||
471 | let command = global_state | ||
472 | .config | ||
473 | .check | ||
474 | .as_ref() | ||
475 | .expect("There should be config, since flycheck is active"); | ||
476 | |||
477 | let progress = match status { | ||
478 | ProgressStatus::Begin(()) => { | ||
479 | create_progress(FLYCHECK_PROGRESS_TOKEN); | ||
480 | lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { | ||
481 | title: "".to_string(), | ||
482 | cancellable: Some(false), | ||
483 | message: Some(command.to_string()), | ||
484 | percentage: None, | ||
485 | }) | ||
486 | } | ||
487 | ProgressStatus::Progress(target) => { | ||
488 | lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport { | ||
489 | cancellable: Some(false), | ||
490 | message: Some(format!("{} [{}]", command, target)), | ||
491 | percentage: None, | ||
492 | }) | ||
493 | } | ||
494 | ProgressStatus::End => end_report(), | ||
495 | }; | ||
496 | (FLYCHECK_PROGRESS_TOKEN, progress) | ||
497 | } | ||
498 | ProgressReport::RootsScanned(status) => { | ||
499 | fn to_message(report: &U32ProgressReport) -> String { | ||
500 | report.to_message("analyzing the workspace", "packages") | ||
501 | } | ||
502 | let progress = match status { | ||
503 | ProgressStatus::Begin(report) => { | ||
504 | create_progress(ROOTS_SCANNED_PROGRESS_TOKEN); | ||
505 | lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { | ||
506 | title: "rust-analyzer".to_string(), | ||
507 | cancellable: Some(false), | ||
508 | message: Some(to_message(&report)), | ||
509 | percentage: Some(report.percentage()), | ||
510 | }) | ||
511 | } | ||
512 | ProgressStatus::Progress(report) => { | ||
513 | lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport { | ||
514 | cancellable: Some(false), | ||
515 | message: Some(to_message(&report)), | ||
516 | percentage: Some(report.percentage()), | ||
517 | }) | ||
518 | } | ||
519 | ProgressStatus::End => end_report(), | ||
520 | }; | ||
521 | (ROOTS_SCANNED_PROGRESS_TOKEN, progress) | ||
522 | } | ||
523 | }; | ||
524 | let params = lsp_types::ProgressParams { | ||
525 | token: lsp_types::ProgressToken::String(token.to_string()), | ||
526 | value: lsp_types::ProgressParamsValue::WorkDone(progress), | ||
527 | }; | ||
528 | let not = notification_new::<lsp_types::notification::Progress>(params); | ||
529 | task_sender.send(Task::SendMessage(not.into())).unwrap() | ||
530 | } | ||
531 | |||
434 | fn on_task( | 532 | fn on_task( |
435 | task: Task, | 533 | task: Task, |
436 | msg_sender: &Sender<Message>, | 534 | msg_sender: &Sender<Message>, |
@@ -445,9 +543,7 @@ fn on_task( | |||
445 | msg_sender.send(response.into()).unwrap(); | 543 | msg_sender.send(response.into()).unwrap(); |
446 | } | 544 | } |
447 | } | 545 | } |
448 | Task::Notify(n) => { | 546 | Task::SendMessage(msg) => msg_sender.send(msg).unwrap(), |
449 | msg_sender.send(n.into()).unwrap(); | ||
450 | } | ||
451 | Task::Diagnostic(task) => on_diagnostic_task(task, msg_sender, state), | 547 | Task::Diagnostic(task) => on_diagnostic_task(task, msg_sender, state), |
452 | } | 548 | } |
453 | } | 549 | } |
@@ -718,42 +814,6 @@ fn on_check_task( | |||
718 | )))?; | 814 | )))?; |
719 | } | 815 | } |
720 | } | 816 | } |
721 | |||
722 | CheckTask::Status(status) => { | ||
723 | if global_state.config.client_caps.work_done_progress { | ||
724 | let progress = match status { | ||
725 | Status::Being => { | ||
726 | lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { | ||
727 | title: "Running `cargo check`".to_string(), | ||
728 | cancellable: Some(false), | ||
729 | message: None, | ||
730 | percentage: None, | ||
731 | }) | ||
732 | } | ||
733 | Status::Progress(target) => { | ||
734 | lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport { | ||
735 | cancellable: Some(false), | ||
736 | message: Some(target), | ||
737 | percentage: None, | ||
738 | }) | ||
739 | } | ||
740 | Status::End => { | ||
741 | lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { | ||
742 | message: None, | ||
743 | }) | ||
744 | } | ||
745 | }; | ||
746 | |||
747 | let params = lsp_types::ProgressParams { | ||
748 | token: lsp_types::ProgressToken::String( | ||
749 | "rustAnalyzer/cargoWatcher".to_string(), | ||
750 | ), | ||
751 | value: lsp_types::ProgressParamsValue::WorkDone(progress), | ||
752 | }; | ||
753 | let not = notification_new::<lsp_types::notification::Progress>(params); | ||
754 | task_sender.send(Task::Notify(not)).unwrap(); | ||
755 | } | ||
756 | } | ||
757 | }; | 817 | }; |
758 | 818 | ||
759 | Ok(()) | 819 | Ok(()) |
@@ -771,15 +831,6 @@ fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state: | |||
771 | } | 831 | } |
772 | } | 832 | } |
773 | 833 | ||
774 | fn send_workspace_analisys_progress(loop_state: &mut LoopState) { | ||
775 | if let Some(progress) = &mut loop_state.roots_progress { | ||
776 | if loop_state.workspace_loaded || progress.report(loop_state.roots_scanned) == IsDone(true) | ||
777 | { | ||
778 | loop_state.roots_progress = None; | ||
779 | } | ||
780 | } | ||
781 | } | ||
782 | |||
783 | struct PoolDispatcher<'a> { | 834 | struct PoolDispatcher<'a> { |
784 | req: Option<Request>, | 835 | req: Option<Request>, |
785 | pool: &'a ThreadPool, | 836 | 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 @@ | |||
1 | //! Utilities for LSP-related boilerplate code. | ||
2 | |||
1 | use crossbeam_channel::Sender; | 3 | use crossbeam_channel::Sender; |
2 | use lsp_server::{Message, Notification, Request, RequestId}; | 4 | use lsp_server::{Message, Notification, Request, RequestId}; |
3 | use ra_db::Canceled; | 5 | use ra_db::Canceled; |
4 | use serde::{de::DeserializeOwned, Serialize}; | 6 | use serde::{de::DeserializeOwned, Serialize}; |
5 | use std::error::Error; | 7 | use std::error::Error; |
6 | 8 | ||
7 | pub fn show_message(typ: lsp_types::MessageType, message: impl Into<String>, sender: &Sender<Message>) { | 9 | pub fn show_message( |
10 | typ: lsp_types::MessageType, | ||
11 | message: impl Into<String>, | ||
12 | sender: &Sender<Message>, | ||
13 | ) { | ||
8 | let message = message.into(); | 14 | let message = message.into(); |
9 | let params = lsp_types::ShowMessageParams { typ, message }; | 15 | let params = lsp_types::ShowMessageParams { typ, message }; |
10 | let not = notification_new::<lsp_types::notification::ShowMessage>(params); | 16 | let not = notification_new::<lsp_types::notification::ShowMessage>(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 @@ | |||
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 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 { | |||
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/workspaceAnalysis" => true, | 215 | } if token == "rustAnalyzer/rootsScanned" => true, |
216 | _ => false, | 216 | _ => false, |
217 | } | 217 | } |
218 | } | 218 | } |