diff options
-rw-r--r-- | crates/flycheck/src/lib.rs | 92 | ||||
-rw-r--r-- | crates/rust-analyzer/src/global_state.rs | 50 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lsp_utils.rs | 85 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 115 | ||||
-rw-r--r-- | crates/rust-analyzer/src/reload.rs | 44 |
6 files changed, 185 insertions, 203 deletions
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 4dcab7a61..92ec4f92e 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs | |||
@@ -7,7 +7,7 @@ use std::{ | |||
7 | io::{self, BufReader}, | 7 | io::{self, BufReader}, |
8 | path::PathBuf, | 8 | path::PathBuf, |
9 | process::{Command, Stdio}, | 9 | process::{Command, Stdio}, |
10 | time::Instant, | 10 | time::Duration, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; | 13 | use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; |
@@ -74,9 +74,6 @@ impl FlycheckHandle { | |||
74 | 74 | ||
75 | #[derive(Debug)] | 75 | #[derive(Debug)] |
76 | pub enum Message { | 76 | pub enum Message { |
77 | /// Request a clearing of all cached diagnostics from the check watcher | ||
78 | ClearDiagnostics, | ||
79 | |||
80 | /// Request adding a diagnostic with fixes included to a file | 77 | /// Request adding a diagnostic with fixes included to a file |
81 | AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic }, | 78 | AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic }, |
82 | 79 | ||
@@ -86,9 +83,10 @@ pub enum Message { | |||
86 | 83 | ||
87 | #[derive(Debug)] | 84 | #[derive(Debug)] |
88 | pub enum Progress { | 85 | pub enum Progress { |
89 | Being, | 86 | DidStart, |
90 | DidCheckCrate(String), | 87 | DidCheckCrate(String), |
91 | End, | 88 | DidFinish, |
89 | DidCancel, | ||
92 | } | 90 | } |
93 | 91 | ||
94 | struct Restart; | 92 | struct Restart; |
@@ -97,19 +95,18 @@ struct FlycheckActor { | |||
97 | sender: Box<dyn Fn(Message) + Send>, | 95 | sender: Box<dyn Fn(Message) + Send>, |
98 | config: FlycheckConfig, | 96 | config: FlycheckConfig, |
99 | workspace_root: PathBuf, | 97 | workspace_root: PathBuf, |
100 | last_update_req: Option<Instant>, | ||
101 | /// WatchThread exists to wrap around the communication needed to be able to | 98 | /// WatchThread exists to wrap around the communication needed to be able to |
102 | /// run `cargo check` without blocking. Currently the Rust standard library | 99 | /// run `cargo check` without blocking. Currently the Rust standard library |
103 | /// doesn't provide a way to read sub-process output without blocking, so we | 100 | /// doesn't provide a way to read sub-process output without blocking, so we |
104 | /// have to wrap sub-processes output handling in a thread and pass messages | 101 | /// have to wrap sub-processes output handling in a thread and pass messages |
105 | /// back over a channel. | 102 | /// back over a channel. |
106 | // XXX: drop order is significant | 103 | // XXX: drop order is significant |
107 | check_process: Option<(Receiver<CheckEvent>, jod_thread::JoinHandle)>, | 104 | check_process: Option<(Receiver<cargo_metadata::Message>, jod_thread::JoinHandle)>, |
108 | } | 105 | } |
109 | 106 | ||
110 | enum Event { | 107 | enum Event { |
111 | Restart(Restart), | 108 | Restart(Restart), |
112 | CheckEvent(Option<CheckEvent>), | 109 | CheckEvent(Option<cargo_metadata::Message>), |
113 | } | 110 | } |
114 | 111 | ||
115 | impl FlycheckActor { | 112 | impl FlycheckActor { |
@@ -118,7 +115,7 @@ impl FlycheckActor { | |||
118 | config: FlycheckConfig, | 115 | config: FlycheckConfig, |
119 | workspace_root: PathBuf, | 116 | workspace_root: PathBuf, |
120 | ) -> FlycheckActor { | 117 | ) -> FlycheckActor { |
121 | FlycheckActor { sender, config, workspace_root, last_update_req: None, check_process: None } | 118 | FlycheckActor { sender, config, workspace_root, check_process: None } |
122 | } | 119 | } |
123 | fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> { | 120 | fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> { |
124 | let check_chan = self.check_process.as_ref().map(|(chan, _thread)| chan); | 121 | let check_chan = self.check_process.as_ref().map(|(chan, _thread)| chan); |
@@ -128,65 +125,48 @@ impl FlycheckActor { | |||
128 | } | 125 | } |
129 | } | 126 | } |
130 | fn run(&mut self, inbox: Receiver<Restart>) { | 127 | fn run(&mut self, inbox: Receiver<Restart>) { |
131 | // If we rerun the thread, we need to discard the previous check results first | ||
132 | self.send(Message::ClearDiagnostics); | ||
133 | self.send(Message::Progress(Progress::End)); | ||
134 | |||
135 | while let Some(event) = self.next_event(&inbox) { | 128 | while let Some(event) = self.next_event(&inbox) { |
136 | match event { | 129 | match event { |
137 | Event::Restart(Restart) => self.last_update_req = Some(Instant::now()), | 130 | Event::Restart(Restart) => { |
131 | while let Ok(Restart) = inbox.recv_timeout(Duration::from_millis(50)) {} | ||
132 | self.cancel_check_process(); | ||
133 | self.check_process = Some(self.start_check_process()); | ||
134 | self.send(Message::Progress(Progress::DidStart)); | ||
135 | } | ||
138 | Event::CheckEvent(None) => { | 136 | Event::CheckEvent(None) => { |
139 | // Watcher finished, replace it with a never channel to | 137 | // Watcher finished, replace it with a never channel to |
140 | // avoid busy-waiting. | 138 | // avoid busy-waiting. |
141 | self.check_process = None; | 139 | assert!(self.check_process.take().is_some()); |
140 | self.send(Message::Progress(Progress::DidFinish)); | ||
142 | } | 141 | } |
143 | Event::CheckEvent(Some(event)) => match event { | 142 | Event::CheckEvent(Some(message)) => match message { |
144 | CheckEvent::Begin => { | 143 | cargo_metadata::Message::CompilerArtifact(msg) => { |
145 | self.send(Message::Progress(Progress::Being)); | ||
146 | } | ||
147 | |||
148 | CheckEvent::End => { | ||
149 | self.send(Message::Progress(Progress::End)); | ||
150 | } | ||
151 | |||
152 | CheckEvent::Msg(cargo_metadata::Message::CompilerArtifact(msg)) => { | ||
153 | self.send(Message::Progress(Progress::DidCheckCrate(msg.target.name))); | 144 | self.send(Message::Progress(Progress::DidCheckCrate(msg.target.name))); |
154 | } | 145 | } |
155 | 146 | ||
156 | CheckEvent::Msg(cargo_metadata::Message::CompilerMessage(msg)) => { | 147 | cargo_metadata::Message::CompilerMessage(msg) => { |
157 | self.send(Message::AddDiagnostic { | 148 | self.send(Message::AddDiagnostic { |
158 | workspace_root: self.workspace_root.clone(), | 149 | workspace_root: self.workspace_root.clone(), |
159 | diagnostic: msg.message, | 150 | diagnostic: msg.message, |
160 | }); | 151 | }); |
161 | } | 152 | } |
162 | 153 | ||
163 | CheckEvent::Msg(cargo_metadata::Message::BuildScriptExecuted(_)) | 154 | cargo_metadata::Message::BuildScriptExecuted(_) |
164 | | CheckEvent::Msg(cargo_metadata::Message::BuildFinished(_)) | 155 | | cargo_metadata::Message::BuildFinished(_) |
165 | | CheckEvent::Msg(cargo_metadata::Message::TextLine(_)) | 156 | | cargo_metadata::Message::TextLine(_) |
166 | | CheckEvent::Msg(cargo_metadata::Message::Unknown) => {} | 157 | | cargo_metadata::Message::Unknown => {} |
167 | }, | 158 | }, |
168 | } | 159 | } |
169 | if self.should_recheck() { | ||
170 | self.last_update_req = None; | ||
171 | self.send(Message::ClearDiagnostics); | ||
172 | self.restart_check_process(); | ||
173 | } | ||
174 | } | 160 | } |
161 | // If we rerun the thread, we need to discard the previous check results first | ||
162 | self.cancel_check_process(); | ||
175 | } | 163 | } |
176 | fn should_recheck(&mut self) -> bool { | 164 | fn cancel_check_process(&mut self) { |
177 | if let Some(_last_update_req) = &self.last_update_req { | 165 | if self.check_process.take().is_some() { |
178 | // We currently only request an update on save, as we need up to | 166 | self.send(Message::Progress(Progress::DidCancel)); |
179 | // date source on disk for cargo check to do it's magic, so we | ||
180 | // don't really need to debounce the requests at this point. | ||
181 | return true; | ||
182 | } | 167 | } |
183 | false | ||
184 | } | 168 | } |
185 | 169 | fn start_check_process(&self) -> (Receiver<cargo_metadata::Message>, jod_thread::JoinHandle) { | |
186 | fn restart_check_process(&mut self) { | ||
187 | // First, clear and cancel the old thread | ||
188 | self.check_process = None; | ||
189 | |||
190 | let mut cmd = match &self.config { | 170 | let mut cmd = match &self.config { |
191 | FlycheckConfig::CargoCommand { | 171 | FlycheckConfig::CargoCommand { |
192 | command, | 172 | command, |
@@ -223,8 +203,6 @@ impl FlycheckActor { | |||
223 | let thread = jod_thread::spawn(move || { | 203 | let thread = jod_thread::spawn(move || { |
224 | // If we trigger an error here, we will do so in the loop instead, | 204 | // If we trigger an error here, we will do so in the loop instead, |
225 | // which will break out of the loop, and continue the shutdown | 205 | // which will break out of the loop, and continue the shutdown |
226 | let _ = message_send.send(CheckEvent::Begin); | ||
227 | |||
228 | let res = run_cargo(cmd, &mut |message| { | 206 | let res = run_cargo(cmd, &mut |message| { |
229 | // Skip certain kinds of messages to only spend time on what's useful | 207 | // Skip certain kinds of messages to only spend time on what's useful |
230 | match &message { | 208 | match &message { |
@@ -237,7 +215,7 @@ impl FlycheckActor { | |||
237 | } | 215 | } |
238 | 216 | ||
239 | // if the send channel was closed, we want to shutdown | 217 | // if the send channel was closed, we want to shutdown |
240 | message_send.send(CheckEvent::Msg(message)).is_ok() | 218 | message_send.send(message).is_ok() |
241 | }); | 219 | }); |
242 | 220 | ||
243 | if let Err(err) = res { | 221 | if let Err(err) = res { |
@@ -245,12 +223,8 @@ impl FlycheckActor { | |||
245 | // to display user-caused misconfiguration errors instead of just logging them here | 223 | // to display user-caused misconfiguration errors instead of just logging them here |
246 | log::error!("Cargo watcher failed {:?}", err); | 224 | log::error!("Cargo watcher failed {:?}", err); |
247 | } | 225 | } |
248 | |||
249 | // We can ignore any error here, as we are already in the progress | ||
250 | // of shutting down. | ||
251 | let _ = message_send.send(CheckEvent::End); | ||
252 | }); | 226 | }); |
253 | self.check_process = Some((message_recv, thread)) | 227 | (message_recv, thread) |
254 | } | 228 | } |
255 | 229 | ||
256 | fn send(&self, check_task: Message) { | 230 | fn send(&self, check_task: Message) { |
@@ -258,12 +232,6 @@ impl FlycheckActor { | |||
258 | } | 232 | } |
259 | } | 233 | } |
260 | 234 | ||
261 | enum CheckEvent { | ||
262 | Begin, | ||
263 | Msg(cargo_metadata::Message), | ||
264 | End, | ||
265 | } | ||
266 | |||
267 | fn run_cargo( | 235 | fn run_cargo( |
268 | mut command: Command, | 236 | mut command: Command, |
269 | on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool, | 237 | on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool, |
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index f224c69e7..b8aa1e5b5 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -12,6 +12,7 @@ use parking_lot::RwLock; | |||
12 | use ra_db::{CrateId, VfsPath}; | 12 | use ra_db::{CrateId, VfsPath}; |
13 | use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId}; | 13 | use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId}; |
14 | use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target}; | 14 | use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target}; |
15 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
15 | 16 | ||
16 | use crate::{ | 17 | use crate::{ |
17 | config::Config, | 18 | config::Config, |
@@ -21,12 +22,10 @@ use crate::{ | |||
21 | main_loop::Task, | 22 | main_loop::Task, |
22 | reload::SourceRootConfig, | 23 | reload::SourceRootConfig, |
23 | request_metrics::{LatestRequests, RequestMetrics}, | 24 | request_metrics::{LatestRequests, RequestMetrics}, |
24 | show_message, | ||
25 | thread_pool::TaskPool, | 25 | thread_pool::TaskPool, |
26 | to_proto::url_from_abs_path, | 26 | to_proto::url_from_abs_path, |
27 | Result, | 27 | Result, |
28 | }; | 28 | }; |
29 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
30 | 29 | ||
31 | #[derive(Eq, PartialEq)] | 30 | #[derive(Eq, PartialEq)] |
32 | pub(crate) enum Status { | 31 | pub(crate) enum Status { |
@@ -58,6 +57,7 @@ pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>; | |||
58 | /// Note that this struct has more than on impl in various modules! | 57 | /// Note that this struct has more than on impl in various modules! |
59 | pub(crate) struct GlobalState { | 58 | pub(crate) struct GlobalState { |
60 | sender: Sender<lsp_server::Message>, | 59 | sender: Sender<lsp_server::Message>, |
60 | req_queue: ReqQueue, | ||
61 | pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>, | 61 | pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>, |
62 | pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>, | 62 | pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>, |
63 | pub(crate) flycheck: Option<Handle<FlycheckHandle, Receiver<flycheck::Message>>>, | 63 | pub(crate) flycheck: Option<Handle<FlycheckHandle, Receiver<flycheck::Message>>>, |
@@ -67,7 +67,6 @@ pub(crate) struct GlobalState { | |||
67 | pub(crate) mem_docs: FxHashSet<VfsPath>, | 67 | pub(crate) mem_docs: FxHashSet<VfsPath>, |
68 | pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, | 68 | pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, |
69 | pub(crate) status: Status, | 69 | pub(crate) status: Status, |
70 | pub(crate) req_queue: ReqQueue, | ||
71 | pub(crate) source_root_config: SourceRootConfig, | 70 | pub(crate) source_root_config: SourceRootConfig, |
72 | pub(crate) proc_macro_client: ProcMacroClient, | 71 | pub(crate) proc_macro_client: ProcMacroClient, |
73 | pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, | 72 | pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, |
@@ -103,16 +102,16 @@ impl GlobalState { | |||
103 | let analysis_host = AnalysisHost::new(config.lru_capacity); | 102 | let analysis_host = AnalysisHost::new(config.lru_capacity); |
104 | GlobalState { | 103 | GlobalState { |
105 | sender, | 104 | sender, |
105 | req_queue: ReqQueue::default(), | ||
106 | task_pool, | 106 | task_pool, |
107 | loader, | 107 | loader, |
108 | flycheck: None, | ||
108 | config, | 109 | config, |
109 | analysis_host, | 110 | analysis_host, |
110 | flycheck: None, | ||
111 | diagnostics: Default::default(), | 111 | diagnostics: Default::default(), |
112 | mem_docs: FxHashSet::default(), | 112 | mem_docs: FxHashSet::default(), |
113 | vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))), | 113 | vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))), |
114 | status: Status::default(), | 114 | status: Status::default(), |
115 | req_queue: ReqQueue::default(), | ||
116 | source_root_config: SourceRootConfig::default(), | 115 | source_root_config: SourceRootConfig::default(), |
117 | proc_macro_client: ProcMacroClient::dummy(), | 116 | proc_macro_client: ProcMacroClient::dummy(), |
118 | workspaces: Arc::new(Vec::new()), | 117 | workspaces: Arc::new(Vec::new()), |
@@ -169,8 +168,35 @@ impl GlobalState { | |||
169 | } | 168 | } |
170 | } | 169 | } |
171 | 170 | ||
172 | pub(crate) fn send(&mut self, message: lsp_server::Message) { | 171 | pub(crate) fn send_request<R: lsp_types::request::Request>( |
173 | self.sender.send(message).unwrap() | 172 | &mut self, |
173 | params: R::Params, | ||
174 | handler: ReqHandler, | ||
175 | ) { | ||
176 | let request = self.req_queue.outgoing.register(R::METHOD.to_string(), params, handler); | ||
177 | self.send(request.into()); | ||
178 | } | ||
179 | pub(crate) fn complete_request(&mut self, response: lsp_server::Response) { | ||
180 | let handler = self.req_queue.outgoing.complete(response.id.clone()); | ||
181 | handler(self, response) | ||
182 | } | ||
183 | |||
184 | pub(crate) fn send_notification<N: lsp_types::notification::Notification>( | ||
185 | &mut self, | ||
186 | params: N::Params, | ||
187 | ) { | ||
188 | let not = lsp_server::Notification::new(N::METHOD.to_string(), params); | ||
189 | self.send(not.into()); | ||
190 | } | ||
191 | |||
192 | pub(crate) fn register_request( | ||
193 | &mut self, | ||
194 | request: &lsp_server::Request, | ||
195 | request_received: Instant, | ||
196 | ) { | ||
197 | self.req_queue | ||
198 | .incoming | ||
199 | .register(request.id.clone(), (request.method.clone(), request_received)); | ||
174 | } | 200 | } |
175 | pub(crate) fn respond(&mut self, response: lsp_server::Response) { | 201 | pub(crate) fn respond(&mut self, response: lsp_server::Response) { |
176 | if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) { | 202 | if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) { |
@@ -182,8 +208,14 @@ impl GlobalState { | |||
182 | self.send(response.into()); | 208 | self.send(response.into()); |
183 | } | 209 | } |
184 | } | 210 | } |
185 | pub(crate) fn show_message(&self, typ: lsp_types::MessageType, message: String) { | 211 | pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) { |
186 | show_message(typ, message, &self.sender) | 212 | if let Some(response) = self.req_queue.incoming.cancel(request_id) { |
213 | self.send(response.into()); | ||
214 | } | ||
215 | } | ||
216 | |||
217 | fn send(&mut self, message: lsp_server::Message) { | ||
218 | self.sender.send(message).unwrap() | ||
187 | } | 219 | } |
188 | } | 220 | } |
189 | 221 | ||
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index a24dfe58c..407944d85 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs | |||
@@ -39,7 +39,7 @@ pub mod config; | |||
39 | use serde::de::DeserializeOwned; | 39 | use serde::de::DeserializeOwned; |
40 | 40 | ||
41 | pub type Result<T, E = Box<dyn std::error::Error + Send + Sync>> = std::result::Result<T, E>; | 41 | pub type Result<T, E = Box<dyn std::error::Error + Send + Sync>> = std::result::Result<T, E>; |
42 | pub use crate::{caps::server_capabilities, lsp_utils::show_message, main_loop::main_loop}; | 42 | pub use crate::{caps::server_capabilities, main_loop::main_loop}; |
43 | use std::fmt; | 43 | use std::fmt; |
44 | 44 | ||
45 | pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> { | 45 | pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> { |
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs index 35917030c..0bc3ff115 100644 --- a/crates/rust-analyzer/src/lsp_utils.rs +++ b/crates/rust-analyzer/src/lsp_utils.rs | |||
@@ -1,24 +1,11 @@ | |||
1 | //! Utilities for LSP-related boilerplate code. | 1 | //! Utilities for LSP-related boilerplate code. |
2 | use std::{error::Error, ops::Range}; | 2 | use std::{error::Error, ops::Range}; |
3 | 3 | ||
4 | use crossbeam_channel::Sender; | 4 | use lsp_server::Notification; |
5 | use lsp_server::{Message, Notification}; | ||
6 | use ra_db::Canceled; | 5 | use ra_db::Canceled; |
7 | use ra_ide::LineIndex; | 6 | use ra_ide::LineIndex; |
8 | use serde::Serialize; | ||
9 | 7 | ||
10 | use crate::from_proto; | 8 | use crate::{from_proto, global_state::GlobalState}; |
11 | |||
12 | pub fn show_message( | ||
13 | typ: lsp_types::MessageType, | ||
14 | message: impl Into<String>, | ||
15 | sender: &Sender<Message>, | ||
16 | ) { | ||
17 | let message = message.into(); | ||
18 | let params = lsp_types::ShowMessageParams { typ, message }; | ||
19 | let not = notification_new::<lsp_types::notification::ShowMessage>(params); | ||
20 | sender.send(not.into()).unwrap(); | ||
21 | } | ||
22 | 9 | ||
23 | pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool { | 10 | pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool { |
24 | e.downcast_ref::<Canceled>().is_some() | 11 | e.downcast_ref::<Canceled>().is_some() |
@@ -30,12 +17,68 @@ pub(crate) fn notification_is<N: lsp_types::notification::Notification>( | |||
30 | notification.method == N::METHOD | 17 | notification.method == N::METHOD |
31 | } | 18 | } |
32 | 19 | ||
33 | pub(crate) fn notification_new<N>(params: N::Params) -> Notification | 20 | #[derive(Debug, Eq, PartialEq)] |
34 | where | 21 | pub(crate) enum Progress { |
35 | N: lsp_types::notification::Notification, | 22 | Begin, |
36 | N::Params: Serialize, | 23 | Report, |
37 | { | 24 | End, |
38 | Notification::new(N::METHOD.to_string(), params) | 25 | } |
26 | |||
27 | impl Progress { | ||
28 | pub(crate) fn percentage(done: usize, total: usize) -> f64 { | ||
29 | (done as f64 / total.max(1) as f64) * 100.0 | ||
30 | } | ||
31 | } | ||
32 | |||
33 | impl GlobalState { | ||
34 | pub(crate) fn show_message(&mut self, typ: lsp_types::MessageType, message: String) { | ||
35 | let message = message.into(); | ||
36 | self.send_notification::<lsp_types::notification::ShowMessage>( | ||
37 | lsp_types::ShowMessageParams { typ, message }, | ||
38 | ) | ||
39 | } | ||
40 | |||
41 | pub(crate) fn report_progress( | ||
42 | &mut self, | ||
43 | title: &str, | ||
44 | state: Progress, | ||
45 | message: Option<String>, | ||
46 | percentage: Option<f64>, | ||
47 | ) { | ||
48 | if !self.config.client_caps.work_done_progress { | ||
49 | return; | ||
50 | } | ||
51 | let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title)); | ||
52 | let work_done_progress = match state { | ||
53 | Progress::Begin => { | ||
54 | self.send_request::<lsp_types::request::WorkDoneProgressCreate>( | ||
55 | lsp_types::WorkDoneProgressCreateParams { token: token.clone() }, | ||
56 | |_, _| (), | ||
57 | ); | ||
58 | |||
59 | lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { | ||
60 | title: title.into(), | ||
61 | cancellable: None, | ||
62 | message, | ||
63 | percentage, | ||
64 | }) | ||
65 | } | ||
66 | Progress::Report => { | ||
67 | lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport { | ||
68 | cancellable: None, | ||
69 | message, | ||
70 | percentage, | ||
71 | }) | ||
72 | } | ||
73 | Progress::End => { | ||
74 | lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message }) | ||
75 | } | ||
76 | }; | ||
77 | self.send_notification::<lsp_types::notification::Progress>(lsp_types::ProgressParams { | ||
78 | token, | ||
79 | value: lsp_types::ProgressParamsValue::WorkDone(work_done_progress), | ||
80 | }); | ||
81 | } | ||
39 | } | 82 | } |
40 | 83 | ||
41 | pub(crate) fn apply_document_changes( | 84 | pub(crate) fn apply_document_changes( |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 8fc816cbd..e5194fe41 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -7,7 +7,7 @@ use std::{ | |||
7 | 7 | ||
8 | use crossbeam_channel::{never, select, Receiver}; | 8 | use crossbeam_channel::{never, select, Receiver}; |
9 | use lsp_server::{Connection, Notification, Request, Response}; | 9 | use lsp_server::{Connection, Notification, Request, Response}; |
10 | use lsp_types::{notification::Notification as _, request::Request as _}; | 10 | use lsp_types::notification::Notification as _; |
11 | use ra_db::VfsPath; | 11 | use ra_db::VfsPath; |
12 | use ra_ide::{Canceled, FileId}; | 12 | use ra_ide::{Canceled, FileId}; |
13 | use ra_prof::profile; | 13 | use ra_prof::profile; |
@@ -18,7 +18,7 @@ use crate::{ | |||
18 | from_proto, | 18 | from_proto, |
19 | global_state::{file_id_to_url, url_to_file_id, GlobalState, Status}, | 19 | global_state::{file_id_to_url, url_to_file_id, GlobalState, Status}, |
20 | handlers, lsp_ext, | 20 | handlers, lsp_ext, |
21 | lsp_utils::{apply_document_changes, is_canceled, notification_is, notification_new}, | 21 | lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress}, |
22 | Result, | 22 | Result, |
23 | }; | 23 | }; |
24 | 24 | ||
@@ -143,10 +143,7 @@ impl GlobalState { | |||
143 | lsp_server::Message::Notification(not) => { | 143 | lsp_server::Message::Notification(not) => { |
144 | self.on_notification(not)?; | 144 | self.on_notification(not)?; |
145 | } | 145 | } |
146 | lsp_server::Message::Response(resp) => { | 146 | lsp_server::Message::Response(resp) => self.complete_request(resp), |
147 | let handler = self.req_queue.outgoing.complete(resp.id.clone()); | ||
148 | handler(self, resp) | ||
149 | } | ||
150 | }, | 147 | }, |
151 | Event::Task(task) => { | 148 | Event::Task(task) => { |
152 | match task { | 149 | match task { |
@@ -181,18 +178,15 @@ impl GlobalState { | |||
181 | became_ready = true; | 178 | became_ready = true; |
182 | Progress::End | 179 | Progress::End |
183 | }; | 180 | }; |
184 | report_progress( | 181 | self.report_progress( |
185 | self, | ||
186 | "roots scanned", | 182 | "roots scanned", |
187 | state, | 183 | state, |
188 | Some(format!("{}/{}", n_done, n_total)), | 184 | Some(format!("{}/{}", n_done, n_total)), |
189 | Some(percentage(n_done, n_total)), | 185 | Some(Progress::percentage(n_done, n_total)), |
190 | ) | 186 | ) |
191 | } | 187 | } |
192 | }, | 188 | }, |
193 | Event::Flycheck(task) => match task { | 189 | Event::Flycheck(task) => match task { |
194 | flycheck::Message::ClearDiagnostics => self.diagnostics.clear_check(), | ||
195 | |||
196 | flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => { | 190 | flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => { |
197 | let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( | 191 | let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( |
198 | &self.config.diagnostics, | 192 | &self.config.diagnostics, |
@@ -215,14 +209,19 @@ impl GlobalState { | |||
215 | 209 | ||
216 | flycheck::Message::Progress(status) => { | 210 | flycheck::Message::Progress(status) => { |
217 | let (state, message) = match status { | 211 | let (state, message) = match status { |
218 | flycheck::Progress::Being => (Progress::Begin, None), | 212 | flycheck::Progress::DidStart => { |
213 | self.diagnostics.clear_check(); | ||
214 | (Progress::Begin, None) | ||
215 | } | ||
219 | flycheck::Progress::DidCheckCrate(target) => { | 216 | flycheck::Progress::DidCheckCrate(target) => { |
220 | (Progress::Report, Some(target)) | 217 | (Progress::Report, Some(target)) |
221 | } | 218 | } |
222 | flycheck::Progress::End => (Progress::End, None), | 219 | flycheck::Progress::DidFinish | flycheck::Progress::DidCancel => { |
220 | (Progress::End, None) | ||
221 | } | ||
223 | }; | 222 | }; |
224 | 223 | ||
225 | report_progress(self, "cargo check", state, message, None); | 224 | self.report_progress("cargo check", state, message, None); |
226 | } | 225 | } |
227 | }, | 226 | }, |
228 | } | 227 | } |
@@ -248,10 +247,9 @@ impl GlobalState { | |||
248 | for file_id in diagnostic_changes { | 247 | for file_id in diagnostic_changes { |
249 | let url = file_id_to_url(&self.vfs.read().0, file_id); | 248 | let url = file_id_to_url(&self.vfs.read().0, file_id); |
250 | let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect(); | 249 | let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect(); |
251 | let params = | 250 | self.send_notification::<lsp_types::notification::PublishDiagnostics>( |
252 | lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version: None }; | 251 | lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version: None }, |
253 | let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params); | 252 | ); |
254 | self.send(not.into()); | ||
255 | } | 253 | } |
256 | } | 254 | } |
257 | 255 | ||
@@ -269,7 +267,7 @@ impl GlobalState { | |||
269 | } | 267 | } |
270 | 268 | ||
271 | fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> { | 269 | fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> { |
272 | self.req_queue.incoming.register(req.id.clone(), (req.method.clone(), request_received)); | 270 | self.register_request(&req, request_received); |
273 | 271 | ||
274 | RequestDispatcher { req: Some(req), global_state: self } | 272 | RequestDispatcher { req: Some(req), global_state: self } |
275 | .on_sync::<lsp_ext::CollectGarbage>(|s, ()| Ok(s.analysis_host.collect_garbage()))? | 273 | .on_sync::<lsp_ext::CollectGarbage>(|s, ()| Ok(s.analysis_host.collect_garbage()))? |
@@ -333,9 +331,7 @@ impl GlobalState { | |||
333 | lsp_types::NumberOrString::Number(id) => id.into(), | 331 | lsp_types::NumberOrString::Number(id) => id.into(), |
334 | lsp_types::NumberOrString::String(id) => id.into(), | 332 | lsp_types::NumberOrString::String(id) => id.into(), |
335 | }; | 333 | }; |
336 | if let Some(response) = this.req_queue.incoming.cancel(id) { | 334 | this.cancel(id); |
337 | this.send(response.into()); | ||
338 | } | ||
339 | Ok(()) | 335 | Ok(()) |
340 | })? | 336 | })? |
341 | .on::<lsp_types::notification::DidOpenTextDocument>(|this, params| { | 337 | .on::<lsp_types::notification::DidOpenTextDocument>(|this, params| { |
@@ -370,13 +366,13 @@ impl GlobalState { | |||
370 | this.loader.handle.invalidate(path.to_path_buf()); | 366 | this.loader.handle.invalidate(path.to_path_buf()); |
371 | } | 367 | } |
372 | } | 368 | } |
373 | let params = lsp_types::PublishDiagnosticsParams { | 369 | this.send_notification::<lsp_types::notification::PublishDiagnostics>( |
374 | uri: params.text_document.uri, | 370 | lsp_types::PublishDiagnosticsParams { |
375 | diagnostics: Vec::new(), | 371 | uri: params.text_document.uri, |
376 | version: None, | 372 | diagnostics: Vec::new(), |
377 | }; | 373 | version: None, |
378 | let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params); | 374 | }, |
379 | this.send(not.into()); | 375 | ); |
380 | Ok(()) | 376 | Ok(()) |
381 | })? | 377 | })? |
382 | .on::<lsp_types::notification::DidSaveTextDocument>(|this, _params| { | 378 | .on::<lsp_types::notification::DidSaveTextDocument>(|this, _params| { |
@@ -388,8 +384,7 @@ impl GlobalState { | |||
388 | .on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| { | 384 | .on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| { |
389 | // As stated in https://github.com/microsoft/language-server-protocol/issues/676, | 385 | // As stated in https://github.com/microsoft/language-server-protocol/issues/676, |
390 | // this notification's parameters should be ignored and the actual config queried separately. | 386 | // this notification's parameters should be ignored and the actual config queried separately. |
391 | let request = this.req_queue.outgoing.register( | 387 | this.send_request::<lsp_types::request::WorkspaceConfiguration>( |
392 | lsp_types::request::WorkspaceConfiguration::METHOD.to_string(), | ||
393 | lsp_types::ConfigurationParams { | 388 | lsp_types::ConfigurationParams { |
394 | items: vec![lsp_types::ConfigurationItem { | 389 | items: vec![lsp_types::ConfigurationItem { |
395 | scope_uri: None, | 390 | scope_uri: None, |
@@ -417,7 +412,6 @@ impl GlobalState { | |||
417 | } | 412 | } |
418 | }, | 413 | }, |
419 | ); | 414 | ); |
420 | this.send(request.into()); | ||
421 | 415 | ||
422 | return Ok(()); | 416 | return Ok(()); |
423 | })? | 417 | })? |
@@ -465,60 +459,3 @@ impl GlobalState { | |||
465 | }); | 459 | }); |
466 | } | 460 | } |
467 | } | 461 | } |
468 | |||
469 | #[derive(Eq, PartialEq)] | ||
470 | enum Progress { | ||
471 | Begin, | ||
472 | Report, | ||
473 | End, | ||
474 | } | ||
475 | |||
476 | fn percentage(done: usize, total: usize) -> f64 { | ||
477 | (done as f64 / total.max(1) as f64) * 100.0 | ||
478 | } | ||
479 | |||
480 | fn report_progress( | ||
481 | global_state: &mut GlobalState, | ||
482 | title: &str, | ||
483 | state: Progress, | ||
484 | message: Option<String>, | ||
485 | percentage: Option<f64>, | ||
486 | ) { | ||
487 | if !global_state.config.client_caps.work_done_progress { | ||
488 | return; | ||
489 | } | ||
490 | let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title)); | ||
491 | let work_done_progress = match state { | ||
492 | Progress::Begin => { | ||
493 | let work_done_progress_create = global_state.req_queue.outgoing.register( | ||
494 | lsp_types::request::WorkDoneProgressCreate::METHOD.to_string(), | ||
495 | lsp_types::WorkDoneProgressCreateParams { token: token.clone() }, | ||
496 | |_, _| (), | ||
497 | ); | ||
498 | global_state.send(work_done_progress_create.into()); | ||
499 | |||
500 | lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { | ||
501 | title: title.into(), | ||
502 | cancellable: None, | ||
503 | message, | ||
504 | percentage, | ||
505 | }) | ||
506 | } | ||
507 | Progress::Report => { | ||
508 | lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport { | ||
509 | cancellable: None, | ||
510 | message, | ||
511 | percentage, | ||
512 | }) | ||
513 | } | ||
514 | Progress::End => { | ||
515 | lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message }) | ||
516 | } | ||
517 | }; | ||
518 | let notification = | ||
519 | notification_new::<lsp_types::notification::Progress>(lsp_types::ProgressParams { | ||
520 | token, | ||
521 | value: lsp_types::ProgressParamsValue::WorkDone(work_done_progress), | ||
522 | }); | ||
523 | global_state.send(notification.into()); | ||
524 | } | ||
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index a22d3e262..ec71f8b29 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs | |||
@@ -1,9 +1,8 @@ | |||
1 | //! Project loading & configuration updates | 1 | //! Project loading & configuration updates |
2 | use std::sync::Arc; | 2 | use std::{mem, sync::Arc}; |
3 | 3 | ||
4 | use crossbeam_channel::unbounded; | 4 | use crossbeam_channel::unbounded; |
5 | use flycheck::FlycheckHandle; | 5 | use flycheck::FlycheckHandle; |
6 | use lsp_types::request::Request; | ||
7 | use ra_db::{CrateGraph, SourceRoot, VfsPath}; | 6 | use ra_db::{CrateGraph, SourceRoot, VfsPath}; |
8 | use ra_ide::AnalysisChange; | 7 | use ra_ide::AnalysisChange; |
9 | use ra_project_model::{PackageRoot, ProcMacroClient, ProjectWorkspace}; | 8 | use ra_project_model::{PackageRoot, ProcMacroClient, ProjectWorkspace}; |
@@ -15,12 +14,14 @@ use crate::{ | |||
15 | }; | 14 | }; |
16 | 15 | ||
17 | impl GlobalState { | 16 | impl GlobalState { |
18 | pub(crate) fn update_configuration(&mut self, new_config: Config) { | 17 | pub(crate) fn update_configuration(&mut self, config: Config) { |
19 | self.analysis_host.update_lru_capacity(new_config.lru_capacity); | 18 | let old_config = mem::replace(&mut self.config, config); |
20 | if new_config.flycheck != self.config.flycheck { | 19 | if self.config.lru_capacity != old_config.lru_capacity { |
20 | self.analysis_host.update_lru_capacity(old_config.lru_capacity); | ||
21 | } | ||
22 | if self.config.flycheck != old_config.flycheck { | ||
21 | self.reload_flycheck(); | 23 | self.reload_flycheck(); |
22 | } | 24 | } |
23 | self.config = new_config; | ||
24 | } | 25 | } |
25 | pub(crate) fn reload(&mut self) { | 26 | pub(crate) fn reload(&mut self) { |
26 | let workspaces = { | 27 | let workspaces = { |
@@ -36,27 +37,31 @@ impl GlobalState { | |||
36 | self.config | 37 | self.config |
37 | .linked_projects | 38 | .linked_projects |
38 | .iter() | 39 | .iter() |
39 | .filter_map(|project| match project { | 40 | .map(|project| match project { |
40 | LinkedProject::ProjectManifest(manifest) => { | 41 | LinkedProject::ProjectManifest(manifest) => { |
41 | ra_project_model::ProjectWorkspace::load( | 42 | ra_project_model::ProjectWorkspace::load( |
42 | manifest.clone(), | 43 | manifest.clone(), |
43 | &self.config.cargo, | 44 | &self.config.cargo, |
44 | self.config.with_sysroot, | 45 | self.config.with_sysroot, |
45 | ) | 46 | ) |
46 | .map_err(|err| { | ||
47 | log::error!("failed to load workspace: {:#}", err); | ||
48 | self.show_message( | ||
49 | lsp_types::MessageType::Error, | ||
50 | format!("rust-analyzer failed to load workspace: {:#}", err), | ||
51 | ); | ||
52 | }) | ||
53 | .ok() | ||
54 | } | 47 | } |
55 | LinkedProject::InlineJsonProject(it) => { | 48 | LinkedProject::InlineJsonProject(it) => { |
56 | Some(ra_project_model::ProjectWorkspace::Json { project: it.clone() }) | 49 | Ok(ra_project_model::ProjectWorkspace::Json { project: it.clone() }) |
57 | } | 50 | } |
58 | }) | 51 | }) |
59 | .collect::<Vec<_>>() | 52 | .collect::<Vec<_>>() |
53 | .into_iter() | ||
54 | .filter_map(|res| { | ||
55 | res.map_err(|err| { | ||
56 | log::error!("failed to load workspace: {:#}", err); | ||
57 | self.show_message( | ||
58 | lsp_types::MessageType::Error, | ||
59 | format!("rust-analyzer failed to load workspace: {:#}", err), | ||
60 | ); | ||
61 | }) | ||
62 | .ok() | ||
63 | }) | ||
64 | .collect::<Vec<_>>() | ||
60 | }; | 65 | }; |
61 | 66 | ||
62 | if let FilesWatcher::Client = self.config.files.watcher { | 67 | if let FilesWatcher::Client = self.config.files.watcher { |
@@ -74,13 +79,10 @@ impl GlobalState { | |||
74 | method: "workspace/didChangeWatchedFiles".to_string(), | 79 | method: "workspace/didChangeWatchedFiles".to_string(), |
75 | register_options: Some(serde_json::to_value(registration_options).unwrap()), | 80 | register_options: Some(serde_json::to_value(registration_options).unwrap()), |
76 | }; | 81 | }; |
77 | let params = lsp_types::RegistrationParams { registrations: vec![registration] }; | 82 | self.send_request::<lsp_types::request::RegisterCapability>( |
78 | let request = self.req_queue.outgoing.register( | 83 | lsp_types::RegistrationParams { registrations: vec![registration] }, |
79 | lsp_types::request::RegisterCapability::METHOD.to_string(), | ||
80 | params, | ||
81 | |_, _| (), | 84 | |_, _| (), |
82 | ); | 85 | ); |
83 | self.send(request.into()); | ||
84 | } | 86 | } |
85 | 87 | ||
86 | let mut change = AnalysisChange::new(); | 88 | let mut change = AnalysisChange::new(); |