Remove redundant Cargo diagnostics settings (#36795)

Kirill Bulatov created

Removes `diagnostics.cargo.fetch_cargo_diagnostics` settings as those
are not needed for the flycheck diagnostics to run.
This setting disabled `checkOnSave` in rust-analyzer and allowed to
update diagnostics via flycheck in the project diagnostics editor with
the "refresh" button.

Instead, `"checkOnSave": false,` can be set manually as
https://zed.dev/docs/languages/rust#more-server-configuration example
shows and flycheck commands can be called manually from anywhere,
including the diagnostics panel, to refresh the diagnostics.

Release Notes:

- Removed redundant `diagnostics.cargo.fetch_cargo_diagnostics` settings

Change summary

Cargo.lock                                 |   1 
assets/settings/default.json               |   5 
crates/diagnostics/Cargo.toml              |   1 
crates/diagnostics/src/diagnostics.rs      | 127 -----------------------
crates/diagnostics/src/toolbar_controls.rs |  38 +-----
crates/languages/src/rust.rs               |  14 --
crates/project/src/project_settings.rs     |  22 ----
docs/src/languages/rust.md                 |  17 ---
8 files changed, 14 insertions(+), 211 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -4685,7 +4685,6 @@ dependencies = [
  "component",
  "ctor",
  "editor",
- "futures 0.3.31",
  "gpui",
  "indoc",
  "language",

assets/settings/default.json 🔗

@@ -1133,11 +1133,6 @@
       // The minimum severity of the diagnostics to show inline.
       // Inherits editor's diagnostics' max severity settings when `null`.
       "max_severity": null
-    },
-    "cargo": {
-      // When enabled, Zed disables rust-analyzer's check on save and starts to query
-      // Cargo diagnostics separately.
-      "fetch_cargo_diagnostics": false
     }
   },
   // Files or globs of files that will be excluded by Zed entirely. They will be skipped during file

crates/diagnostics/Cargo.toml 🔗

@@ -18,7 +18,6 @@ collections.workspace = true
 component.workspace = true
 ctor.workspace = true
 editor.workspace = true
-futures.workspace = true
 gpui.workspace = true
 indoc.workspace = true
 language.workspace = true

crates/diagnostics/src/diagnostics.rs 🔗

@@ -13,7 +13,6 @@ use editor::{
     DEFAULT_MULTIBUFFER_CONTEXT, Editor, EditorEvent, ExcerptRange, MultiBuffer, PathKey,
     display_map::{BlockPlacement, BlockProperties, BlockStyle, CustomBlockId},
 };
-use futures::future::join_all;
 use gpui::{
     AnyElement, AnyView, App, AsyncApp, Context, Entity, EventEmitter, FocusHandle, Focusable,
     Global, InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled,
@@ -24,7 +23,6 @@ use language::{
 };
 use project::{
     DiagnosticSummary, Project, ProjectPath,
-    lsp_store::rust_analyzer_ext::{cancel_flycheck, run_flycheck},
     project_settings::{DiagnosticSeverity, ProjectSettings},
 };
 use settings::Settings;
@@ -79,17 +77,10 @@ pub(crate) struct ProjectDiagnosticsEditor {
     paths_to_update: BTreeSet<ProjectPath>,
     include_warnings: bool,
     update_excerpts_task: Option<Task<Result<()>>>,
-    cargo_diagnostics_fetch: CargoDiagnosticsFetchState,
     diagnostic_summary_update: Task<()>,
     _subscription: Subscription,
 }
 
-struct CargoDiagnosticsFetchState {
-    fetch_task: Option<Task<()>>,
-    cancel_task: Option<Task<()>>,
-    diagnostic_sources: Arc<Vec<ProjectPath>>,
-}
-
 impl EventEmitter<EditorEvent> for ProjectDiagnosticsEditor {}
 
 const DIAGNOSTICS_UPDATE_DELAY: Duration = Duration::from_millis(50);
@@ -260,11 +251,7 @@ impl ProjectDiagnosticsEditor {
                 )
             });
             this.diagnostics.clear();
-            this.update_all_diagnostics(false, window, cx);
-        })
-        .detach();
-        cx.observe_release(&cx.entity(), |editor, _, cx| {
-            editor.stop_cargo_diagnostics_fetch(cx);
+            this.update_all_excerpts(window, cx);
         })
         .detach();
 
