From 262750adc9a7862f0ea0c87dde2952563b5020ee Mon Sep 17 00:00:00 2001 From: "zed-zippy[bot]" <234243425+zed-zippy[bot]@users.noreply.github.com> Date: Wed, 17 Dec 2025 19:10:27 +0000 Subject: [PATCH] Fix a bug where switching the disable AI flag would cause a panic (#45050) (cherry-pick to preview) (#45140) Cherry-pick of #45050 to preview ---- Also quiet some noisy logs Release Notes: - N/A Co-authored-by: Mikayla Maki --- crates/agent/src/history_store.rs | 13 +++++-------- crates/agent_ui/Cargo.toml | 2 +- crates/agent_ui_v2/Cargo.toml | 7 +++++++ crates/search/src/buffer_search.rs | 5 ++++- crates/workspace/src/dock.rs | 14 +++++--------- crates/workspace/src/workspace.rs | 20 ++++++++++++++++---- crates/zed/Cargo.toml | 4 ++++ crates/zed/src/zed.rs | 25 +++++++++++++++++++++++-- 8 files changed, 65 insertions(+), 25 deletions(-) diff --git a/crates/agent/src/history_store.rs b/crates/agent/src/history_store.rs index 5a1b923d139060ed7df679a69d96928d03559c9d..c455f73316e3fc7a641fa8a31ac0ad766a2ae584 100644 --- a/crates/agent/src/history_store.rs +++ b/crates/agent/src/history_store.rs @@ -216,14 +216,10 @@ impl HistoryStore { } pub fn reload(&self, cx: &mut Context) { - let database_future = ThreadsDatabase::connect(cx); + let database_connection = ThreadsDatabase::connect(cx); cx.spawn(async move |this, cx| { - let threads = database_future - .await - .map_err(|err| anyhow!(err))? - .list_threads() - .await?; - + let database = database_connection.await; + let threads = database.map_err(|err| anyhow!(err))?.list_threads().await?; this.update(cx, |this, cx| { if this.recently_opened_entries.len() < MAX_RECENTLY_OPENED_ENTRIES { for thread in threads @@ -344,7 +340,8 @@ impl HistoryStore { fn load_recently_opened_entries(cx: &AsyncApp) -> Task>> { cx.background_spawn(async move { if cfg!(any(feature = "test-support", test)) { - anyhow::bail!("history store does not persist in tests"); + log::warn!("history store does not persist in tests"); + return Ok(VecDeque::new()); } let json = KEY_VALUE_STORE .read_kvp(RECENTLY_OPENED_THREADS_KEY)? diff --git a/crates/agent_ui/Cargo.toml b/crates/agent_ui/Cargo.toml index 38580b4d2c61597718d9fb718a20e52e84222481..8a9633e578a85323f2a289bd83c169a1f5d7f272 100644 --- a/crates/agent_ui/Cargo.toml +++ b/crates/agent_ui/Cargo.toml @@ -13,7 +13,7 @@ path = "src/agent_ui.rs" doctest = false [features] -test-support = ["assistant_text_thread/test-support", "eval_utils", "gpui/test-support", "language/test-support", "reqwest_client", "workspace/test-support"] +test-support = ["assistant_text_thread/test-support", "eval_utils", "gpui/test-support", "language/test-support", "reqwest_client", "workspace/test-support", "agent/test-support"] unit-eval = [] [dependencies] diff --git a/crates/agent_ui_v2/Cargo.toml b/crates/agent_ui_v2/Cargo.toml index f24ef47471cdcfe0910cf36c5e220c5276d5f6ae..2b2cf337adf578432d594ce14f2f58e5911c45fb 100644 --- a/crates/agent_ui_v2/Cargo.toml +++ b/crates/agent_ui_v2/Cargo.toml @@ -12,6 +12,10 @@ workspace = true path = "src/agent_ui_v2.rs" doctest = false +[features] +test-support = ["agent/test-support"] + + [dependencies] agent.workspace = true agent_servers.workspace = true @@ -38,3 +42,6 @@ time_format.workspace = true ui.workspace = true util.workspace = true workspace.workspace = true + +[dev-dependencies] +agent = { workspace = true, features = ["test-support"] } diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 686d385aa07accac168062fa598790b36e80199f..a39971ae30142101e7395900e6af1a8c1f114925 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -7,7 +7,6 @@ use crate::{ search_bar::{ActionButtonState, input_base_styles, render_action_button, render_text_input}, }; use any_vec::AnyVec; -use anyhow::Context as _; use collections::HashMap; use editor::{ DisplayPoint, Editor, EditorSettings, MultiBufferOffset, @@ -634,15 +633,19 @@ impl BufferSearchBar { .read(cx) .as_singleton() .expect("query editor should be backed by a singleton buffer"); + query_buffer .read(cx) .set_language_registry(languages.clone()); cx.spawn(async move |buffer_search_bar, cx| { + use anyhow::Context as _; + let regex_language = languages .language_for_name("regex") .await .context("loading regex language")?; + buffer_search_bar .update(cx, |buffer_search_bar, cx| { buffer_search_bar.regex_language = Some(regex_language); diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index edc5705a28ecd7d378c0f959ac82a6493c82d325..f084d0d34df0195b34a02d66077c7b01dcc960eb 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -1,5 +1,4 @@ use crate::persistence::model::DockData; -use crate::utility_pane::utility_slot_for_dock_position; use crate::{DraggedDock, Event, ModalLayer, Pane}; use crate::{Workspace, status_bar::StatusItemView}; use anyhow::Context as _; @@ -705,7 +704,7 @@ impl Dock { panel: &Entity, window: &mut Window, cx: &mut Context, - ) { + ) -> bool { if let Some(panel_ix) = self .panel_entries .iter() @@ -724,15 +723,12 @@ impl Dock { } } - let slot = utility_slot_for_dock_position(self.position); - if let Some(workspace) = self.workspace.upgrade() { - workspace.update(cx, |workspace, cx| { - workspace.clear_utility_pane_if_provider(slot, Entity::entity_id(panel), cx); - }); - } - self.panel_entries.remove(panel_ix); cx.notify(); + + true + } else { + false } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 41304fd77f1eff8d890ff21a3051e57ce3ab295e..870da3f0eb2250e93ddaeff731cd8cd2a1184c34 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -130,7 +130,9 @@ pub use workspace_settings::{ use zed_actions::{Spawn, feedback::FileBugReport}; use crate::{ - item::ItemBufferKind, notifications::NotificationId, utility_pane::UTILITY_PANE_MIN_WIDTH, + item::ItemBufferKind, + notifications::NotificationId, + utility_pane::{UTILITY_PANE_MIN_WIDTH, utility_slot_for_dock_position}, }; use crate::{ persistence::{ @@ -974,6 +976,7 @@ impl AppState { #[cfg(any(test, feature = "test-support"))] pub fn test(cx: &mut App) -> Arc { + use fs::Fs; use node_runtime::NodeRuntime; use session::Session; use settings::SettingsStore; @@ -984,6 +987,7 @@ impl AppState { } let fs = fs::FakeFs::new(cx.background_executor().clone()); + ::set_global(fs.clone(), cx); let languages = Arc::new(LanguageRegistry::test(cx.background_executor().clone())); let clock = Arc::new(clock::FakeSystemClock::new()); let http_client = http_client::FakeHttpClient::with_404_response(); @@ -1789,10 +1793,18 @@ impl Workspace { window: &mut Window, cx: &mut Context, ) { + let mut found_in_dock = None; for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] { - dock.update(cx, |dock, cx| { - dock.remove_panel(panel, window, cx); - }) + let found = dock.update(cx, |dock, cx| dock.remove_panel(panel, window, cx)); + + if found { + found_in_dock = Some(dock.clone()); + } + } + if let Some(found_in_dock) = found_in_dock { + let position = found_in_dock.read(cx).position(); + let slot = utility_slot_for_dock_position(position); + self.clear_utility_pane_if_provider(slot, Entity::entity_id(panel), cx); } } diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 141de1139fb571020377ef9b115ed8204bad100b..44fddf80f28b9383689e41ba35cfea5ecb4aa156 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -195,6 +195,10 @@ terminal_view = { workspace = true, features = ["test-support"] } tree-sitter-md.workspace = true tree-sitter-rust.workspace = true workspace = { workspace = true, features = ["test-support"] } +agent_ui = { workspace = true, features = ["test-support"] } +agent_ui_v2 = { workspace = true, features = ["test-support"] } +search = { workspace = true, features = ["test-support"] } + [package.metadata.bundle-dev] icon = ["resources/app-icon-dev@2x.png", "resources/app-icon-dev.png"] diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index c1d98936aa2ad20e6eef7f18bfed2d2c0615395a..ba768149bdae2943912f6ac39035042d63e3198d 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -705,7 +705,6 @@ fn setup_or_teardown_ai_panel( .disable_ai || cfg!(test); let existing_panel = workspace.panel::

(cx); - match (disable_ai, existing_panel) { (false, None) => cx.spawn_in(window, async move |workspace, cx| { let panel = load_panel(workspace.clone(), cx.clone()).await?; @@ -2311,7 +2310,7 @@ mod tests { use project::{Project, ProjectPath}; use semver::Version; use serde_json::json; - use settings::{SettingsStore, watch_config_file}; + use settings::{SaturatingBool, SettingsStore, watch_config_file}; use std::{ path::{Path, PathBuf}, time::Duration, @@ -5155,6 +5154,28 @@ mod tests { ); } + #[gpui::test] + async fn test_disable_ai_crash(cx: &mut gpui::TestAppContext) { + let app_state = init_test(cx); + cx.update(init); + let project = Project::test(app_state.fs.clone(), [], cx).await; + let _window = cx.add_window(|window, cx| Workspace::test_new(project, window, cx)); + + cx.run_until_parked(); + + cx.update(|cx| { + SettingsStore::update_global(cx, |settings_store, cx| { + settings_store.update_user_settings(cx, |settings| { + settings.disable_ai = Some(SaturatingBool(true)); + }); + }); + }); + + cx.run_until_parked(); + + // If this panics, the test has failed + } + #[gpui::test] async fn test_prefer_focused_window(cx: &mut gpui::TestAppContext) { let app_state = init_test(cx);