diagnostics: Fix diagnostics pane clearing up too eagerly on typing (#37546)

Lukas Wirth created

Closes https://github.com/zed-industries/zed/issues/30494

Release Notes:

- Fixed diagnostics pane closing buffers too eagerly when typing inside
it

Change summary

crates/diagnostics/src/diagnostics.rs | 74 ++++++++++++++--------------
crates/diagnostics/src/items.rs       | 63 ++++++++++--------------
2 files changed, 64 insertions(+), 73 deletions(-)

Detailed changes

crates/diagnostics/src/diagnostics.rs 🔗

@@ -94,43 +94,44 @@ impl Render for ProjectDiagnosticsEditor {
             0
         };
 
-        let child = if warning_count + self.summary.error_count == 0 {
-            let label = if self.summary.warning_count == 0 {
-                SharedString::new_static("No problems in workspace")
+        let child =
+            if warning_count + self.summary.error_count == 0 && self.editor.read(cx).is_empty(cx) {
+                let label = if self.summary.warning_count == 0 {
+                    SharedString::new_static("No problems in workspace")
+                } else {
+                    SharedString::new_static("No errors in workspace")
+                };
+                v_flex()
+                    .key_context("EmptyPane")
+                    .size_full()
+                    .gap_1()
+                    .justify_center()
+                    .items_center()
+                    .text_center()
+                    .bg(cx.theme().colors().editor_background)
+                    .child(Label::new(label).color(Color::Muted))
+                    .when(self.summary.warning_count > 0, |this| {
+                        let plural_suffix = if self.summary.warning_count > 1 {
+                            "s"
+                        } else {
+                            ""
+                        };
+                        let label = format!(
+                            "Show {} warning{}",
+                            self.summary.warning_count, plural_suffix
+                        );
+                        this.child(
+                            Button::new("diagnostics-show-warning-label", label).on_click(
+                                cx.listener(|this, _, window, cx| {
+                                    this.toggle_warnings(&Default::default(), window, cx);
+                                    cx.notify();
+                                }),
+                            ),
+                        )
+                    })
             } else {
-                SharedString::new_static("No errors in workspace")
+                div().size_full().child(self.editor.clone())
             };
-            v_flex()
-                .key_context("EmptyPane")
-                .size_full()
-                .gap_1()
-                .justify_center()
-                .items_center()
-                .text_center()
-                .bg(cx.theme().colors().editor_background)
-                .child(Label::new(label).color(Color::Muted))
-                .when(self.summary.warning_count > 0, |this| {
-                    let plural_suffix = if self.summary.warning_count > 1 {
-                        "s"
-                    } else {
-                        ""
-                    };
-                    let label = format!(
-                        "Show {} warning{}",
-                        self.summary.warning_count, plural_suffix
-                    );
-                    this.child(
-                        Button::new("diagnostics-show-warning-label", label).on_click(cx.listener(
-                            |this, _, window, cx| {
-                                this.toggle_warnings(&Default::default(), window, cx);
-                                cx.notify();
-                            },
-                        )),
-                    )
-                })
-        } else {
-            div().size_full().child(self.editor.clone())
-        };
 
         div()
             .key_context("Diagnostics")
@@ -233,6 +234,7 @@ impl ProjectDiagnosticsEditor {
                         }
                     }
                     EditorEvent::Blurred => this.update_stale_excerpts(window, cx),
+                    EditorEvent::Saved => this.update_stale_excerpts(window, cx),
                     _ => {}
                 }
             },
@@ -277,7 +279,7 @@ impl ProjectDiagnosticsEditor {
     }
 
     fn update_stale_excerpts(&mut self, window: &mut Window, cx: &mut Context<Self>) {
-        if self.update_excerpts_task.is_some() {
+        if self.update_excerpts_task.is_some() || self.multibuffer.read(cx).is_dirty(cx) {
             return;
         }
 

crates/diagnostics/src/items.rs 🔗

@@ -32,49 +32,38 @@ impl Render for DiagnosticIndicator {
         }
 
         let diagnostic_indicator = match (self.summary.error_count, self.summary.warning_count) {
-            (0, 0) => h_flex().map(|this| {
-                this.child(
-                    Icon::new(IconName::Check)
-                        .size(IconSize::Small)
-                        .color(Color::Default),
-                )
-            }),
-            (0, warning_count) => h_flex()
-                .gap_1()
-                .child(
-                    Icon::new(IconName::Warning)
-                        .size(IconSize::Small)
-                        .color(Color::Warning),
-                )
-                .child(Label::new(warning_count.to_string()).size(LabelSize::Small)),
-            (error_count, 0) => h_flex()
-                .gap_1()
-                .child(
-                    Icon::new(IconName::XCircle)
-                        .size(IconSize::Small)
-                        .color(Color::Error),
-                )
-                .child(Label::new(error_count.to_string()).size(LabelSize::Small)),
+            (0, 0) => h_flex().child(
+                Icon::new(IconName::Check)
+                    .size(IconSize::Small)
+                    .color(Color::Default),
+            ),
             (error_count, warning_count) => h_flex()
                 .gap_1()
-                .child(
-                    Icon::new(IconName::XCircle)
-                        .size(IconSize::Small)
-                        .color(Color::Error),
-                )
-                .child(Label::new(error_count.to_string()).size(LabelSize::Small))
-                .child(
-                    Icon::new(IconName::Warning)
-                        .size(IconSize::Small)
-                        .color(Color::Warning),
-                )
-                .child(Label::new(warning_count.to_string()).size(LabelSize::Small)),
+                .when(error_count > 0, |this| {
+                    this.child(
+                        Icon::new(IconName::XCircle)
+                            .size(IconSize::Small)
+                            .color(Color::Error),
+                    )
+                    .child(Label::new(error_count.to_string()).size(LabelSize::Small))
+                })
+                .when(warning_count > 0, |this| {
+                    this.child(
+                        Icon::new(IconName::Warning)
+                            .size(IconSize::Small)
+                            .color(Color::Warning),
+                    )
+                    .child(Label::new(warning_count.to_string()).size(LabelSize::Small))
+                }),
         };
 
         let status = if let Some(diagnostic) = &self.current_diagnostic {
-            let message = diagnostic.message.split('\n').next().unwrap().to_string();
+            let message = diagnostic
+                .message
+                .split_once('\n')
+                .map_or(&*diagnostic.message, |(first, _)| first);
             Some(
-                Button::new("diagnostic_message", message)
+                Button::new("diagnostic_message", SharedString::new(message))
                     .label_size(LabelSize::Small)
                     .tooltip(|window, cx| {
                         Tooltip::for_action(