@@ -281,15 +268,10 @@ impl ProjectDiagnosticsEditor {
             editor,
             paths_to_update: Default::default(),
             update_excerpts_task: None,
-            cargo_diagnostics_fetch: CargoDiagnosticsFetchState {
-                fetch_task: None,
-                cancel_task: None,
-                diagnostic_sources: Arc::new(Vec::new()),
-            },
             diagnostic_summary_update: Task::ready(()),
             _subscription: project_event_subscription,
         };
-        this.update_all_diagnostics(true, window, cx);
+        this.update_all_excerpts(window, cx);
         this
     }
 
@@ -373,20 +355,10 @@ impl ProjectDiagnosticsEditor {
         window: &mut Window,
         cx: &mut Context<Self>,
     ) {
-        let fetch_cargo_diagnostics = ProjectSettings::get_global(cx)
-            .diagnostics
-            .fetch_cargo_diagnostics();
-
-        if fetch_cargo_diagnostics {
-            if self.cargo_diagnostics_fetch.fetch_task.is_some() {
-                self.stop_cargo_diagnostics_fetch(cx);
-            } else {
-                self.update_all_diagnostics(false, window, cx);
-            }
-        } else if self.update_excerpts_task.is_some() {
+        if self.update_excerpts_task.is_some() {
             self.update_excerpts_task = None;
         } else {
-            self.update_all_diagnostics(false, window, cx);
+            self.update_all_excerpts(window, cx);
         }
         cx.notify();
     }
@@ -404,73 +376,6 @@ impl ProjectDiagnosticsEditor {
         }
     }
 
-    fn update_all_diagnostics(
-        &mut self,
-        first_launch: bool,
-        window: &mut Window,
-        cx: &mut Context<Self>,
-    ) {
-        let cargo_diagnostics_sources = self.cargo_diagnostics_sources(cx);
-        if cargo_diagnostics_sources.is_empty() {
-            self.update_all_excerpts(window, cx);
-        } else if first_launch && !self.summary.is_empty() {
-            self.update_all_excerpts(window, cx);
-        } else {
-            self.fetch_cargo_diagnostics(Arc::new(cargo_diagnostics_sources), cx);
-        }
-    }
-
-    fn fetch_cargo_diagnostics(
-        &mut self,
-        diagnostics_sources: Arc<Vec<ProjectPath>>,
-        cx: &mut Context<Self>,
-    ) {
-        let project = self.project.clone();
-        self.cargo_diagnostics_fetch.cancel_task = None;
-        self.cargo_diagnostics_fetch.fetch_task = None;
-        self.cargo_diagnostics_fetch.diagnostic_sources = diagnostics_sources.clone();
-        if self.cargo_diagnostics_fetch.diagnostic_sources.is_empty() {
-            return;
-        }
-
-        self.cargo_diagnostics_fetch.fetch_task = Some(cx.spawn(async move |editor, cx| {
-            let mut fetch_tasks = Vec::new();
-            for buffer_path in diagnostics_sources.iter().cloned() {
-                if cx
-                    .update(|cx| {
-                        fetch_tasks.push(run_flycheck(project.clone(), Some(buffer_path), cx));
-                    })
-                    .is_err()
-                {
-                    break;
-                }
-            }
-
-            let _ = join_all(fetch_tasks).await;
-            editor
-                .update(cx, |editor, _| {
-                    editor.cargo_diagnostics_fetch.fetch_task = None;
-                })
-                .ok();
-        }));
-    }
-
-    fn stop_cargo_diagnostics_fetch(&mut self, cx: &mut App) {
-        self.cargo_diagnostics_fetch.fetch_task = None;
-        let mut cancel_gasks = Vec::new();
-        for buffer_path in std::mem::take(&mut self.cargo_diagnostics_fetch.diagnostic_sources)
-            .iter()
-            .cloned()
-        {
-            cancel_gasks.push(cancel_flycheck(self.project.clone(), Some(buffer_path), cx));
-        }
-
-        self.cargo_diagnostics_fetch.cancel_task = Some(cx.background_spawn(async move {
-            let _ = join_all(cancel_gasks).await;
-            log::info!("Finished fetching cargo diagnostics");
-        }));
-    }
-
     /// Enqueue an update of all excerpts. Updates all paths that either
     /// currently have diagnostics or are currently present in this view.
     fn update_all_excerpts(&mut self, window: &mut Window, cx: &mut Context<Self>) {
@@ -695,30 +600,6 @@ impl ProjectDiagnosticsEditor {
             })
         })
     }
