Fix edit prediction disablement with `"disable_ai": true` setting (#35513)

Michael Sloan created

Even after #35327 edit predictions were still being queried and shown
after setting `"disable_ai": true`

Also moves `DisableAiSettings` to the `project` crate so that it gets
included in tests via existing use of `Project::init_settings(cx)`.

Release Notes:

- Fixed `"disable_ai": true` setting disabling edit predictions.

Change summary

Cargo.lock                                                      |  1 
crates/agent_ui/src/agent_panel.rs                              |  4 
crates/agent_ui/src/agent_ui.rs                                 |  3 
crates/agent_ui/src/inline_assistant.rs                         |  4 
crates/client/src/client.rs                                     | 28 --
crates/copilot/src/copilot.rs                                   |  2 
crates/editor/src/editor.rs                                     | 15 +
crates/git_ui/Cargo.toml                                        |  1 
crates/git_ui/src/commit_modal.rs                               |  2 
crates/git_ui/src/git_panel.rs                                  |  7 
crates/inline_completion_button/Cargo.toml                      |  1 
crates/inline_completion_button/src/inline_completion_button.rs |  3 
crates/onboarding/src/ai_setup_page.rs                          |  6 
crates/project/src/project.rs                                   | 30 ++
crates/welcome/src/welcome.rs                                   |  3 
crates/zed/src/zed/quick_action_bar.rs                          |  2 
crates/zeta/src/init.rs                                         |  2 
17 files changed, 60 insertions(+), 54 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -6315,7 +6315,6 @@ dependencies = [
  "buffer_diff",
  "call",
  "chrono",
- "client",
  "cloud_llm_client",
  "collections",
  "command_palette_hooks",

crates/agent_ui/src/agent_panel.rs 🔗

@@ -43,7 +43,7 @@ use anyhow::{Result, anyhow};
 use assistant_context::{AssistantContext, ContextEvent, ContextSummary};
 use assistant_slash_command::SlashCommandWorkingSet;
 use assistant_tool::ToolWorkingSet;
-use client::{DisableAiSettings, UserStore, zed_urls};
+use client::{UserStore, zed_urls};
 use cloud_llm_client::{CompletionIntent, Plan, UsageLimit};
 use editor::{Anchor, AnchorRangeExt as _, Editor, EditorEvent, MultiBuffer};
 use feature_flags::{self, FeatureFlagAppExt};
@@ -58,7 +58,7 @@ use language::LanguageRegistry;
 use language_model::{
     ConfigurationError, ConfiguredModel, LanguageModelProviderTosView, LanguageModelRegistry,
 };
-use project::{Project, ProjectPath, Worktree};
+use project::{DisableAiSettings, Project, ProjectPath, Worktree};
 use prompt_store::{PromptBuilder, PromptStore, UserPromptId};
 use rules_library::{RulesLibrary, open_rules_library};
 use search::{BufferSearchBar, buffer_search};

crates/agent_ui/src/agent_ui.rs 🔗

@@ -31,7 +31,7 @@ use std::sync::Arc;
 use agent::{Thread, ThreadId};
 use agent_settings::{AgentProfileId, AgentSettings, LanguageModelSelection};
 use assistant_slash_command::SlashCommandRegistry;
-use client::{Client, DisableAiSettings};
+use client::Client;
 use command_palette_hooks::CommandPaletteFilter;
 use feature_flags::FeatureFlagAppExt as _;
 use fs::Fs;
@@ -40,6 +40,7 @@ use language::LanguageRegistry;
 use language_model::{
     ConfiguredModel, LanguageModel, LanguageModelId, LanguageModelProviderId, LanguageModelRegistry,
 };
+use project::DisableAiSettings;
 use prompt_store::PromptBuilder;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};

crates/agent_ui/src/inline_assistant.rs 🔗

@@ -16,7 +16,7 @@ use agent::{
 };
 use agent_settings::AgentSettings;
 use anyhow::{Context as _, Result};
-use client::{DisableAiSettings, telemetry::Telemetry};
+use client::telemetry::Telemetry;
 use collections::{HashMap, HashSet, VecDeque, hash_map};
 use editor::SelectionEffects;
 use editor::{
@@ -39,7 +39,7 @@ use language_model::{
 };
 use multi_buffer::MultiBufferRow;
 use parking_lot::Mutex;
-use project::{CodeAction, LspAction, Project, ProjectTransaction};
+use project::{CodeAction, DisableAiSettings, LspAction, Project, ProjectTransaction};
 use prompt_store::{PromptBuilder, PromptStore};
 use settings::{Settings, SettingsStore};
 use telemetry_events::{AssistantEventData, AssistantKind, AssistantPhase};

crates/client/src/client.rs 🔗

@@ -150,7 +150,6 @@ impl Settings for ProxySettings {
 
 pub fn init_settings(cx: &mut App) {
     TelemetrySettings::register(cx);
-    DisableAiSettings::register(cx);
     ClientSettings::register(cx);
     ProxySettings::register(cx);
 }
@@ -539,33 +538,6 @@ impl settings::Settings for TelemetrySettings {
     }
 }
 
-/// Whether to disable all AI features in Zed.
-///
-/// Default: false
-#[derive(Copy, Clone, Debug)]
-pub struct DisableAiSettings {
-    pub disable_ai: bool,
-}
-
-impl settings::Settings for DisableAiSettings {
-    const KEY: Option<&'static str> = Some("disable_ai");
-
-    type FileContent = Option<bool>;
-
-    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
-        Ok(Self {
-            disable_ai: sources
-                .user
-                .or(sources.server)
-                .copied()
-                .flatten()
-                .unwrap_or(sources.default.ok_or_else(Self::missing_default)?),
-        })
-    }
-
-    fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
-}
-
 impl Client {
     pub fn new(
         clock: Arc<dyn SystemClock>,

crates/copilot/src/copilot.rs 🔗

@@ -6,7 +6,6 @@ mod sign_in;
 use crate::sign_in::initiate_sign_in_within_workspace;
 use ::fs::Fs;
 use anyhow::{Context as _, Result, anyhow};
-use client::DisableAiSettings;
 use collections::{HashMap, HashSet};
 use command_palette_hooks::CommandPaletteFilter;
 use futures::{Future, FutureExt, TryFutureExt, channel::oneshot, future::Shared};
@@ -24,6 +23,7 @@ use language::{
 use lsp::{LanguageServer, LanguageServerBinary, LanguageServerId, LanguageServerName};
 use node_runtime::NodeRuntime;
 use parking_lot::Mutex;
+use project::DisableAiSettings;
 use request::StatusNotification;
 use serde_json::json;
 use settings::Settings;

crates/editor/src/editor.rs 🔗

@@ -83,7 +83,7 @@ use aho_corasick::AhoCorasick;
 use anyhow::{Context as _, Result, anyhow};
 use blink_manager::BlinkManager;
 use buffer_diff::DiffHunkStatus;
-use client::{Collaborator, DisableAiSettings, ParticipantIndex};
+use client::{Collaborator, ParticipantIndex};
 use clock::{AGENT_REPLICA_ID, ReplicaId};
 use code_context_menus::{
     AvailableCodeAction, CodeActionContents, CodeActionsItem, CodeActionsMenu, CodeContextMenu,
@@ -148,8 +148,8 @@ use parking_lot::Mutex;
 use persistence::DB;
 use project::{
     BreakpointWithPosition, CodeAction, Completion, CompletionIntent, CompletionResponse,
-    CompletionSource, DocumentHighlight, InlayHint, Location, LocationLink, PrepareRenameResponse,
-    Project, ProjectItem, ProjectPath, ProjectTransaction, TaskSourceKind,
+    CompletionSource, DisableAiSettings, DocumentHighlight, InlayHint, Location, LocationLink,
+    PrepareRenameResponse, Project, ProjectItem, ProjectPath, ProjectTransaction, TaskSourceKind,
     debugger::breakpoint_store::Breakpoint,
     debugger::{
         breakpoint_store::{
@@ -6995,6 +6995,10 @@ impl Editor {
         window: &mut Window,
         cx: &mut Context<Self>,
     ) -> Option<()> {
+        if DisableAiSettings::get_global(cx).disable_ai {
+            return None;
+        }
+
         let provider = self.edit_prediction_provider()?;
         let cursor = self.selections.newest_anchor().head();
         let (buffer, cursor_buffer_position) =
@@ -7052,6 +7056,7 @@ impl Editor {
     pub fn update_edit_prediction_settings(&mut self, cx: &mut Context<Self>) {
         if self.edit_prediction_provider.is_none() || DisableAiSettings::get_global(cx).disable_ai {
             self.edit_prediction_settings = EditPredictionSettings::Disabled;
+            self.discard_inline_completion(false, cx);
         } else {
             let selection = self.selections.newest_anchor();
             let cursor = selection.head();
@@ -7669,6 +7674,10 @@ impl Editor {
         _window: &mut Window,
         cx: &mut Context<Self>,
     ) -> Option<()> {
+        if DisableAiSettings::get_global(cx).disable_ai {
+            return None;
+        }
+
         let selection = self.selections.newest_anchor();
         let cursor = selection.head();
         let multibuffer = self.buffer.read(cx).snapshot(cx);

crates/git_ui/Cargo.toml 🔗

@@ -23,7 +23,6 @@ askpass.workspace = true
 buffer_diff.workspace = true
 call.workspace = true
 chrono.workspace = true
-client.workspace = true
 cloud_llm_client.workspace = true
 collections.workspace = true
 command_palette_hooks.workspace = true

crates/git_ui/src/commit_modal.rs 🔗

@@ -1,9 +1,9 @@
 use crate::branch_picker::{self, BranchList};
 use crate::git_panel::{GitPanel, commit_message_editor};
-use client::DisableAiSettings;
 use git::repository::CommitOptions;
 use git::{Amend, Commit, GenerateCommitMessage, Signoff};
 use panel::{panel_button, panel_editor_style};
+use project::DisableAiSettings;
 use settings::Settings;
 use ui::{
     ContextMenu, KeybindingHint, PopoverMenu, PopoverMenuHandle, SplitButton, Tooltip, prelude::*,

crates/git_ui/src/git_panel.rs 🔗

@@ -12,7 +12,6 @@ use crate::{
 use agent_settings::AgentSettings;
 use anyhow::Context as _;
 use askpass::AskPassDelegate;
-use client::DisableAiSettings;
 use db::kvp::KEY_VALUE_STORE;
 use editor::{
     Editor, EditorElement, EditorMode, EditorSettings, MultiBuffer, ShowScrollbar,
@@ -51,10 +50,9 @@ use panel::{
     PanelHeader, panel_button, panel_editor_container, panel_editor_style, panel_filled_button,
     panel_icon_button,
 };
-use project::git_store::{RepositoryEvent, RepositoryId};
 use project::{
-    Fs, Project, ProjectPath,
-    git_store::{GitStoreEvent, Repository},
+    DisableAiSettings, Fs, Project, ProjectPath,
+    git_store::{GitStoreEvent, Repository, RepositoryEvent, RepositoryId},
 };
 use serde::{Deserialize, Serialize};
 use settings::{Settings, SettingsStore};
@@ -5115,7 +5113,6 @@ mod tests {
             language::init(cx);
             editor::init(cx);
             Project::init_settings(cx);
-            client::DisableAiSettings::register(cx);
             crate::init(cx);
         });
     }

crates/inline_completion_button/Cargo.toml 🔗

@@ -25,6 +25,7 @@ indoc.workspace = true
 inline_completion.workspace = true
 language.workspace = true
 paths.workspace = true
+project.workspace = true
 regex.workspace = true
 settings.workspace = true
 supermaven.workspace = true

crates/inline_completion_button/src/inline_completion_button.rs 🔗

@@ -1,5 +1,5 @@
 use anyhow::Result;
-use client::{DisableAiSettings, UserStore, zed_urls};
+use client::{UserStore, zed_urls};
 use cloud_llm_client::UsageLimit;
 use copilot::{Copilot, Status};
 use editor::{
@@ -19,6 +19,7 @@ use language::{
     EditPredictionsMode, File, Language,
     language_settings::{self, AllLanguageSettings, EditPredictionProvider, all_language_settings},
 };
+use project::DisableAiSettings;
 use regex::Regex;
 use settings::{Settings, SettingsStore, update_settings_file};
 use std::{

crates/onboarding/src/ai_setup_page.rs 🔗

@@ -1,22 +1,20 @@
 use std::sync::Arc;
 
 use ai_onboarding::{AiUpsellCard, SignInStatus};
-use client::DisableAiSettings;
 use fs::Fs;
 use gpui::{
     Action, AnyView, App, DismissEvent, EventEmitter, FocusHandle, Focusable, Window, prelude::*,
 };
 use itertools;
-
 use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry};
+use project::DisableAiSettings;
 use settings::{Settings, update_settings_file};
 use ui::{
     Badge, ButtonLike, Divider, Modal, ModalFooter, ModalHeader, Section, SwitchField, ToggleState,
     prelude::*,
 };
-use workspace::ModalView;
-
 use util::ResultExt;
+use workspace::ModalView;
 use zed_actions::agent::OpenSettings;
 
 use crate::Onboarding;

crates/project/src/project.rs 🔗

@@ -97,7 +97,7 @@ use rpc::{
 };
 use search::{SearchInputKind, SearchQuery, SearchResult};
 use search_history::SearchHistory;
-use settings::{InvalidSettingsError, Settings, SettingsLocation, SettingsStore};
+use settings::{InvalidSettingsError, Settings, SettingsLocation, SettingsSources, SettingsStore};
 use smol::channel::Receiver;
 use snippet::Snippet;
 use snippet_provider::SnippetProvider;
@@ -942,10 +942,38 @@ pub enum PulledDiagnostics {
     },
 }
 
+/// Whether to disable all AI features in Zed.
+///
+/// Default: false
+#[derive(Copy, Clone, Debug)]
+pub struct DisableAiSettings {
+    pub disable_ai: bool,
+}
+
+impl settings::Settings for DisableAiSettings {
+    const KEY: Option<&'static str> = Some("disable_ai");
+
+    type FileContent = Option<bool>;
+
+    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
+        Ok(Self {
+            disable_ai: sources
+                .user
+                .or(sources.server)
+                .copied()
+                .flatten()
+                .unwrap_or(sources.default.ok_or_else(Self::missing_default)?),
+        })
+    }
+
+    fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
+}
+
 impl Project {
     pub fn init_settings(cx: &mut App) {
         WorktreeSettings::register(cx);
         ProjectSettings::register(cx);
+        DisableAiSettings::register(cx);
     }
 
     pub fn init(client: &Arc<Client>, cx: &mut App) {

crates/welcome/src/welcome.rs 🔗

@@ -1,10 +1,11 @@
-use client::{DisableAiSettings, TelemetrySettings, telemetry::Telemetry};
+use client::{TelemetrySettings, telemetry::Telemetry};
 use db::kvp::KEY_VALUE_STORE;
 use gpui::{
     Action, App, Context, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement,
     ParentElement, Render, Styled, Subscription, Task, WeakEntity, Window, actions, svg,
 };
 use language::language_settings::{EditPredictionProvider, all_language_settings};
+use project::DisableAiSettings;
 use settings::{Settings, SettingsStore};
 use std::sync::Arc;
 use ui::{CheckboxWithLabel, ElevationIndex, Tooltip, prelude::*};

crates/zed/src/zed/quick_action_bar.rs 🔗

@@ -2,7 +2,6 @@ mod preview;
 mod repl_menu;
 
 use agent_settings::AgentSettings;
-use client::DisableAiSettings;
 use editor::actions::{
     AddSelectionAbove, AddSelectionBelow, CodeActionSource, DuplicateLineDown, GoToDiagnostic,
     GoToHunk, GoToPreviousDiagnostic, GoToPreviousHunk, MoveLineDown, MoveLineUp, SelectAll,
@@ -16,6 +15,7 @@ use gpui::{
     FocusHandle, Focusable, InteractiveElement, ParentElement, Render, Styled, Subscription,
     WeakEntity, Window, anchored, deferred, point,
 };
+use project::DisableAiSettings;
 use project::project_settings::DiagnosticSeverity;
 use search::{BufferSearchBar, buffer_search};
 use settings::{Settings, SettingsStore};

crates/zeta/src/init.rs 🔗

@@ -1,10 +1,10 @@
 use std::any::{Any, TypeId};
 
-use client::DisableAiSettings;
 use command_palette_hooks::CommandPaletteFilter;
 use feature_flags::{FeatureFlagAppExt as _, PredictEditsRateCompletionsFeatureFlag};
 use gpui::actions;
 use language::language_settings::{AllLanguageSettings, EditPredictionProvider};
+use project::DisableAiSettings;
 use settings::{Settings, SettingsStore, update_settings_file};
 use ui::App;
 use workspace::Workspace;