diff options
-rw-r--r-- | Cargo.lock | 13 | ||||
-rw-r--r-- | crates/ra_flycheck/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_flycheck/src/lib.rs | 62 | ||||
-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 | 23 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lsp_utils.rs | 52 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 36 | ||||
-rw-r--r-- | crates/rust-analyzer/tests/heavy_tests/support.rs | 4 | ||||
-rw-r--r-- | editors/code/src/main.ts | 3 | ||||
-rw-r--r-- | editors/code/src/status_display.ts | 100 |
12 files changed, 299 insertions, 133 deletions
diff --git a/Cargo.lock b/Cargo.lock index c2d00adeb..bed9acf8f 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -967,6 +967,7 @@ dependencies = [ | |||
967 | "crossbeam-channel", | 967 | "crossbeam-channel", |
968 | "jod-thread", | 968 | "jod-thread", |
969 | "log", | 969 | "log", |
970 | "ra_progress", | ||
970 | "ra_toolchain", | 971 | "ra_toolchain", |
971 | "serde_json", | 972 | "serde_json", |
972 | ] | 973 | ] |
@@ -1080,7 +1081,11 @@ dependencies = [ | |||
1080 | "ra_hir", | 1081 | "ra_hir", |
1081 | "ra_ide_db", | 1082 | "ra_ide_db", |
1082 | "ra_prof", | 1083 | "ra_prof", |
1084 | <<<<<<< HEAD | ||
1083 | "ra_ssr", | 1085 | "ra_ssr", |
1086 | ======= | ||
1087 | "ra_progress", | ||
1088 | >>>>>>> Veetaha-feat/sync-branch | ||
1084 | "ra_syntax", | 1089 | "ra_syntax", |
1085 | "ra_text_edit", | 1090 | "ra_text_edit", |
1086 | "rand", | 1091 | "rand", |
@@ -1169,6 +1174,13 @@ dependencies = [ | |||
1169 | ] | 1174 | ] |
1170 | 1175 | ||
1171 | [[package]] | 1176 | [[package]] |
1177 | name = "ra_progress" | ||
1178 | version = "0.1.0" | ||
1179 | dependencies = [ | ||
1180 | "crossbeam-channel", | ||
1181 | ] | ||
1182 | |||
1183 | [[package]] | ||
1172 | name = "ra_project_model" | 1184 | name = "ra_project_model" |
1173 | version = "0.1.0" | 1185 | version = "0.1.0" |
1174 | dependencies = [ | 1186 | dependencies = [ |
@@ -1392,6 +1404,7 @@ dependencies = [ | |||
1392 | "ra_mbe", | 1404 | "ra_mbe", |
1393 | "ra_proc_macro_srv", | 1405 | "ra_proc_macro_srv", |
1394 | "ra_prof", | 1406 | "ra_prof", |
1407 | "ra_progress", | ||
1395 | "ra_project_model", | 1408 | "ra_project_model", |
1396 | "ra_syntax", | 1409 | "ra_syntax", |
1397 | "ra_text_edit", | 1410 | "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" | |||
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_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 68d04f3e3..2bbed395f 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 87f3fe4db..7759c0ae3 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -27,9 +27,13 @@ use crate::{ | |||
27 | }; | 27 | }; |
28 | use rustc_hash::{FxHashMap, FxHashSet}; | 28 | use rustc_hash::{FxHashMap, FxHashSet}; |
29 | 29 | ||
30 | fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) -> Option<Flycheck> { | 30 | fn create_flycheck( |
31 | workspaces: &[ProjectWorkspace], | ||
32 | config: &FlycheckConfig, | ||
33 | progress_src: &ProgressSource<(), String>, | ||
34 | ) -> Option<Flycheck> { | ||
31 | // FIXME: Figure out the multi-workspace situation | 35 | // FIXME: Figure out the multi-workspace situation |
32 | workspaces.iter().find_map(|w| match w { | 36 | workspaces.iter().find_map(move |w| match w { |
33 | ProjectWorkspace::Cargo { cargo, .. } => { | 37 | ProjectWorkspace::Cargo { cargo, .. } => { |
34 | let cargo_project_root = cargo.workspace_root().to_path_buf(); | 38 | let cargo_project_root = cargo.workspace_root().to_path_buf(); |
35 | Some(Flycheck::new(config.clone(), cargo_project_root.into())) | 39 | Some(Flycheck::new(config.clone(), cargo_project_root.into())) |
@@ -143,7 +147,12 @@ impl GlobalState { | |||
143 | } | 147 | } |
144 | change.set_crate_graph(crate_graph); | 148 | change.set_crate_graph(crate_graph); |
145 | 149 | ||
146 | let flycheck = config.check.as_ref().and_then(|c| create_flycheck(&workspaces, c)); | 150 | let (flycheck_progress_receiver, flycheck_progress_src) = |
151 | ProgressSource::real_if(config.client_caps.work_done_progress); | ||
152 | let flycheck = config | ||
153 | .check | ||
154 | .as_ref() | ||
155 | .and_then(|c| create_flycheck(&workspaces, c, &flycheck_progress_src)); | ||
147 | 156 | ||
148 | let mut analysis_host = AnalysisHost::new(lru_capacity); | 157 | let mut analysis_host = AnalysisHost::new(lru_capacity); |
149 | analysis_host.apply_change(change); | 158 | analysis_host.apply_change(change); |
@@ -153,6 +162,8 @@ impl GlobalState { | |||
153 | loader, | 162 | loader, |
154 | task_receiver, | 163 | task_receiver, |
155 | flycheck, | 164 | flycheck, |
165 | flycheck_progress_src, | ||
166 | flycheck_progress_receiver, | ||
156 | diagnostics: Default::default(), | 167 | diagnostics: Default::default(), |
157 | mem_docs: FxHashSet::default(), | 168 | mem_docs: FxHashSet::default(), |
158 | vfs: Arc::new(RwLock::new((vfs, FxHashMap::default()))), | 169 | vfs: Arc::new(RwLock::new((vfs, FxHashMap::default()))), |
@@ -170,8 +181,10 @@ impl GlobalState { | |||
170 | pub(crate) fn update_configuration(&mut self, config: Config) { | 181 | pub(crate) fn update_configuration(&mut self, config: Config) { |
171 | self.analysis_host.update_lru_capacity(config.lru_capacity); | 182 | self.analysis_host.update_lru_capacity(config.lru_capacity); |
172 | if config.check != self.config.check { | 183 | if config.check != self.config.check { |
173 | self.flycheck = | 184 | self.flycheck = config |
174 | config.check.as_ref().and_then(|it| create_flycheck(&self.workspaces, it)); | 185 | .check |
186 | .as_ref() | ||
187 | .and_then(|it| create_flycheck(&self.workspaces, it, &self.flycheck_progress_src)); | ||
175 | } | 188 | } |
176 | 189 | ||
177 | self.config = config; | 190 | self.config = config; |
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs new file mode 100644 index 000000000..c79022797 --- /dev/null +++ b/crates/rust-analyzer/src/lsp_utils.rs | |||
@@ -0,0 +1,52 @@ | |||
1 | //! Utilities for LSP-related boilerplate code. | ||
2 | |||
3 | use crossbeam_channel::Sender; | ||
4 | use lsp_server::{Message, Notification, Request, RequestId}; | ||
5 | use ra_db::Canceled; | ||
6 | use serde::{de::DeserializeOwned, Serialize}; | ||
7 | use std::error::Error; | ||
8 | |||
9 | pub fn show_message( | ||
10 | typ: lsp_types::MessageType, | ||
11 | message: impl Into<String>, | ||
12 | sender: &Sender<Message>, | ||
13 | ) { | ||
14 | let message = message.into(); | ||
15 | let params = lsp_types::ShowMessageParams { typ, message }; | ||
16 | let not = notification_new::<lsp_types::notification::ShowMessage>(params); | ||
17 | sender.send(not.into()).unwrap(); | ||
18 | } | ||
19 | |||
20 | pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool { | ||
21 | e.downcast_ref::<Canceled>().is_some() | ||
22 | } | ||
23 | |||
24 | pub(crate) fn notification_is<N: lsp_types::notification::Notification>( | ||
25 | notification: &Notification, | ||
26 | ) -> bool { | ||
27 | notification.method == N::METHOD | ||
28 | } | ||
29 | |||
30 | pub(crate) fn notification_cast<N>(notification: Notification) -> Result<N::Params, Notification> | ||
31 | where | ||
32 | N: lsp_types::notification::Notification, | ||
33 | N::Params: DeserializeOwned, | ||
34 | { | ||
35 | notification.extract(N::METHOD) | ||
36 | } | ||
37 | |||
38 | pub(crate) fn notification_new<N>(params: N::Params) -> Notification | ||
39 | where | ||
40 | N: lsp_types::notification::Notification, | ||
41 | N::Params: Serialize, | ||
42 | { | ||
43 | Notification::new(N::METHOD.to_string(), params) | ||
44 | } | ||
45 | |||
46 | pub(crate) fn request_new<R>(id: RequestId, params: R::Params) -> Request | ||
47 | where | ||
48 | R: lsp_types::request::Request, | ||
49 | R::Params: Serialize, | ||
50 | { | ||
51 | Request::new(id, R::METHOD.to_string(), params) | ||
52 | } | ||
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index eb9e7f913..7ccdbd29c 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -28,6 +28,14 @@ use crate::{ | |||
28 | request_metrics::RequestMetrics, | 28 | request_metrics::RequestMetrics, |
29 | LspError, Result, | 29 | LspError, Result, |
30 | }; | 30 | }; |
31 | pub use lsp_utils::show_message; | ||
32 | use lsp_utils::{is_canceled, notification_cast, notification_is, notification_new, request_new}; | ||
33 | use ra_progress::{ | ||
34 | IsDone, ProgressStatus, U32Progress, U32ProgressReport, U32ProgressSource, U32ProgressStatus, | ||
35 | }; | ||
36 | |||
37 | const FLYCHECK_PROGRESS_TOKEN: &str = "rustAnalyzer/flycheck"; | ||
38 | const ROOTS_SCANNED_PROGRESS_TOKEN: &str = "rustAnalyzer/rootsScanned"; | ||
31 | 39 | ||
32 | pub fn main_loop(config: Config, connection: Connection) -> Result<()> { | 40 | pub fn main_loop(config: Config, connection: Connection) -> Result<()> { |
33 | log::info!("initial config: {:#?}", config); | 41 | log::info!("initial config: {:#?}", config); |
@@ -138,6 +146,18 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { | |||
138 | recv(global_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task { | 146 | recv(global_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task { |
139 | Ok(task) => Event::CheckWatcher(task), | 147 | Ok(task) => Event::CheckWatcher(task), |
140 | Err(RecvError) => return Err("check watcher died".into()), | 148 | Err(RecvError) => return Err("check watcher died".into()), |
149 | }, | ||
150 | recv(global_state.flycheck_progress_receiver) -> status => match status { | ||
151 | Ok(status) => Event::ProgressReport(ProgressReport::Flycheck(status)), | ||
152 | Err(RecvError) => return Err("check watcher died".into()), | ||
153 | }, | ||
154 | recv(roots_scanned_progress_receiver) -> status => match status { | ||
155 | Ok(status) => Event::ProgressReport(ProgressReport::RootsScanned(status)), | ||
156 | Err(RecvError) => { | ||
157 | // Roots analysis has finished, we no longer need this receiver | ||
158 | roots_scanned_progress_receiver = never(); | ||
159 | continue; | ||
160 | } | ||
141 | } | 161 | } |
142 | }; | 162 | }; |
143 | if let Event::Msg(Message::Request(req)) = &event { | 163 | if let Event::Msg(Message::Request(req)) = &event { |
@@ -169,6 +189,7 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { | |||
169 | enum Task { | 189 | enum Task { |
170 | Respond(Response), | 190 | Respond(Response), |
171 | Notify(Notification), | 191 | Notify(Notification), |
192 | SendRequest(Request), | ||
172 | Diagnostic(DiagnosticTask), | 193 | Diagnostic(DiagnosticTask), |
173 | } | 194 | } |
174 | 195 | ||
@@ -177,6 +198,13 @@ enum Event { | |||
177 | Task(Task), | 198 | Task(Task), |
178 | Vfs(vfs::loader::Message), | 199 | Vfs(vfs::loader::Message), |
179 | CheckWatcher(CheckTask), | 200 | CheckWatcher(CheckTask), |
201 | ProgressReport(ProgressReport), | ||
202 | } | ||
203 | |||
204 | #[derive(Debug)] | ||
205 | enum ProgressReport { | ||
206 | Flycheck(ProgressStatus<(), String>), | ||
207 | RootsScanned(U32ProgressStatus), | ||
180 | } | 208 | } |
181 | 209 | ||
182 | impl fmt::Debug for Event { | 210 | impl fmt::Debug for Event { |
@@ -212,6 +240,7 @@ impl fmt::Debug for Event { | |||
212 | Event::Task(it) => fmt::Debug::fmt(it, f), | 240 | Event::Task(it) => fmt::Debug::fmt(it, f), |
213 | Event::Vfs(it) => fmt::Debug::fmt(it, f), | 241 | Event::Vfs(it) => fmt::Debug::fmt(it, f), |
214 | Event::CheckWatcher(it) => fmt::Debug::fmt(it, f), | 242 | Event::CheckWatcher(it) => fmt::Debug::fmt(it, f), |
243 | Event::ProgressReport(it) => fmt::Debug::fmt(it, f), | ||
215 | } | 244 | } |
216 | } | 245 | } |
217 | } | 246 | } |
@@ -262,6 +291,9 @@ fn loop_turn( | |||
262 | } | 291 | } |
263 | }, | 292 | }, |
264 | Event::CheckWatcher(task) => on_check_task(task, global_state, task_sender)?, | 293 | Event::CheckWatcher(task) => on_check_task(task, global_state, task_sender)?, |
294 | Event::ProgressReport(report) => { | ||
295 | on_progress_report(report, task_sender, loop_state, global_state) | ||
296 | } | ||
265 | Event::Msg(msg) => match msg { | 297 | Event::Msg(msg) => match msg { |
266 | Message::Request(req) => { | 298 | Message::Request(req) => { |
267 | on_request(global_state, pool, task_sender, &connection.sender, loop_start, req)? | 299 | on_request(global_state, pool, task_sender, &connection.sender, loop_start, req)? |
@@ -826,7 +858,7 @@ where | |||
826 | Err(e) => match e.downcast::<LspError>() { | 858 | Err(e) => match e.downcast::<LspError>() { |
827 | Ok(lsp_error) => Response::new_err(id, lsp_error.code, lsp_error.message), | 859 | Ok(lsp_error) => Response::new_err(id, lsp_error.code, lsp_error.message), |
828 | Err(e) => { | 860 | Err(e) => { |
829 | if is_canceled(&e) { | 861 | if is_canceled(&*e) { |
830 | Response::new_err( | 862 | Response::new_err( |
831 | id, | 863 | id, |
832 | ErrorCode::ContentModified as i32, | 864 | ErrorCode::ContentModified as i32, |
@@ -853,7 +885,7 @@ fn update_file_notifications_on_threadpool( | |||
853 | for file_id in subscriptions { | 885 | for file_id in subscriptions { |
854 | match handlers::publish_diagnostics(&world, file_id) { | 886 | match handlers::publish_diagnostics(&world, file_id) { |
855 | Err(e) => { | 887 | Err(e) => { |
856 | if !is_canceled(&e) { | 888 | if !is_canceled(&*e) { |
857 | log::error!("failed to compute diagnostics: {:?}", e); | 889 | log::error!("failed to compute diagnostics: {:?}", e); |
858 | } | 890 | } |
859 | } | 891 | } |
diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs index 49f194f7e..15d2a05a4 100644 --- a/crates/rust-analyzer/tests/heavy_tests/support.rs +++ b/crates/rust-analyzer/tests/heavy_tests/support.rs | |||
@@ -202,7 +202,11 @@ impl Server { | |||
202 | ProgressParams { | 202 | ProgressParams { |
203 | token: lsp_types::ProgressToken::String(ref token), | 203 | token: lsp_types::ProgressToken::String(ref token), |
204 | value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(_)), | 204 | value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(_)), |
205 | <<<<<<< HEAD | ||
205 | } if token == "rustAnalyzer/roots scanned" => true, | 206 | } if token == "rustAnalyzer/roots scanned" => true, |
207 | ======= | ||
208 | } if token == "rustAnalyzer/rootsScanned" => true, | ||
209 | >>>>>>> Veetaha-feat/sync-branch | ||
206 | _ => false, | 210 | _ => false, |
207 | } | 211 | } |
208 | } | 212 | } |
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 12b4d0510..cdb63b46f 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts | |||
@@ -5,7 +5,6 @@ import { promises as fs, PathLike } from "fs"; | |||
5 | 5 | ||
6 | import * as commands from './commands'; | 6 | import * as commands from './commands'; |
7 | import { activateInlayHints } from './inlay_hints'; | 7 | import { activateInlayHints } from './inlay_hints'; |
8 | import { activateStatusDisplay } from './status_display'; | ||
9 | import { Ctx } from './ctx'; | 8 | import { Ctx } from './ctx'; |
10 | import { Config, NIGHTLY_TAG } from './config'; | 9 | import { Config, NIGHTLY_TAG } from './config'; |
11 | import { log, assert, isValidExecutable } from './util'; | 10 | import { log, assert, isValidExecutable } from './util'; |
@@ -117,8 +116,6 @@ export async function activate(context: vscode.ExtensionContext) { | |||
117 | 116 | ||
118 | ctx.pushCleanup(activateTaskProvider(workspaceFolder)); | 117 | ctx.pushCleanup(activateTaskProvider(workspaceFolder)); |
119 | 118 | ||
120 | activateStatusDisplay(ctx); | ||
121 | |||
122 | activateInlayHints(ctx); | 119 | activateInlayHints(ctx); |
123 | 120 | ||
124 | vscode.workspace.onDidChangeConfiguration( | 121 | 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 @@ | |||
1 | import * as vscode from 'vscode'; | ||
2 | |||
3 | import { WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressReport, WorkDoneProgressEnd, Disposable } from 'vscode-languageclient'; | ||
4 | |||
5 | import { Ctx } from './ctx'; | ||
6 | |||
7 | const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; | ||
8 | |||
9 | export function activateStatusDisplay(ctx: Ctx) { | ||
10 | const statusDisplay = new StatusDisplay(ctx.config.checkOnSave.command); | ||
11 | ctx.pushCleanup(statusDisplay); | ||
12 | const client = ctx.client; | ||
13 | if (client != null) { | ||
14 | ctx.pushCleanup(client.onProgress( | ||
15 | WorkDoneProgress.type, | ||
16 | 'rustAnalyzer/cargoWatcher', | ||
17 | params => statusDisplay.handleProgressNotification(params) | ||
18 | )); | ||
19 | } | ||
20 | } | ||
21 | |||
22 | class StatusDisplay implements Disposable { | ||
23 | packageName?: string; | ||
24 | |||
25 | private i: number = 0; | ||
26 | private statusBarItem: vscode.StatusBarItem; | ||
27 | private command: string; | ||
28 | private timer?: NodeJS.Timeout; | ||
29 | |||
30 | constructor(command: string) { | ||
31 | this.statusBarItem = vscode.window.createStatusBarItem( | ||
32 | vscode.StatusBarAlignment.Left, | ||
33 | 10, | ||
34 | ); | ||
35 | this.command = command; | ||
36 | this.statusBarItem.hide(); | ||
37 | } | ||
38 | |||
39 | show() { | ||
40 | this.packageName = undefined; | ||
41 | |||
42 | this.timer = | ||
43 | this.timer || | ||
44 | setInterval(() => { | ||
45 | this.tick(); | ||
46 | this.refreshLabel(); | ||
47 | }, 300); | ||
48 | |||
49 | this.statusBarItem.show(); | ||
50 | } | ||
51 | |||
52 | hide() { | ||
53 | if (this.timer) { | ||
54 | clearInterval(this.timer); | ||
55 | this.timer = undefined; | ||
56 | } | ||
57 | |||
58 | this.statusBarItem.hide(); | ||
59 | } | ||
60 | |||
61 | dispose() { | ||
62 | if (this.timer) { | ||
63 | clearInterval(this.timer); | ||
64 | this.timer = undefined; | ||
65 | } | ||
66 | |||
67 | this.statusBarItem.dispose(); | ||
68 | } | ||
69 | |||
70 | refreshLabel() { | ||
71 | if (this.packageName) { | ||
72 | this.statusBarItem.text = `${spinnerFrames[this.i]} cargo ${this.command} [${this.packageName}]`; | ||
73 | } else { | ||
74 | this.statusBarItem.text = `${spinnerFrames[this.i]} cargo ${this.command}`; | ||
75 | } | ||
76 | } | ||
77 | |||
78 | handleProgressNotification(params: WorkDoneProgressBegin | WorkDoneProgressReport | WorkDoneProgressEnd) { | ||
79 | switch (params.kind) { | ||
80 | case 'begin': | ||
81 | this.show(); | ||
82 | break; | ||
83 | |||
84 | case 'report': | ||
85 | if (params.message) { | ||
86 | this.packageName = params.message; | ||
87 | this.refreshLabel(); | ||
88 | } | ||
89 | break; | ||
90 | |||
91 | case 'end': | ||
92 | this.hide(); | ||
93 | break; | ||
94 | } | ||
95 | } | ||
96 | |||
97 | private tick() { | ||
98 | this.i = (this.i + 1) % spinnerFrames.length; | ||
99 | } | ||
100 | } | ||