-
-    pub fn cargo_diagnostics_sources(&self, cx: &App) -> Vec<ProjectPath> {
-        let fetch_cargo_diagnostics = ProjectSettings::get_global(cx)
-            .diagnostics
-            .fetch_cargo_diagnostics();
-        if !fetch_cargo_diagnostics {
-            return Vec::new();
-        }
-        self.project
-            .read(cx)
-            .worktrees(cx)
-            .filter_map(|worktree| {
-                let _cargo_toml_entry = worktree.read(cx).entry_for_path("Cargo.toml")?;
-                let rust_file_entry = worktree.read(cx).entries(false, 0).find(|entry| {
-                    entry
-                        .path
-                        .extension()
-                        .and_then(|extension| extension.to_str())
-                        == Some("rs")
-                })?;
-                self.project.read(cx).path_for_entry(rust_file_entry.id, cx)
-            })
-            .collect()
-    }
 }
 
 impl Focusable for ProjectDiagnosticsEditor {

crates/diagnostics/src/toolbar_controls.rs 🔗

@@ -1,5 +1,3 @@
-use std::sync::Arc;
-
 use crate::{ProjectDiagnosticsEditor, ToggleDiagnosticsRefresh};
 use gpui::{Context, Entity, EventEmitter, ParentElement, Render, WeakEntity, Window};
 use ui::prelude::*;
@@ -15,26 +13,18 @@ impl Render for ToolbarControls {
         let mut include_warnings = false;
         let mut has_stale_excerpts = false;
         let mut is_updating = false;
-        let cargo_diagnostics_sources = Arc::new(self.diagnostics().map_or(Vec::new(), |editor| {
-            editor.read(cx).cargo_diagnostics_sources(cx)
-        }));
-        let fetch_cargo_diagnostics = !cargo_diagnostics_sources.is_empty();
 
         if let Some(editor) = self.diagnostics() {
             let diagnostics = editor.read(cx);
             include_warnings = diagnostics.include_warnings;
             has_stale_excerpts = !diagnostics.paths_to_update.is_empty();
-            is_updating = if fetch_cargo_diagnostics {
-                diagnostics.cargo_diagnostics_fetch.fetch_task.is_some()
-            } else {
-                diagnostics.update_excerpts_task.is_some()
-                    || diagnostics
-                        .project
-                        .read(cx)
-                        .language_servers_running_disk_based_diagnostics(cx)
-                        .next()
-                        .is_some()
-            };
+            is_updating = diagnostics.update_excerpts_task.is_some()
+                || diagnostics
+                    .project
+                    .read(cx)
+                    .language_servers_running_disk_based_diagnostics(cx)
+                    .next()
+                    .is_some();
         }
 
         let tooltip = if include_warnings {
@@ -64,7 +54,6 @@ impl Render for ToolbarControls {
                             .on_click(cx.listener(move |toolbar_controls, _, _, cx| {
                                 if let Some(diagnostics) = toolbar_controls.diagnostics() {
                                     diagnostics.update(cx, |diagnostics, cx| {
-                                        diagnostics.stop_cargo_diagnostics_fetch(cx);
                                         diagnostics.update_excerpts_task = None;
                                         cx.notify();
                                     });
@@ -76,7 +65,7 @@ impl Render for ToolbarControls {
                         IconButton::new("refresh-diagnostics", IconName::ArrowCircle)
                             .icon_color(Color::Info)
                             .shape(IconButtonShape::Square)
-                            .disabled(!has_stale_excerpts && !fetch_cargo_diagnostics)
+                            .disabled(!has_stale_excerpts)
                             .tooltip(Tooltip::for_action_title(
                                 "Refresh diagnostics",
                                 &ToggleDiagnosticsRefresh,
@@ -84,17 +73,8 @@ impl Render for ToolbarControls {
                             .on_click(cx.listener({
                                 move |toolbar_controls, _, window, cx| {
                                     if let Some(diagnostics) = toolbar_controls.diagnostics() {
-                                        let cargo_diagnostics_sources =
-                                            Arc::clone(&cargo_diagnostics_sources);
                                         diagnostics.update(cx, move |diagnostics, cx| {
-                                            if fetch_cargo_diagnostics {
-                                                diagnostics.fetch_cargo_diagnostics(
-                                                    cargo_diagnostics_sources,
-                                                    cx,
-                                                );
-                                            } else {
-                                                diagnostics.update_all_excerpts(window, cx);
-                                            }
+                                            diagnostics.update_all_excerpts(window, cx);
                                         });
                                     }
                                 }

crates/languages/src/rust.rs 🔗

@@ -510,20 +510,6 @@ impl LspAdapter for RustLspAdapter {
             }
         }
 
-        let cargo_diagnostics_fetched_separately = ProjectSettings::get_global(cx)
-            .diagnostics
-            .fetch_cargo_diagnostics();
-        if cargo_diagnostics_fetched_separately {
-            let disable_check_on_save = json!({
-                "checkOnSave": false,
-            });
-            if let Some(initialization_options) = &mut original.initialization_options {
-                merge_json_value_into(disable_check_on_save, initialization_options);
-            } else {
-                original.initialization_options = Some(disable_check_on_save);
-            }
-        }
-
         Ok(original)
     }
 }

crates/project/src/project_settings.rs 🔗

@@ -181,17 +181,6 @@ pub struct DiagnosticsSettings {
 
     /// Settings for showing inline diagnostics.
     pub inline: InlineDiagnosticsSettings,
-
-    /// Configuration, related to Rust language diagnostics.
-    pub cargo: Option<CargoDiagnosticsSettings>,
-}
-
-impl DiagnosticsSettings {
-    pub fn fetch_cargo_diagnostics(&self) -> bool {
-        self.cargo
-            .as_ref()
-            .is_some_and(|cargo_diagnostics| cargo_diagnostics.fetch_cargo_diagnostics)
-    }
 }
 
 #[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema)]
@@ -258,7 +247,6 @@ impl Default for DiagnosticsSettings {
             include_warnings: true,
             lsp_pull_diagnostics: LspPullDiagnosticsSettings::default(),
             inline: InlineDiagnosticsSettings::default(),
-            cargo: None,
         }
     }
 }
@@ -292,16 +280,6 @@ impl Default for GlobalLspSettings {
     }
 }
 
-#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
-pub struct CargoDiagnosticsSettings {
-    /// When enabled, Zed disables rust-analyzer's check on save and starts to query
-    /// Cargo diagnostics separately.
-    ///
-    /// Default: false
-    #[serde(default)]
-    pub fetch_cargo_diagnostics: bool,
-}
-
 #[derive(
     Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, JsonSchema,
 )]

docs/src/languages/rust.md 🔗

@@ -136,22 +136,7 @@ This is enabled by default and can be configured as
 ## Manual Cargo Diagnostics fetch
 
 By default, rust-analyzer has `checkOnSave: true` enabled, which causes every buffer save to trigger a `cargo check --workspace --all-targets` command.
-For lager projects this might introduce excessive wait times, so a more fine-grained triggering could be enabled by altering the
-
-```json
-"diagnostics": {
-  "cargo": {
-    // When enabled, Zed disables rust-analyzer's check on save and starts to query
-    // Cargo diagnostics separately.
-    "fetch_cargo_diagnostics": false
-  }
-}
-```
-
-default settings.
-
-This will stop rust-analyzer from running `cargo check ...` on save, yet still allow to run
-`editor: run/clear/cancel flycheck` commands in Rust files to refresh cargo diagnostics; the project diagnostics editor will also refresh cargo diagnostics with `editor: run flycheck` command when the setting is enabled.
+If disabled with `checkOnSave: false` (see the example of the server configuration json above), it's still possible to fetch the diagnostics manually, with the `editor: run/clear/cancel flycheck` commands in Rust files to refresh cargo diagnostics; the project diagnostics editor will also refresh cargo diagnostics with `editor: run flycheck` command when the setting is enabled.
 
 ## More server configuration