]> git.lizzy.rs Git - rust.git/blobdiff - crates/rust-analyzer/src/reload.rs
More robust status notifications
[rust.git] / crates / rust-analyzer / src / reload.rs
index aa8504c3d5168cedf12404ffab8b0ac6d527a466..301c7003b99dafa325467925aa29c2a49746203d 100644 (file)
@@ -9,11 +9,10 @@
 
 use crate::{
     config::{Config, FilesWatcher, LinkedProject},
-    global_state::{GlobalState, Status},
+    global_state::GlobalState,
     lsp_ext,
     main_loop::Task,
 };
-use lsp_ext::StatusParams;
 
 #[derive(Debug)]
 pub(crate) enum ProjectWorkspaceProgress {
@@ -30,6 +29,13 @@ pub(crate) enum BuildDataProgress {
 }
 
 impl GlobalState {
+    pub(crate) fn is_quiescent(&self) -> bool {
+        !(self.fetch_workspaces_queue.op_in_progress()
+            || self.fetch_build_data_queue.op_in_progress()
+            || self.vfs_progress_config_version < self.vfs_config_version
+            || self.vfs_progress_n_done < self.vfs_progress_n_total)
+    }
+
     pub(crate) fn update_configuration(&mut self, config: Config) {
         let _p = profile::span("GlobalState::update_configuration");
         let old_config = mem::replace(&mut self.config, Arc::new(config));
@@ -46,25 +52,17 @@ pub(crate) fn maybe_refresh(&mut self, changes: &[(AbsPathBuf, ChangeKind)]) {
         if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) {
             return;
         }
-        match self.status {
-            Status::Loading | Status::NeedsReload => return,
-            Status::Ready { .. } | Status::Invalid => (),
-        }
         log::info!(
-            "Reloading workspace because of the following changes: {}",
+            "Requesting workspace reload because of the following changes: {}",
             itertools::join(
                 changes
                     .iter()
                     .filter(|(path, kind)| is_interesting(path, *kind))
-                    .map(|(path, kind)| format!("{}/{:?}", path.display(), kind)),
+                    .map(|(path, kind)| format!("{}{:?}", path.display(), kind)),
                 ", "
             )
         );
-        if self.config.cargo_autoreload() {
-            self.fetch_workspaces_request();
-        } else {
-            self.transition(Status::NeedsReload);
-        }
+        self.fetch_workspaces_request();
 
         fn is_interesting(path: &AbsPath, change_kind: ChangeKind) -> bool {
             const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
@@ -101,46 +99,32 @@ fn is_interesting(path: &AbsPath, change_kind: ChangeKind) -> bool {
             false
         }
     }
-    pub(crate) fn transition(&mut self, new_status: Status) {
-        self.status = new_status;
-        if self.config.status_notification() {
-            let lsp_status = match new_status {
-                Status::Loading => lsp_ext::Status::Loading,
-                Status::Ready { partial: true } => lsp_ext::Status::ReadyPartial,
-                Status::Ready { partial: false } => lsp_ext::Status::Ready,
-                Status::Invalid => lsp_ext::Status::Invalid,
-                Status::NeedsReload => lsp_ext::Status::NeedsReload,
-            };
-            self.send_notification::<lsp_ext::StatusNotification>(StatusParams {
-                status: lsp_status,
-            });
+    pub(crate) fn report_new_status_if_needed(&mut self) {
+        if !self.config.server_status_notification() {
+            return;
         }
-    }
-
-    pub(crate) fn fetch_build_data_request(&mut self, build_data_collector: BuildDataCollector) {
-        self.fetch_build_data_queue.request_op(build_data_collector);
-    }
 
-    pub(crate) fn fetch_build_data_if_needed(&mut self) {
-        let mut build_data_collector = match self.fetch_build_data_queue.should_start_op() {
-            Some(it) => it,
-            None => return,
+        let mut status = lsp_ext::ServerStatusParams {
+            health: lsp_ext::Health::Ok,
+            quiescent: self.is_quiescent(),
+            message: None,
         };
-        self.task_pool.handle.spawn_with_sender(move |sender| {
-            sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap();
+        if !self.config.cargo_autoreload()
+            && self.is_quiescent()
+            && self.fetch_workspaces_queue.op_requested()
+        {
+            status.health = lsp_ext::Health::Warning;
+            status.message = Some("Workspace reload required".to_string())
+        }
+        if let Some(error) = self.loading_error() {
+            status.health = lsp_ext::Health::Error;
+            status.message = Some(format!("Workspace reload failed: {}", error))
+        }
 
-            let progress = {
-                let sender = sender.clone();
-                move |msg| {
-                    sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap()
-                }
-            };
-            let res = build_data_collector.collect(&progress);
-            sender.send(Task::FetchBuildData(BuildDataProgress::End(res))).unwrap();
-        });
-    }
-    pub(crate) fn fetch_build_data_completed(&mut self) {
-        self.fetch_build_data_queue.op_completed()
+        if self.last_reported_status.as_ref() != Some(&status) {
+            self.last_reported_status = Some(status.clone());
+            self.send_notification::<lsp_ext::ServerStatusNotification>(status);
+        }
     }
 
     pub(crate) fn fetch_workspaces_request(&mut self) {
@@ -194,54 +178,66 @@ pub(crate) fn fetch_workspaces_if_needed(&mut self) {
             }
         });
     }
-    pub(crate) fn fetch_workspaces_completed(&mut self) {
-        self.fetch_workspaces_queue.op_completed()
+    pub(crate) fn fetch_workspaces_completed(
+        &mut self,
+        workspaces: Vec<anyhow::Result<ProjectWorkspace>>,
+    ) {
+        self.fetch_workspaces_queue.op_completed(workspaces)
     }
 
-    pub(crate) fn switch_workspaces(
+    pub(crate) fn fetch_build_data_request(&mut self, build_data_collector: BuildDataCollector) {
+        self.fetch_build_data_queue.request_op(build_data_collector);
+    }
+    pub(crate) fn fetch_build_data_if_needed(&mut self) {
+        let mut build_data_collector = match self.fetch_build_data_queue.should_start_op() {
+            Some(it) => it,
+            None => return,
+        };
+        self.task_pool.handle.spawn_with_sender(move |sender| {
+            sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap();
+
+            let progress = {
+                let sender = sender.clone();
+                move |msg| {
+                    sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap()
+                }
+            };
+            let res = build_data_collector.collect(&progress);
+            sender.send(Task::FetchBuildData(BuildDataProgress::End(res))).unwrap();
+        });
+    }
+    pub(crate) fn fetch_build_data_completed(
         &mut self,
-        workspaces: Vec<anyhow::Result<ProjectWorkspace>>,
-        workspace_build_data: Option<anyhow::Result<BuildDataResult>>,
+        build_data: anyhow::Result<BuildDataResult>,
     ) {
-        let _p = profile::span("GlobalState::switch_workspaces");
-        log::info!("will switch workspaces: {:?}", workspaces);
+        self.fetch_build_data_queue.op_completed(Some(build_data))
+    }
 
-        let mut has_errors = false;
-        let workspaces = workspaces
-            .into_iter()
-            .filter_map(|res| {
-                res.map_err(|err| {
-                    has_errors = true;
-                    log::error!("failed to load workspace: {:#}", err);
-                    if self.workspaces.is_empty() {
-                        self.show_message(
-                            lsp_types::MessageType::Error,
-                            format!("rust-analyzer failed to load workspace: {:#}", err),
-                        );
-                    }
-                })
-                .ok()
-            })
-            .collect::<Vec<_>>();
+    pub(crate) fn switch_workspaces(&mut self) {
+        let _p = profile::span("GlobalState::switch_workspaces");
+        log::info!("will switch workspaces");
 
-        let workspace_build_data = match workspace_build_data {
-            Some(Ok(it)) => Some(it),
-            Some(Err(err)) => {
-                log::error!("failed to fetch build data: {:#}", err);
-                self.show_message(
-                    lsp_types::MessageType::Error,
-                    format!("rust-analyzer failed to fetch build data: {:#}", err),
-                );
+        if let Some(error_message) = self.loading_error() {
+            log::error!("failed to switch workspaces: {}", error_message);
+            self.show_message(lsp_types::MessageType::Error, error_message);
+            if !self.workspaces.is_empty() {
                 return;
             }
-            None => None,
-        };
-
-        if &*self.workspaces == &workspaces && self.workspace_build_data == workspace_build_data {
-            return;
         }
 
-        if !self.workspaces.is_empty() && has_errors {
+        let workspaces = self
+            .fetch_workspaces_queue
+            .last_op_result()
+            .iter()
+            .filter_map(|res| res.as_ref().ok().cloned())
+            .collect::<Vec<_>>();
+
+        let workspace_build_data = match self.fetch_build_data_queue.last_op_result() {
+            Some(Ok(it)) => Some(it.clone()),
+            None | Some(Err(_)) => None,
+        };
+
+        if *self.workspaces == workspaces && self.workspace_build_data == workspace_build_data {
             return;
         }
 
@@ -337,14 +333,6 @@ pub(crate) fn switch_workspaces(
         };
         change.set_crate_graph(crate_graph);
 
-        if self.config.run_build_scripts() && workspace_build_data.is_none() {
-            let mut collector = BuildDataCollector::default();
-            for ws in &workspaces {
-                ws.collect_build_data_configs(&mut collector);
-            }
-            self.fetch_build_data_request(collector)
-        }
-
         self.source_root_config = project_folders.source_root_config;
         self.workspaces = Arc::new(workspaces);
         self.workspace_build_data = workspace_build_data;
@@ -355,6 +343,24 @@ pub(crate) fn switch_workspaces(
         log::info!("did switch workspaces");
     }
 
+    fn loading_error(&self) -> Option<String> {
+        let mut message = None;
+
+        for ws in self.fetch_workspaces_queue.last_op_result() {
+            if let Err(err) = ws {
+                let message = message.get_or_insert_with(String::new);
+                stdx::format_to!(message, "rust-analyzer failed to load workspace: {:#}\n", err);
+            }
+        }
+
+        if let Some(Err(err)) = self.fetch_build_data_queue.last_op_result() {
+            let message = message.get_or_insert_with(String::new);
+            stdx::format_to!(message, "rust-analyzer failed to fetch build data: {:#}\n", err);
+        }
+
+        message
+    }
+
     fn reload_flycheck(&mut self) {
         let _p = profile::span("GlobalState::reload_flycheck");
         let config = match self.config.flycheck() {