about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-03-10 10:12:15 +0000
committerbors <bors@rust-lang.org>2023-03-10 10:12:15 +0000
commit070f8f8578c5d68364c2950bec5c8b33db35800a (patch)
treeee9f8a10b5470260561698707a076cf1b562eb10
parent1b5bba45354f639fc55c82c8267902ad79cde6ca (diff)
parentc03775e4779bd48a2607cbd04fb0e9d4fa5b92cb (diff)
Auto merge of #14313 - Veykril:notifs, r=Veykril
Make project loading errors less intrusive

cc https://github.com/rust-lang/rust-analyzer/issues/14193 closes https://github.com/rust-lang/rust-analyzer/issues/9909

![image](https://user-images.githubusercontent.com/3757771/224274817-a5a8cb0c-7bda-454d-9772-5f839f360b9a.png)
-rw-r--r--crates/rust-analyzer/src/main_loop.rs47
-rw-r--r--crates/rust-analyzer/src/reload.rs23
-rw-r--r--editors/code/src/commands.ts8
-rw-r--r--editors/code/src/ctx.ts34
-rw-r--r--editors/code/src/main.ts1
5 files changed, 68 insertions, 45 deletions
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 43a0bf8ec05..dd0804b4398 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -111,12 +111,7 @@ impl fmt::Debug for Event {
 
 impl GlobalState {
     fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> {
-        if self.config.linked_projects().is_empty()
-            && self.config.detached_files().is_empty()
-            && self.config.notifications().cargo_toml_not_found
-        {
-            self.show_and_log_error("rust-analyzer failed to discover workspace".to_string(), None);
-        };
+        self.update_status_or_notify();
 
         if self.config.did_save_text_document_dynamic_registration() {
             let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions {
@@ -394,25 +389,28 @@ impl GlobalState {
             });
         }
 
+        self.update_status_or_notify();
+
+        let loop_duration = loop_start.elapsed();
+        if loop_duration > Duration::from_millis(100) && was_quiescent {
+            tracing::warn!("overly long loop turn: {:?}", loop_duration);
+            self.poke_rust_analyzer_developer(format!("overly long loop turn: {loop_duration:?}"));
+        }
+        Ok(())
+    }
+
+    fn update_status_or_notify(&mut self) {
         let status = self.current_status();
         if self.last_reported_status.as_ref() != Some(&status) {
             self.last_reported_status = Some(status.clone());
 
             if self.config.server_status_notification() {
                 self.send_notification::<lsp_ext::ServerStatusNotification>(status);
-            } else {
-                if let (lsp_ext::Health::Error, Some(message)) = (status.health, &status.message) {
-                    self.show_message(lsp_types::MessageType::ERROR, message.clone());
-                }
+            } else if let (lsp_ext::Health::Error, Some(message)) = (status.health, &status.message)
+            {
+                self.show_and_log_error(message.clone(), None);
             }
         }
-
-        let loop_duration = loop_start.elapsed();
-        if loop_duration > Duration::from_millis(100) && was_quiescent {
-            tracing::warn!("overly long loop turn: {:?}", loop_duration);
-            self.poke_rust_analyzer_developer(format!("overly long loop turn: {loop_duration:?}"));
-        }
-        Ok(())
     }
 
     fn handle_task(&mut self, prime_caches_progress: &mut Vec<PrimeCachesProgress>, task: Task) {
@@ -445,6 +443,9 @@ impl GlobalState {
                     ProjectWorkspaceProgress::Report(msg) => (Progress::Report, Some(msg)),
                     ProjectWorkspaceProgress::End(workspaces) => {
                         self.fetch_workspaces_queue.op_completed(Some(workspaces));
+                        if let Err(e) = self.fetch_workspace_error() {
+                            tracing::error!("FetchWorkspaceError:\n{e}");
+                        }
 
                         let old = Arc::clone(&self.workspaces);
                         self.switch_workspaces("fetched workspace".to_string());
@@ -466,6 +467,9 @@ impl GlobalState {
                     BuildDataProgress::Report(msg) => (Some(Progress::Report), Some(msg)),
                     BuildDataProgress::End(build_data_result) => {
                         self.fetch_build_data_queue.op_completed(build_data_result);
+                        if let Err(e) = self.fetch_build_data_error() {
+                            tracing::error!("FetchBuildDataError:\n{e}");
+                        }
 
                         self.switch_workspaces("fetched build data".to_string());
 
@@ -498,6 +502,7 @@ impl GlobalState {
                 self.vfs_progress_n_total = n_total;
                 self.vfs_progress_n_done = n_done;
 
+                // if n_total != 0 {
                 let state = if n_done == 0 {
                     Progress::Begin
                 } else if n_done < n_total {
@@ -512,7 +517,8 @@ impl GlobalState {
                     Some(format!("{n_done}/{n_total}")),
                     Some(Progress::fraction(n_done, n_total)),
                     None,
-                )
+                );
+                // }
             }
         }
     }
@@ -554,7 +560,10 @@ impl GlobalState {
                     flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)),
                     flycheck::Progress::DidCancel => (Progress::End, None),
                     flycheck::Progress::DidFailToRestart(err) => {
-                        self.show_and_log_error("cargo check failed".to_string(), Some(err));
+                        self.show_and_log_error(
+                            "cargo check failed to start".to_string(),
+                            Some(err),
+                        );
                         return;
                     }
                     flycheck::Progress::DidFinish(result) => {
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 2dfbb2ffb97..fca242d91fd 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -56,7 +56,8 @@ pub(crate) enum BuildDataProgress {
 
 impl GlobalState {
     pub(crate) fn is_quiescent(&self) -> bool {
-        !(self.fetch_workspaces_queue.op_in_progress()
+        !(self.last_reported_status.is_none()
+            || 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)
@@ -108,9 +109,9 @@ impl GlobalState {
             status.message = Some("Workspace reload required".to_string())
         }
 
-        if let Err(error) = self.fetch_workspace_error() {
+        if let Err(_) = self.fetch_workspace_error() {
             status.health = lsp_ext::Health::Error;
-            status.message = Some(error)
+            status.message = Some("Failed to load workspaces".to_string())
         }
 
         if self.config.linked_projects().is_empty()
@@ -118,8 +119,9 @@ impl GlobalState {
             && self.config.notifications().cargo_toml_not_found
         {
             status.health = lsp_ext::Health::Warning;
-            status.message = Some("Workspace reload required".to_string())
+            status.message = Some("Failed to discover workspace".to_string())
         }
+
         status
     }
 
@@ -201,10 +203,7 @@ impl GlobalState {
         let _p = profile::span("GlobalState::switch_workspaces");
         tracing::info!(%cause, "will switch workspaces");
 
-        if let Err(error_message) = self.fetch_workspace_error() {
-            if !self.config.server_status_notification() {
-                self.show_and_log_error(error_message, None);
-            }
+        if let Err(_) = self.fetch_workspace_error() {
             if !self.workspaces.is_empty() {
                 // It only makes sense to switch to a partially broken workspace
                 // if we don't have any workspace at all yet.
@@ -212,10 +211,6 @@ impl GlobalState {
             }
         }
 
-        if let Err(error) = self.fetch_build_data_error() {
-            self.show_and_log_error("failed to run build scripts".to_string(), Some(error));
-        }
-
         let Some(workspaces) = self.fetch_workspaces_queue.last_op_result() else { return; };
         let workspaces =
             workspaces.iter().filter_map(|res| res.as_ref().ok().cloned()).collect::<Vec<_>>();
@@ -394,7 +389,7 @@ impl GlobalState {
         tracing::info!("did switch workspaces");
     }
 
-    fn fetch_workspace_error(&self) -> Result<(), String> {
+    pub(super) fn fetch_workspace_error(&self) -> Result<(), String> {
         let mut buf = String::new();
 
         let Some(last_op_result) = self.fetch_workspaces_queue.last_op_result() else { return Ok(()) };
@@ -415,7 +410,7 @@ impl GlobalState {
         Err(buf)
     }
 
-    fn fetch_build_data_error(&self) -> Result<(), String> {
+    pub(super) fn fetch_build_data_error(&self) -> Result<(), String> {
         let mut buf = String::new();
 
         for ws in &self.fetch_build_data_queue.last_op_result().1 {
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 70b91fe7dc8..f4a4579a92c 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -93,6 +93,14 @@ export function triggerParameterHints(_: CtxInit): Cmd {
     };
 }
 
+export function openLogs(ctx: CtxInit): Cmd {
+    return async () => {
+        if (ctx.client.outputChannel) {
+            ctx.client.outputChannel.show();
+        }
+    };
+}
+
 export function matchingBrace(ctx: CtxInit): Cmd {
     return async () => {
         const editor = ctx.activeRustEditor;
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index e2a30e0cc45..1708d47cee7 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -282,18 +282,18 @@ export class Ctx {
     setServerStatus(status: ServerStatusParams | { health: "stopped" }) {
         let icon = "";
         const statusBar = this.statusBar;
+        statusBar.tooltip = new vscode.MarkdownString("", true);
+        statusBar.tooltip.isTrusted = true;
         switch (status.health) {
             case "ok":
-                statusBar.tooltip = (status.message ?? "Ready") + "\nClick to stop server.";
-                statusBar.command = "rust-analyzer.stopServer";
+                statusBar.tooltip.appendText(status.message ?? "Ready");
                 statusBar.color = undefined;
                 statusBar.backgroundColor = undefined;
                 break;
             case "warning":
-                statusBar.tooltip =
-                    (status.message ? status.message + "\n" : "") + "Click to reload.";
-
-                statusBar.command = "rust-analyzer.reloadWorkspace";
+                if (status.message) {
+                    statusBar.tooltip.appendText(status.message);
+                }
                 statusBar.color = new vscode.ThemeColor("statusBarItem.warningForeground");
                 statusBar.backgroundColor = new vscode.ThemeColor(
                     "statusBarItem.warningBackground"
@@ -301,22 +301,32 @@ export class Ctx {
                 icon = "$(warning) ";
                 break;
             case "error":
-                statusBar.tooltip =
-                    (status.message ? status.message + "\n" : "") + "Click to reload.";
-
-                statusBar.command = "rust-analyzer.reloadWorkspace";
+                if (status.message) {
+                    statusBar.tooltip.appendText(status.message);
+                }
                 statusBar.color = new vscode.ThemeColor("statusBarItem.errorForeground");
                 statusBar.backgroundColor = new vscode.ThemeColor("statusBarItem.errorBackground");
                 icon = "$(error) ";
                 break;
             case "stopped":
-                statusBar.tooltip = "Server is stopped.\nClick to start.";
-                statusBar.command = "rust-analyzer.startServer";
+                statusBar.tooltip.appendText("Server is stopped");
+                statusBar.tooltip.appendMarkdown(
+                    "\n\n[Start server](command:rust-analyzer.startServer)"
+                );
                 statusBar.color = undefined;
                 statusBar.backgroundColor = undefined;
                 statusBar.text = `$(stop-circle) rust-analyzer`;
                 return;
         }
+        if (statusBar.tooltip.value) {
+            statusBar.tooltip.appendText("\n\n");
+        }
+        statusBar.tooltip.appendMarkdown("[Stop server](command:rust-analyzer.stopServer)");
+        statusBar.tooltip.appendMarkdown(
+            "\n\n[Reload Workspace](command:rust-analyzer.reloadWorkspace)"
+        );
+        statusBar.tooltip.appendMarkdown("\n\n[Restart server](command:rust-analyzer.startServer)");
+        statusBar.tooltip.appendMarkdown("\n\n[Open logs](command:rust-analyzer.openLogs)");
         if (!status.quiescent) icon = "$(sync~spin) ";
         statusBar.text = `${icon}rust-analyzer`;
     }
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 1eb01f30c1e..8a2412af849 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -188,5 +188,6 @@ function createCommands(): Record<string, CommandFactory> {
         runSingle: { enabled: commands.runSingle },
         showReferences: { enabled: commands.showReferences },
         triggerParameterHints: { enabled: commands.triggerParameterHints },
+        openLogs: { enabled: commands.openLogs },
     };
 }