aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/rust-analyzer/src/main_loop.rs326
1 files changed, 166 insertions, 160 deletions
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index c2f43df1d..94b9c0ca2 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -126,6 +126,45 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
126 Ok(()) 126 Ok(())
127} 127}
128 128
129enum Event {
130 Lsp(lsp_server::Message),
131 Task(Task),
132 Vfs(vfs::loader::Message),
133 Flycheck(flycheck::Message),
134}
135
136impl fmt::Debug for Event {
137 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
138 let debug_verbose_not = |not: &Notification, f: &mut fmt::Formatter| {
139 f.debug_struct("Notification").field("method", &not.method).finish()
140 };
141
142 match self {
143 Event::Lsp(lsp_server::Message::Notification(not)) => {
144 if notification_is::<lsp_types::notification::DidOpenTextDocument>(not)
145 || notification_is::<lsp_types::notification::DidChangeTextDocument>(not)
146 {
147 return debug_verbose_not(not, f);
148 }
149 }
150 Event::Task(Task::Respond(resp)) => {
151 return f
152 .debug_struct("Response")
153 .field("id", &resp.id)
154 .field("error", &resp.error)
155 .finish();
156 }
157 _ => (),
158 }
159 match self {
160 Event::Lsp(it) => fmt::Debug::fmt(it, f),
161 Event::Task(it) => fmt::Debug::fmt(it, f),
162 Event::Vfs(it) => fmt::Debug::fmt(it, f),
163 Event::Flycheck(it) => fmt::Debug::fmt(it, f),
164 }
165 }
166}
167
129impl GlobalState { 168impl GlobalState {
130 fn next_event(&self, inbox: &Receiver<lsp_server::Message>) -> Option<Event> { 169 fn next_event(&self, inbox: &Receiver<lsp_server::Message>) -> Option<Event> {
131 select! { 170 select! {
@@ -145,97 +184,150 @@ impl GlobalState {
145 184
146 fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> { 185 fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> {
147 while let Some(event) = self.next_event(&inbox) { 186 while let Some(event) = self.next_event(&inbox) {
148 let loop_start = Instant::now(); 187 if let Event::Lsp(lsp_server::Message::Notification(not)) = &event {
149 // NOTE: don't count blocking select! call as a loop-turn time 188 if not.method == lsp_types::notification::Exit::METHOD {
150 let _p = profile("main_loop_inner/loop-turn"); 189 return Ok(());
151 190 }
152 log::info!("loop turn = {:?}", event);
153 let queue_count = self.task_pool.0.len();
154 if queue_count > 0 {
155 log::info!("queued count = {}", queue_count);
156 } 191 }
192 self.loop_turn(event)?
193 }
194 Err("client exited without proper shutdown sequence")?
195 }
157 196
158 let mut became_ready = false; 197 fn loop_turn(&mut self, event: Event) -> Result<()> {
159 match event { 198 let loop_start = Instant::now();
160 Event::Lsp(msg) => match msg { 199 // NOTE: don't count blocking select! call as a loop-turn time
161 lsp_server::Message::Request(req) => self.on_request(loop_start, req)?, 200 let _p = profile("main_loop_inner/loop-turn");
162 lsp_server::Message::Notification(not) => { 201
163 if not.method == lsp_types::notification::Exit::METHOD { 202 log::info!("loop turn = {:?}", event);
164 return Ok(()); 203 let queue_count = self.task_pool.0.len();
204 if queue_count > 0 {
205 log::info!("queued count = {}", queue_count);
206 }
207
208 let mut became_ready = false;
209 match event {
210 Event::Lsp(msg) => match msg {
211 lsp_server::Message::Request(req) => self.on_request(loop_start, req)?,
212 lsp_server::Message::Notification(not) => {
213 self.on_notification(not)?;
214 }
215 lsp_server::Message::Response(resp) => {
216 let handler = self.req_queue.outgoing.complete(resp.id.clone());
217 handler(self, resp)
218 }
219 },
220 Event::Task(task) => {
221 self.on_task(task);
222 self.maybe_collect_garbage();
223 }
224 Event::Vfs(task) => match task {
225 vfs::loader::Message::Loaded { files } => {
226 let vfs = &mut self.vfs.write().0;
227 for (path, contents) in files {
228 let path = VfsPath::from(path);
229 if !self.mem_docs.contains(&path) {
230 vfs.set_file_contents(path, contents)
165 } 231 }
166 self.on_notification(not)?;
167 } 232 }
168 lsp_server::Message::Response(resp) => {
169 let handler = self.req_queue.outgoing.complete(resp.id.clone());
170 handler(&mut self, resp)
171 }
172 },
173 Event::Task(task) => {
174 self.on_task(task);
175 self.maybe_collect_garbage();
176 } 233 }
177 Event::Vfs(task) => match task { 234 vfs::loader::Message::Progress { n_total, n_done } => {
178 vfs::loader::Message::Loaded { files } => { 235 let state = if n_done == 0 {
179 let vfs = &mut self.vfs.write().0; 236 Progress::Begin
180 for (path, contents) in files { 237 } else if n_done < n_total {
181 let path = VfsPath::from(path); 238 Progress::Report
182 if !self.mem_docs.contains(&path) { 239 } else {
183 vfs.set_file_contents(path, contents) 240 assert_eq!(n_done, n_total);
241 self.status = Status::Ready;
242 became_ready = true;
243 Progress::End
244 };
245 report_progress(
246 self,
247 "roots scanned",
248 state,
249 Some(format!("{}/{}", n_done, n_total)),
250 Some(percentage(n_done, n_total)),
251 )
252 }
253 },
254 Event::Flycheck(task) => match task {
255 flycheck::Message::ClearDiagnostics => {
256 on_diagnostic_task(DiagnosticTask::ClearCheck, self)
257 }
258
259 flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => {
260 let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
261 &self.config.diagnostics,
262 &diagnostic,
263 &workspace_root,
264 );
265 for diag in diagnostics {
266 let path = from_proto::vfs_path(&diag.location.uri)?;
267 let file_id = match self.vfs.read().0.file_id(&path) {
268 Some(file) => FileId(file.0),
269 None => {
270 log::error!(
271 "File with cargo diagnostic not found in VFS: {}",
272 path
273 );
274 return Ok(());
184 } 275 }
185 }
186 }
187 vfs::loader::Message::Progress { n_total, n_done } => {
188 let state = if n_done == 0 {
189 Progress::Begin
190 } else if n_done < n_total {
191 Progress::Report
192 } else {
193 assert_eq!(n_done, n_total);
194 self.status = Status::Ready;
195 became_ready = true;
196 Progress::End
197 }; 276 };
198 report_progress( 277
199 &mut self, 278 on_diagnostic_task(
200 "roots scanned", 279 DiagnosticTask::AddCheck(
201 state, 280 file_id,
202 Some(format!("{}/{}", n_done, n_total)), 281 diag.diagnostic,
203 Some(percentage(n_done, n_total)), 282 diag.fixes.into_iter().map(|it| it.into()).collect(),
283 ),
284 self,
204 ) 285 )
205 } 286 }
206 }, 287 }
207 Event::Flycheck(task) => on_check_task(task, &mut self)?,
208 }
209 288
210 let state_changed = self.process_changes(); 289 flycheck::Message::Progress(status) => {
211 if became_ready { 290 let (state, message) = match status {
212 if let Some(flycheck) = &self.flycheck { 291 flycheck::Progress::Being => (Progress::Begin, None),
213 flycheck.0.update(); 292 flycheck::Progress::DidCheckCrate(target) => {
293 (Progress::Report, Some(target))
294 }
295 flycheck::Progress::End => (Progress::End, None),
296 };
297
298 report_progress(self, "cargo check", state, message, None);
214 } 299 }
300 },
301 }
302
303 let state_changed = self.process_changes();
304 if became_ready {
305 if let Some(flycheck) = &self.flycheck {
306 flycheck.0.update();
215 } 307 }
308 }
216 309
217 if self.status == Status::Ready && (state_changed || became_ready) { 310 if self.status == Status::Ready && (state_changed || became_ready) {
218 let subscriptions = self 311 let subscriptions = self
219 .mem_docs 312 .mem_docs
220 .iter() 313 .iter()
221 .map(|path| self.vfs.read().0.file_id(&path).unwrap()) 314 .map(|path| self.vfs.read().0.file_id(&path).unwrap())
222 .collect::<Vec<_>>(); 315 .collect::<Vec<_>>();
223 316
224 self.update_file_notifications_on_threadpool(subscriptions); 317 self.update_file_notifications_on_threadpool(subscriptions);
225 } 318 }
226 319
227 let loop_duration = loop_start.elapsed(); 320 let loop_duration = loop_start.elapsed();
228 if loop_duration > Duration::from_millis(100) { 321 if loop_duration > Duration::from_millis(100) {
229 log::error!("overly long loop turn: {:?}", loop_duration); 322 log::error!("overly long loop turn: {:?}", loop_duration);
230 if env::var("RA_PROFILE").is_ok() { 323 if env::var("RA_PROFILE").is_ok() {
231 self.show_message( 324 self.show_message(
232 lsp_types::MessageType::Error, 325 lsp_types::MessageType::Error,
233 format!("overly long loop turn: {:?}", loop_duration), 326 format!("overly long loop turn: {:?}", loop_duration),
234 ) 327 )
235 }
236 } 328 }
237 } 329 }
238 Err("client exited without proper shutdown sequence")? 330 Ok(())
239 } 331 }
240 332
241 fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> { 333 fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> {
@@ -461,96 +553,10 @@ pub(crate) enum Task {
461 Unit, 553 Unit,
462} 554}
463 555
464enum Event {
465 Lsp(lsp_server::Message),
466 Task(Task),
467 Vfs(vfs::loader::Message),
468 Flycheck(flycheck::Message),
469}
470
471impl fmt::Debug for Event {
472 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
473 let debug_verbose_not = |not: &Notification, f: &mut fmt::Formatter| {
474 f.debug_struct("Notification").field("method", &not.method).finish()
475 };
476
477 match self {
478 Event::Lsp(lsp_server::Message::Notification(not)) => {
479 if notification_is::<lsp_types::notification::DidOpenTextDocument>(not)
480 || notification_is::<lsp_types::notification::DidChangeTextDocument>(not)
481 {
482 return debug_verbose_not(not, f);
483 }
484 }
485 Event::Task(Task::Respond(resp)) => {
486 return f
487 .debug_struct("Response")
488 .field("id", &resp.id)
489 .field("error", &resp.error)
490 .finish();
491 }
492 _ => (),
493 }
494 match self {
495 Event::Lsp(it) => fmt::Debug::fmt(it, f),
496 Event::Task(it) => fmt::Debug::fmt(it, f),
497 Event::Vfs(it) => fmt::Debug::fmt(it, f),
498 Event::Flycheck(it) => fmt::Debug::fmt(it, f),
499 }
500 }
501}
502
503pub(crate) type ReqHandler = fn(&mut GlobalState, Response); 556pub(crate) type ReqHandler = fn(&mut GlobalState, Response);
504pub(crate) type ReqQueue = lsp_server::ReqQueue<(&'static str, Instant), ReqHandler>; 557pub(crate) type ReqQueue = lsp_server::ReqQueue<(&'static str, Instant), ReqHandler>;
505const DO_NOTHING: ReqHandler = |_, _| (); 558const DO_NOTHING: ReqHandler = |_, _| ();
506 559
507fn on_check_task(task: flycheck::Message, global_state: &mut GlobalState) -> Result<()> {
508 match task {
509 flycheck::Message::ClearDiagnostics => {
510 on_diagnostic_task(DiagnosticTask::ClearCheck, global_state)
511 }
512
513 flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => {
514 let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
515 &global_state.config.diagnostics,
516 &diagnostic,
517 &workspace_root,
518 );
519 for diag in diagnostics {
520 let path = from_proto::vfs_path(&diag.location.uri)?;
521 let file_id = match global_state.vfs.read().0.file_id(&path) {
522 Some(file) => FileId(file.0),
523 None => {
524 log::error!("File with cargo diagnostic not found in VFS: {}", path);
525 return Ok(());
526 }
527 };
528
529 on_diagnostic_task(
530 DiagnosticTask::AddCheck(
531 file_id,
532 diag.diagnostic,
533 diag.fixes.into_iter().map(|it| it.into()).collect(),
534 ),
535 global_state,
536 )
537 }
538 }
539
540 flycheck::Message::Progress(status) => {
541 let (state, message) = match status {
542 flycheck::Progress::Being => (Progress::Begin, None),
543 flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)),
544 flycheck::Progress::End => (Progress::End, None),
545 };
546
547 report_progress(global_state, "cargo check", state, message, None);
548 }
549 };
550
551 Ok(())
552}
553
554fn on_diagnostic_task(task: DiagnosticTask, global_state: &mut GlobalState) { 560fn on_diagnostic_task(task: DiagnosticTask, global_state: &mut GlobalState) {
555 let subscriptions = global_state.diagnostics.handle_task(task); 561 let subscriptions = global_state.diagnostics.handle_task(task);
556 562