diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index caefa57c869d3..171a327d818d1 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -164,6 +164,34 @@ struct Error { message: String, } +/// Experimental: Informs the end user about the state of the server +/// +/// [Rust Analyzer Specification](https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/lsp-extensions.md#server-status) +#[derive(Debug)] +pub enum ServerStatus {} + +/// Other(String) variant to handle unknown values due to this still being experimental +#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub enum ServerHealthStatus { + Ok, + Warning, + Error, + Other(String), +} + +#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct ServerStatusParams { + pub health: ServerHealthStatus, + pub message: Option, +} + +impl lsp_types::notification::Notification for ServerStatus { + type Params = ServerStatusParams; + const METHOD: &'static str = "experimental/serverStatus"; +} + impl LanguageServer { /// Starts a language server process. pub fn new( diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 5fb0ede9b5fc7..66a0be0cc21ca 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -50,7 +50,7 @@ use log::error; use lsp::{ DiagnosticSeverity, DiagnosticTag, DidChangeWatchedFilesRegistrationOptions, DocumentHighlightKind, LanguageServer, LanguageServerBinary, LanguageServerId, - MessageActionItem, OneOf, + MessageActionItem, OneOf, ServerHealthStatus, ServerStatus, }; use lsp_command::*; use node_runtime::NodeRuntime; @@ -3201,6 +3201,50 @@ impl Project { let disk_based_diagnostics_progress_token = adapter.disk_based_diagnostics_progress_token.clone(); + language_server + .on_notification::({ + let this = this.clone(); + let name = name.to_string(); + move |params, mut cx| { + let this = this.clone(); + let name = name.to_string(); + if let Some(ref message) = params.message { + let message = message.trim(); + if !message.is_empty() { + let formatted_message = format!( + "Language server {name} (id {server_id}) status update: {message}" + ); + match params.health { + ServerHealthStatus::Ok => log::info!("{}", formatted_message), + ServerHealthStatus::Warning => log::warn!("{}", formatted_message), + ServerHealthStatus::Error => { + log::error!("{}", formatted_message); + let (tx, _rx) = smol::channel::bounded(1); + let request = LanguageServerPromptRequest { + level: PromptLevel::Critical, + message: params.message.unwrap_or_default(), + actions: Vec::new(), + response_channel: tx, + lsp_name: name.clone(), + }; + let _ = this + .update(&mut cx, |_, cx| { + cx.emit(Event::LanguageServerPrompt(request)); + }) + .ok(); + } + ServerHealthStatus::Other(status) => { + log::info!( + "Unknown server health: {status}\n{formatted_message}" + ) + } + } + } + } + } + }) + .detach(); + language_server .on_notification::(move |params, mut cx| { if let Some(this) = this.upgrade() { diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index 94fb597c83cbc..521ea6a2bbbb1 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -213,6 +213,7 @@ impl Render for LanguageServerPrompt { .id("language_server_prompt_notification") .elevation_3(cx) .items_start() + .justify_between() .p_2() .gap_2() .w_full()