diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 43a0bf8ec05e..dd0804b4398a 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) -> 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,18 +389,7 @@ impl GlobalState { }); } - 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::(status); - } else { - if let (lsp_ext::Health::Error, Some(message)) = (status.health, &status.message) { - self.show_message(lsp_types::MessageType::ERROR, message.clone()); - } - } - } + self.update_status_or_notify(); let loop_duration = loop_start.elapsed(); if loop_duration > Duration::from_millis(100) && was_quiescent { @@ -415,6 +399,20 @@ impl GlobalState { 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::(status); + } else if let (lsp_ext::Health::Error, Some(message)) = (status.health, &status.message) + { + self.show_and_log_error(message.clone(), None); + } + } + } + fn handle_task(&mut self, prime_caches_progress: &mut Vec, task: Task) { match task { Task::Response(response) => self.respond(response), @@ -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 2dfbb2ffb972..fca242d91fd4 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::>(); @@ -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 70b91fe7dc87..f4a4579a92c9 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 e2a30e0cc454..1708d47cee77 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 1eb01f30c1e1..8a2412af849c 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -188,5 +188,6 @@ function createCommands(): Record { runSingle: { enabled: commands.runSingle }, showReferences: { enabled: commands.showReferences }, triggerParameterHints: { enabled: commands.triggerParameterHints }, + openLogs: { enabled: commands.openLogs }, }; }