From dba11cb060f3632a590a4f9c585d58218f17cced Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 25 Jun 2020 19:01:16 +0200 Subject: -mMake it harder to accidently early-exit the loop --- crates/rust-analyzer/src/main_loop.rs | 326 +++++++++++++++++----------------- 1 file changed, 166 insertions(+), 160 deletions(-) (limited to 'crates') 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<()> { Ok(()) } +enum Event { + Lsp(lsp_server::Message), + Task(Task), + Vfs(vfs::loader::Message), + Flycheck(flycheck::Message), +} + +impl fmt::Debug for Event { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let debug_verbose_not = |not: &Notification, f: &mut fmt::Formatter| { + f.debug_struct("Notification").field("method", ¬.method).finish() + }; + + match self { + Event::Lsp(lsp_server::Message::Notification(not)) => { + if notification_is::(not) + || notification_is::(not) + { + return debug_verbose_not(not, f); + } + } + Event::Task(Task::Respond(resp)) => { + return f + .debug_struct("Response") + .field("id", &resp.id) + .field("error", &resp.error) + .finish(); + } + _ => (), + } + match self { + Event::Lsp(it) => fmt::Debug::fmt(it, f), + Event::Task(it) => fmt::Debug::fmt(it, f), + Event::Vfs(it) => fmt::Debug::fmt(it, f), + Event::Flycheck(it) => fmt::Debug::fmt(it, f), + } + } +} + impl GlobalState { fn next_event(&self, inbox: &Receiver) -> Option { select! { @@ -145,97 +184,150 @@ impl GlobalState { fn run(mut self, inbox: Receiver) -> Result<()> { while let Some(event) = self.next_event(&inbox) { - let loop_start = Instant::now(); - // NOTE: don't count blocking select! call as a loop-turn time - let _p = profile("main_loop_inner/loop-turn"); - - log::info!("loop turn = {:?}", event); - let queue_count = self.task_pool.0.len(); - if queue_count > 0 { - log::info!("queued count = {}", queue_count); + if let Event::Lsp(lsp_server::Message::Notification(not)) = &event { + if not.method == lsp_types::notification::Exit::METHOD { + return Ok(()); + } } + self.loop_turn(event)? + } + Err("client exited without proper shutdown sequence")? + } - let mut became_ready = false; - match event { - Event::Lsp(msg) => match msg { - lsp_server::Message::Request(req) => self.on_request(loop_start, req)?, - lsp_server::Message::Notification(not) => { - if not.method == lsp_types::notification::Exit::METHOD { - return Ok(()); + fn loop_turn(&mut self, event: Event) -> Result<()> { + let loop_start = Instant::now(); + // NOTE: don't count blocking select! call as a loop-turn time + let _p = profile("main_loop_inner/loop-turn"); + + log::info!("loop turn = {:?}", event); + let queue_count = self.task_pool.0.len(); + if queue_count > 0 { + log::info!("queued count = {}", queue_count); + } + + let mut became_ready = false; + match event { + Event::Lsp(msg) => match msg { + lsp_server::Message::Request(req) => self.on_request(loop_start, req)?, + lsp_server::Message::Notification(not) => { + self.on_notification(not)?; + } + lsp_server::Message::Response(resp) => { + let handler = self.req_queue.outgoing.complete(resp.id.clone()); + handler(self, resp) + } + }, + Event::Task(task) => { + self.on_task(task); + self.maybe_collect_garbage(); + } + Event::Vfs(task) => match task { + vfs::loader::Message::Loaded { files } => { + let vfs = &mut self.vfs.write().0; + for (path, contents) in files { + let path = VfsPath::from(path); + if !self.mem_docs.contains(&path) { + vfs.set_file_contents(path, contents) } - self.on_notification(not)?; } - lsp_server::Message::Response(resp) => { - let handler = self.req_queue.outgoing.complete(resp.id.clone()); - handler(&mut self, resp) - } - }, - Event::Task(task) => { - self.on_task(task); - self.maybe_collect_garbage(); } - Event::Vfs(task) => match task { - vfs::loader::Message::Loaded { files } => { - let vfs = &mut self.vfs.write().0; - for (path, contents) in files { - let path = VfsPath::from(path); - if !self.mem_docs.contains(&path) { - vfs.set_file_contents(path, contents) + vfs::loader::Message::Progress { n_total, n_done } => { + let state = if n_done == 0 { + Progress::Begin + } else if n_done < n_total { + Progress::Report + } else { + assert_eq!(n_done, n_total); + self.status = Status::Ready; + became_ready = true; + Progress::End + }; + report_progress( + self, + "roots scanned", + state, + Some(format!("{}/{}", n_done, n_total)), + Some(percentage(n_done, n_total)), + ) + } + }, + Event::Flycheck(task) => match task { + flycheck::Message::ClearDiagnostics => { + on_diagnostic_task(DiagnosticTask::ClearCheck, self) + } + + flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => { + let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( + &self.config.diagnostics, + &diagnostic, + &workspace_root, + ); + for diag in diagnostics { + let path = from_proto::vfs_path(&diag.location.uri)?; + let file_id = match self.vfs.read().0.file_id(&path) { + Some(file) => FileId(file.0), + None => { + log::error!( + "File with cargo diagnostic not found in VFS: {}", + path + ); + return Ok(()); } - } - } - vfs::loader::Message::Progress { n_total, n_done } => { - let state = if n_done == 0 { - Progress::Begin - } else if n_done < n_total { - Progress::Report - } else { - assert_eq!(n_done, n_total); - self.status = Status::Ready; - became_ready = true; - Progress::End }; - report_progress( - &mut self, - "roots scanned", - state, - Some(format!("{}/{}", n_done, n_total)), - Some(percentage(n_done, n_total)), + + on_diagnostic_task( + DiagnosticTask::AddCheck( + file_id, + diag.diagnostic, + diag.fixes.into_iter().map(|it| it.into()).collect(), + ), + self, ) } - }, - Event::Flycheck(task) => on_check_task(task, &mut self)?, - } + } - let state_changed = self.process_changes(); - if became_ready { - if let Some(flycheck) = &self.flycheck { - flycheck.0.update(); + flycheck::Message::Progress(status) => { + let (state, message) = match status { + flycheck::Progress::Being => (Progress::Begin, None), + flycheck::Progress::DidCheckCrate(target) => { + (Progress::Report, Some(target)) + } + flycheck::Progress::End => (Progress::End, None), + }; + + report_progress(self, "cargo check", state, message, None); } + }, + } + + let state_changed = self.process_changes(); + if became_ready { + if let Some(flycheck) = &self.flycheck { + flycheck.0.update(); } + } - if self.status == Status::Ready && (state_changed || became_ready) { - let subscriptions = self - .mem_docs - .iter() - .map(|path| self.vfs.read().0.file_id(&path).unwrap()) - .collect::>(); + if self.status == Status::Ready && (state_changed || became_ready) { + let subscriptions = self + .mem_docs + .iter() + .map(|path| self.vfs.read().0.file_id(&path).unwrap()) + .collect::>(); - self.update_file_notifications_on_threadpool(subscriptions); - } + self.update_file_notifications_on_threadpool(subscriptions); + } - let loop_duration = loop_start.elapsed(); - if loop_duration > Duration::from_millis(100) { - log::error!("overly long loop turn: {:?}", loop_duration); - if env::var("RA_PROFILE").is_ok() { - self.show_message( - lsp_types::MessageType::Error, - format!("overly long loop turn: {:?}", loop_duration), - ) - } + let loop_duration = loop_start.elapsed(); + if loop_duration > Duration::from_millis(100) { + log::error!("overly long loop turn: {:?}", loop_duration); + if env::var("RA_PROFILE").is_ok() { + self.show_message( + lsp_types::MessageType::Error, + format!("overly long loop turn: {:?}", loop_duration), + ) } } - Err("client exited without proper shutdown sequence")? + Ok(()) } fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> { @@ -461,96 +553,10 @@ pub(crate) enum Task { Unit, } -enum Event { - Lsp(lsp_server::Message), - Task(Task), - Vfs(vfs::loader::Message), - Flycheck(flycheck::Message), -} - -impl fmt::Debug for Event { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let debug_verbose_not = |not: &Notification, f: &mut fmt::Formatter| { - f.debug_struct("Notification").field("method", ¬.method).finish() - }; - - match self { - Event::Lsp(lsp_server::Message::Notification(not)) => { - if notification_is::(not) - || notification_is::(not) - { - return debug_verbose_not(not, f); - } - } - Event::Task(Task::Respond(resp)) => { - return f - .debug_struct("Response") - .field("id", &resp.id) - .field("error", &resp.error) - .finish(); - } - _ => (), - } - match self { - Event::Lsp(it) => fmt::Debug::fmt(it, f), - Event::Task(it) => fmt::Debug::fmt(it, f), - Event::Vfs(it) => fmt::Debug::fmt(it, f), - Event::Flycheck(it) => fmt::Debug::fmt(it, f), - } - } -} - pub(crate) type ReqHandler = fn(&mut GlobalState, Response); pub(crate) type ReqQueue = lsp_server::ReqQueue<(&'static str, Instant), ReqHandler>; const DO_NOTHING: ReqHandler = |_, _| (); -fn on_check_task(task: flycheck::Message, global_state: &mut GlobalState) -> Result<()> { - match task { - flycheck::Message::ClearDiagnostics => { - on_diagnostic_task(DiagnosticTask::ClearCheck, global_state) - } - - flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => { - let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( - &global_state.config.diagnostics, - &diagnostic, - &workspace_root, - ); - for diag in diagnostics { - let path = from_proto::vfs_path(&diag.location.uri)?; - let file_id = match global_state.vfs.read().0.file_id(&path) { - Some(file) => FileId(file.0), - None => { - log::error!("File with cargo diagnostic not found in VFS: {}", path); - return Ok(()); - } - }; - - on_diagnostic_task( - DiagnosticTask::AddCheck( - file_id, - diag.diagnostic, - diag.fixes.into_iter().map(|it| it.into()).collect(), - ), - global_state, - ) - } - } - - flycheck::Message::Progress(status) => { - let (state, message) = match status { - flycheck::Progress::Being => (Progress::Begin, None), - flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)), - flycheck::Progress::End => (Progress::End, None), - }; - - report_progress(global_state, "cargo check", state, message, None); - } - }; - - Ok(()) -} - fn on_diagnostic_task(task: DiagnosticTask, global_state: &mut GlobalState) { let subscriptions = global_state.diagnostics.handle_task(task); -- cgit v1.2.3 From 86a4d4cb9c526e095985cc26668c4ed653941fc2 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 25 Jun 2020 19:23:52 +0200 Subject: Simplify --- crates/rust-analyzer/src/dispatch.rs | 44 ++++++++++++++------------------ crates/rust-analyzer/src/global_state.rs | 14 +++++++--- crates/rust-analyzer/src/main_loop.rs | 42 ++++++++++-------------------- 3 files changed, 43 insertions(+), 57 deletions(-) (limited to 'crates') diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs index 5fdbed8ef..03b373dee 100644 --- a/crates/rust-analyzer/src/dispatch.rs +++ b/crates/rust-analyzer/src/dispatch.rs @@ -1,5 +1,5 @@ //! A visitor for downcasting arbitrary request (JSON) into a specific type. -use std::{panic, time::Instant}; +use std::panic; use serde::{de::DeserializeOwned, Serialize}; @@ -13,7 +13,6 @@ use crate::{ pub(crate) struct RequestDispatcher<'a> { pub(crate) req: Option, pub(crate) global_state: &'a mut GlobalState, - pub(crate) request_received: Instant, } impl<'a> RequestDispatcher<'a> { @@ -34,12 +33,12 @@ impl<'a> RequestDispatcher<'a> { } }; let world = panic::AssertUnwindSafe(&mut *self.global_state); - let task = panic::catch_unwind(move || { + let response = panic::catch_unwind(move || { let result = f(world.0, params); - result_to_task::(id, result) + result_to_response::(id, result) }) .map_err(|_| format!("sync task {:?} panicked", R::METHOD))?; - self.global_state.on_task(task); + self.global_state.respond(response); Ok(self) } @@ -64,7 +63,7 @@ impl<'a> RequestDispatcher<'a> { let world = self.global_state.snapshot(); move || { let result = f(world, params); - result_to_task::(id, result) + Task::Response(result_to_response::(id, result)) } }); @@ -72,17 +71,14 @@ impl<'a> RequestDispatcher<'a> { } pub(crate) fn finish(&mut self) { - match self.req.take() { - None => (), - Some(req) => { - log::error!("unknown request: {:?}", req); - let resp = lsp_server::Response::new_err( - req.id, - lsp_server::ErrorCode::MethodNotFound as i32, - "unknown request".to_string(), - ); - self.global_state.send(resp.into()); - } + if let Some(req) = self.req.take() { + log::error!("unknown request: {:?}", req); + let response = lsp_server::Response::new_err( + req.id, + lsp_server::ErrorCode::MethodNotFound as i32, + "unknown request".to_string(), + ); + self.global_state.respond(response) } } @@ -99,21 +95,20 @@ impl<'a> RequestDispatcher<'a> { return None; } }; - self.global_state - .req_queue - .incoming - .register(id.clone(), (R::METHOD, self.request_received)); Some((id, params)) } } -fn result_to_task(id: lsp_server::RequestId, result: Result) -> Task +fn result_to_response( + id: lsp_server::RequestId, + result: Result, +) -> lsp_server::Response where R: lsp_types::request::Request + 'static, R::Params: DeserializeOwned + 'static, R::Result: Serialize + 'static, { - let response = match result { + match result { Ok(resp) => lsp_server::Response::new_ok(id, &resp), Err(e) => match e.downcast::() { Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message), @@ -133,8 +128,7 @@ where } } }, - }; - Task::Respond(response) + } } pub(crate) struct NotificationDispatcher<'a> { diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 56d50c789..149b1b5f9 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -253,13 +253,19 @@ impl GlobalState { self.analysis_host.collect_garbage() } - pub(crate) fn complete_request(&mut self, request: RequestMetrics) { - self.latest_requests.write().record(request) - } - pub(crate) fn send(&mut self, message: lsp_server::Message) { self.sender.send(message).unwrap() } + pub(crate) fn respond(&mut self, response: lsp_server::Response) { + if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) { + let duration = start.elapsed(); + log::info!("handled req#{} in {:?}", response.id, duration); + let metrics = + RequestMetrics { id: response.id.clone(), method: method.to_string(), duration }; + self.latest_requests.write().record(metrics); + self.send(response.into()); + } + } pub(crate) fn show_message(&mut self, typ: lsp_types::MessageType, message: String) { show_message(typ, message, &self.sender) } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 94b9c0ca2..76dea3e22 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -23,7 +23,6 @@ use crate::{ lsp_utils::{ apply_document_changes, is_canceled, notification_is, notification_new, show_message, }, - request_metrics::RequestMetrics, Result, }; @@ -147,7 +146,7 @@ impl fmt::Debug for Event { return debug_verbose_not(not, f); } } - Event::Task(Task::Respond(resp)) => { + Event::Task(Task::Response(resp)) => { return f .debug_struct("Response") .field("id", &resp.id) @@ -218,7 +217,13 @@ impl GlobalState { } }, Event::Task(task) => { - self.on_task(task); + match task { + Task::Response(response) => self.respond(response), + Task::Diagnostics(tasks) => { + tasks.into_iter().for_each(|task| on_diagnostic_task(task, self)) + } + Task::Unit => (), + } self.maybe_collect_garbage(); } Event::Vfs(task) => match task { @@ -331,7 +336,9 @@ impl GlobalState { } fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> { - RequestDispatcher { req: Some(req), global_state: self, request_received } + self.req_queue.incoming.register(req.id.clone(), (req.method.clone(), request_received)); + + RequestDispatcher { req: Some(req), global_state: self } .on_sync::(|s, ()| Ok(s.collect_garbage()))? .on_sync::(|s, p| handlers::handle_join_lines(s.snapshot(), p))? .on_sync::(|s, p| handlers::handle_on_enter(s.snapshot(), p))? @@ -492,27 +499,6 @@ impl GlobalState { .finish(); Ok(()) } - pub(crate) fn on_task(&mut self, task: Task) { - match task { - Task::Respond(response) => { - if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) - { - let duration = start.elapsed(); - log::info!("handled req#{} in {:?}", response.id, duration); - self.complete_request(RequestMetrics { - id: response.id.clone(), - method: method.to_string(), - duration, - }); - self.send(response.into()); - } - } - Task::Diagnostics(tasks) => { - tasks.into_iter().for_each(|task| on_diagnostic_task(task, self)) - } - Task::Unit => (), - } - } fn update_file_notifications_on_threadpool(&mut self, subscriptions: Vec) { log::trace!("updating notifications for {:?}", subscriptions); if self.config.publish_diagnostics { @@ -548,13 +534,13 @@ impl GlobalState { #[derive(Debug)] pub(crate) enum Task { - Respond(Response), - Diagnostics(Vec), + Response(Response), + Diagnostics(()), Unit, } pub(crate) type ReqHandler = fn(&mut GlobalState, Response); -pub(crate) type ReqQueue = lsp_server::ReqQueue<(&'static str, Instant), ReqHandler>; +pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>; const DO_NOTHING: ReqHandler = |_, _| (); fn on_diagnostic_task(task: DiagnosticTask, global_state: &mut GlobalState) { -- cgit v1.2.3 From df769e5bb4e26a2f150ce2c37b9b9364ee10bab8 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 25 Jun 2020 22:45:35 +0200 Subject: Simplify diagnostics handling --- crates/rust-analyzer/src/diagnostics.rs | 54 ++++++++++++----------------- crates/rust-analyzer/src/handlers.rs | 5 ++- crates/rust-analyzer/src/main_loop.rs | 61 ++++++++++++++------------------- 3 files changed, 50 insertions(+), 70 deletions(-) (limited to 'crates') diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs index 290609e7f..f3cdb842b 100644 --- a/crates/rust-analyzer/src/diagnostics.rs +++ b/crates/rust-analyzer/src/diagnostics.rs @@ -1,14 +1,15 @@ //! Book keeping for keeping diagnostics easily in sync with the client. pub(crate) mod to_proto; -use std::{collections::HashMap, sync::Arc}; +use std::{collections::HashMap, mem, sync::Arc}; use lsp_types::{Diagnostic, Range}; use ra_ide::FileId; +use rustc_hash::FxHashSet; use crate::lsp_ext; -pub type CheckFixes = Arc>>; +pub(crate) type CheckFixes = Arc>>; #[derive(Debug, Default, Clone)] pub struct DiagnosticsConfig { @@ -17,32 +18,26 @@ pub struct DiagnosticsConfig { } #[derive(Debug, Default, Clone)] -pub struct DiagnosticCollection { - pub native: HashMap>, - pub check: HashMap>, - pub check_fixes: CheckFixes, +pub(crate) struct DiagnosticCollection { + pub(crate) native: HashMap>, + pub(crate) check: HashMap>, + pub(crate) check_fixes: CheckFixes, + changes: FxHashSet, } #[derive(Debug, Clone)] -pub struct Fix { - pub range: Range, - pub action: lsp_ext::CodeAction, -} - -#[derive(Debug)] -pub enum DiagnosticTask { - ClearCheck, - AddCheck(FileId, Diagnostic, Vec), - SetNative(FileId, Vec), +pub(crate) struct Fix { + pub(crate) range: Range, + pub(crate) action: lsp_ext::CodeAction, } impl DiagnosticCollection { - pub fn clear_check(&mut self) -> Vec { + pub(crate) fn clear_check(&mut self) { Arc::make_mut(&mut self.check_fixes).clear(); - self.check.drain().map(|(key, _value)| key).collect() + self.changes.extend(self.check.drain().map(|(key, _value)| key)) } - pub fn add_check_diagnostic( + pub(crate) fn add_check_diagnostic( &mut self, file_id: FileId, diagnostic: Diagnostic, @@ -61,30 +56,25 @@ impl DiagnosticCollection { .or_default() .extend(fixes.into_iter().map(|action| Fix { range: diagnostic.range, action })); diagnostics.push(diagnostic); + self.changes.insert(file_id); } - pub fn set_native_diagnostics(&mut self, file_id: FileId, diagnostics: Vec) { + pub(crate) fn set_native_diagnostics(&mut self, file_id: FileId, diagnostics: Vec) { self.native.insert(file_id, diagnostics); + self.changes.insert(file_id); } - pub fn diagnostics_for(&self, file_id: FileId) -> impl Iterator { + pub(crate) fn diagnostics_for(&self, file_id: FileId) -> impl Iterator { let native = self.native.get(&file_id).into_iter().flatten(); let check = self.check.get(&file_id).into_iter().flatten(); native.chain(check) } - pub fn handle_task(&mut self, task: DiagnosticTask) -> Vec { - match task { - DiagnosticTask::ClearCheck => self.clear_check(), - DiagnosticTask::AddCheck(file_id, diagnostic, fixes) => { - self.add_check_diagnostic(file_id, diagnostic, fixes); - vec![file_id] - } - DiagnosticTask::SetNative(file_id, diagnostics) => { - self.set_native_diagnostics(file_id, diagnostics); - vec![file_id] - } + pub(crate) fn take_changes(&mut self) -> Option> { + if self.changes.is_empty() { + return None; } + Some(mem::take(&mut self.changes)) } } diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index b2ff9a157..12b494496 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -31,7 +31,6 @@ use stdx::{format_to, split_delim}; use crate::{ cargo_target_spec::CargoTargetSpec, config::RustfmtConfig, - diagnostics::DiagnosticTask, from_json, from_proto, global_state::GlobalStateSnapshot, lsp_ext::{self, InlayHint, InlayHintsParams}, @@ -950,7 +949,7 @@ pub(crate) fn handle_ssr( pub(crate) fn publish_diagnostics( snap: &GlobalStateSnapshot, file_id: FileId, -) -> Result { +) -> Result> { let _p = profile("publish_diagnostics"); let line_index = snap.analysis.file_line_index(file_id)?; let diagnostics: Vec = snap @@ -967,7 +966,7 @@ pub(crate) fn publish_diagnostics( tags: None, }) .collect(); - Ok(DiagnosticTask::SetNative(file_id, diagnostics)) + Ok(diagnostics) } pub(crate) fn handle_inlay_hints( diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 76dea3e22..1bd9d6389 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -15,7 +15,6 @@ use ra_project_model::{PackageRoot, ProjectWorkspace}; use crate::{ config::{Config, FilesWatcher, LinkedProject}, - diagnostics::DiagnosticTask, dispatch::{NotificationDispatcher, RequestDispatcher}, from_proto, global_state::{file_id_to_url, GlobalState, Status}, @@ -132,6 +131,13 @@ enum Event { Flycheck(flycheck::Message), } +#[derive(Debug)] +pub(crate) enum Task { + Response(Response), + Diagnostics(Vec<(FileId, Vec)>), + Unit, +} + impl fmt::Debug for Event { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let debug_verbose_not = |not: &Notification, f: &mut fmt::Formatter| { @@ -219,8 +225,10 @@ impl GlobalState { Event::Task(task) => { match task { Task::Response(response) => self.respond(response), - Task::Diagnostics(tasks) => { - tasks.into_iter().for_each(|task| on_diagnostic_task(task, self)) + Task::Diagnostics(diagnostics_per_file) => { + for (file_id, diagnostics) in diagnostics_per_file { + self.diagnostics.set_native_diagnostics(file_id, diagnostics) + } } Task::Unit => (), } @@ -257,9 +265,7 @@ impl GlobalState { } }, Event::Flycheck(task) => match task { - flycheck::Message::ClearDiagnostics => { - on_diagnostic_task(DiagnosticTask::ClearCheck, self) - } + flycheck::Message::ClearDiagnostics => self.diagnostics.clear_check(), flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => { let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( @@ -279,15 +285,7 @@ impl GlobalState { return Ok(()); } }; - - on_diagnostic_task( - DiagnosticTask::AddCheck( - file_id, - diag.diagnostic, - diag.fixes.into_iter().map(|it| it.into()).collect(), - ), - self, - ) + self.diagnostics.add_check_diagnostic(file_id, diag.diagnostic, diag.fixes) } } @@ -322,9 +320,20 @@ impl GlobalState { self.update_file_notifications_on_threadpool(subscriptions); } + if let Some(diagnostic_changes) = self.diagnostics.take_changes() { + for file_id in diagnostic_changes { + let url = file_id_to_url(&self.vfs.read().0, file_id); + let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect(); + let params = + lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version: None }; + let not = notification_new::(params); + self.send(not.into()); + } + } + let loop_duration = loop_start.elapsed(); if loop_duration > Duration::from_millis(100) { - log::error!("overly long loop turn: {:?}", loop_duration); + log::warn!("overly long loop turn: {:?}", loop_duration); if env::var("RA_PROFILE").is_ok() { self.show_message( lsp_types::MessageType::Error, @@ -516,6 +525,7 @@ impl GlobalState { () }) .ok() + .map(|diags| (file_id, diags)) }) .collect::>(); Task::Diagnostics(diagnostics) @@ -532,29 +542,10 @@ impl GlobalState { } } -#[derive(Debug)] -pub(crate) enum Task { - Response(Response), - Diagnostics(()), - Unit, -} - pub(crate) type ReqHandler = fn(&mut GlobalState, Response); pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>; const DO_NOTHING: ReqHandler = |_, _| (); -fn on_diagnostic_task(task: DiagnosticTask, global_state: &mut GlobalState) { - let subscriptions = global_state.diagnostics.handle_task(task); - - for file_id in subscriptions { - let url = file_id_to_url(&global_state.vfs.read().0, file_id); - let diagnostics = global_state.diagnostics.diagnostics_for(file_id).cloned().collect(); - let params = lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version: None }; - let not = notification_new::(params); - global_state.send(not.into()); - } -} - #[derive(Eq, PartialEq)] enum Progress { Begin, -- cgit v1.2.3