Expose more errors from rust-analyzer on invalid Cargo.toml contents (#8356)

Zephaniah Ong created

Release Notes:

- Fixed ([#7574](https://github.com/zed-industries/zed/issues/7574)).

Change summary

crates/lsp/src/lsp.rs                 | 28 +++++++++++++++++
crates/project/src/project.rs         | 46 ++++++++++++++++++++++++++++
crates/workspace/src/notifications.rs |  1 
3 files changed, 74 insertions(+), 1 deletion(-)

Detailed changes

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<String>,
+}
+
+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(

crates/project/src/project.rs 🔗

@@ -46,7 +46,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;
@@ -3141,6 +3141,50 @@ impl Project {
         let disk_based_diagnostics_progress_token =
             adapter.disk_based_diagnostics_progress_token.clone();
 
+        language_server
+            .on_notification::<ServerStatus, _>({
+                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::<lsp::notification::Progress, _>(move |params, mut cx| {
                 if let Some(this) = this.upgrade() {

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()