Settings refactor (#38367)

Co-Authored-By: Ben K <ben@zed.dev>
Co-Authored-By: Anthony <anthony@zed.dev>
Co-Authored-By: Mikayla <mikayla@zed.dev>

Release Notes:

- settings: Major internal changes to settings. The primary user-facing
effect is that some settings which did not make sense in project
settings files are no-longer read from there. (For example the inline
blame settings)

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
Co-authored-by: Anthony <anthony@zed.dev>
This commit is contained in:
Conrad Irwin
2025-09-18 16:47:23 +00:00
committed by GitHub
co-authored by Ben Kunkle Mikayla Maki Anthony
parent 0a9023bce0
commit fcdab160f9
219 changed files with 11697 additions and 11894 deletions
+2 -2
View File
@@ -7,7 +7,7 @@ use acp_thread::{AgentConnection, Plan};
use action_log::ActionLog;
use agent_client_protocol::{self as acp, PromptCapabilities};
use agent_servers::{AgentServer, AgentServerDelegate};
use agent_settings::{AgentProfileId, AgentSettings, CompletionMode, NotifyWhenAgentWaiting};
use agent_settings::{AgentProfileId, AgentSettings, CompletionMode};
use agent2::{DbThreadMetadata, HistoryEntry, HistoryEntryId, HistoryStore, NativeAgentServer};
use anyhow::{Result, anyhow, bail};
use arrayvec::ArrayVec;
@@ -35,7 +35,7 @@ use markdown::{HeadingLevelStyles, Markdown, MarkdownElement, MarkdownStyle};
use project::{Project, ProjectEntryId};
use prompt_store::{PromptId, PromptStore};
use rope::Point;
use settings::{Settings as _, SettingsStore};
use settings::{NotifyWhenAgentWaiting, Settings as _, SettingsStore};
use std::cell::RefCell;
use std::path::Path;
use std::sync::Arc;
+81 -92
View File
@@ -26,12 +26,8 @@ use language_model::{
};
use notifications::status_toast::{StatusToast, ToastIcon};
use project::{
agent_server_store::{
AgentServerCommand, AgentServerStore, AllAgentServersSettings, CLAUDE_CODE_NAME,
CustomAgentServerSettings, GEMINI_NAME,
},
agent_server_store::{AgentServerStore, CLAUDE_CODE_NAME, GEMINI_NAME},
context_server_store::{ContextServerConfiguration, ContextServerStatus, ContextServerStore},
project_settings::{ContextServerSettings, ProjectSettings},
};
use settings::{Settings, SettingsStore, update_settings_file};
use ui::{
@@ -419,8 +415,8 @@ impl AgentConfiguration {
always_allow_tool_actions,
move |state, _window, cx| {
let allow = state == &ToggleState::Selected;
update_settings_file::<AgentSettings>(fs.clone(), cx, move |settings, _| {
settings.set_always_allow_tool_actions(allow);
update_settings_file(fs.clone(), cx, move |settings, _| {
settings.agent.get_or_insert_default().set_always_allow_tool_actions(allow);
});
},
)
@@ -437,8 +433,11 @@ impl AgentConfiguration {
single_file_review,
move |state, _window, cx| {
let allow = state == &ToggleState::Selected;
update_settings_file::<AgentSettings>(fs.clone(), cx, move |settings, _| {
settings.set_single_file_review(allow);
update_settings_file(fs.clone(), cx, move |settings, _| {
settings
.agent
.get_or_insert_default()
.set_single_file_review(allow);
});
},
)
@@ -457,8 +456,8 @@ impl AgentConfiguration {
play_sound_when_agent_done,
move |state, _window, cx| {
let allow = state == &ToggleState::Selected;
update_settings_file::<AgentSettings>(fs.clone(), cx, move |settings, _| {
settings.set_play_sound_when_agent_done(allow);
update_settings_file(fs.clone(), cx, move |settings, _| {
settings.agent.get_or_insert_default().set_play_sound_when_agent_done(allow);
});
},
)
@@ -477,8 +476,8 @@ impl AgentConfiguration {
use_modifier_to_send,
move |state, _window, cx| {
let allow = state == &ToggleState::Selected;
update_settings_file::<AgentSettings>(fs.clone(), cx, move |settings, _| {
settings.set_use_modifier_to_send(allow);
update_settings_file(fs.clone(), cx, move |settings, _| {
settings.agent.get_or_insert_default().set_use_modifier_to_send(allow);
});
},
)
@@ -862,14 +861,14 @@ impl AgentConfiguration {
async move |cx| {
uninstall_extension_task.await?;
cx.update(|cx| {
update_settings_file::<ProjectSettings>(
update_settings_file(
fs.clone(),
cx,
{
let context_server_id =
context_server_id.clone();
move |settings, _| {
settings
settings.project
.context_servers
.remove(&context_server_id.0);
}
@@ -944,67 +943,53 @@ impl AgentConfiguration {
.flex_none()
.child(context_server_configuration_menu)
.child(
Switch::new("context-server-switch", is_running.into())
.color(SwitchColor::Accent)
.on_click({
let context_server_manager =
self.context_server_store.clone();
let fs = self.fs.clone();
Switch::new("context-server-switch", is_running.into())
.color(SwitchColor::Accent)
.on_click({
let context_server_manager = self.context_server_store.clone();
let fs = self.fs.clone();
move |state, _window, cx| {
let is_enabled = match state {
ToggleState::Unselected
| ToggleState::Indeterminate => {
context_server_manager.update(
cx,
|this, cx| {
this.stop_server(
&context_server_id,
cx,
)
.log_err();
},
);
false
}
ToggleState::Selected => {
context_server_manager.update(
cx,
|this, cx| {
if let Some(server) =
this.get_server(&context_server_id)
{
this.start_server(server, cx);
}
},
);
true
}
};
update_settings_file::<ProjectSettings>(
fs.clone(),
cx,
{
let context_server_id =
context_server_id.clone();
move |settings, _| {
settings
.context_servers
.entry(context_server_id.0)
.or_insert_with(|| {
ContextServerSettings::Extension {
enabled: is_enabled,
settings: serde_json::json!({}),
}
})
.set_enabled(is_enabled);
move |state, _window, cx| {
let is_enabled = match state {
ToggleState::Unselected
| ToggleState::Indeterminate => {
context_server_manager.update(cx, |this, cx| {
this.stop_server(&context_server_id, cx)
.log_err();
});
false
}
ToggleState::Selected => {
context_server_manager.update(cx, |this, cx| {
if let Some(server) =
this.get_server(&context_server_id)
{
this.start_server(server, cx);
}
},
);
}
}),
),
});
true
}
};
update_settings_file(fs.clone(), cx, {
let context_server_id = context_server_id.clone();
move |settings, _| {
settings
.project
.context_servers
.entry(context_server_id.0)
.or_insert_with(|| {
settings::ContextServerSettingsContent::Extension {
enabled: is_enabled,
settings: serde_json::json!({}),
}
})
.set_enabled(is_enabled);
}
});
}
}),
),
),
)
.map(|parent| {
@@ -1236,15 +1221,12 @@ fn show_unable_to_uninstall_extension_with_context_server(
let context_server_id = context_server_id.clone();
async move |_workspace_handle, cx| {
cx.update(|cx| {
update_settings_file::<ProjectSettings>(
fs,
cx,
move |settings, _| {
settings
.context_servers
.remove(&context_server_id.0);
},
);
update_settings_file(fs, cx, move |settings, _| {
settings
.project
.context_servers
.remove(&context_server_id.0);
});
})?;
anyhow::Ok(())
}
@@ -1282,7 +1264,7 @@ async fn open_new_agent_servers_entry_in_settings_editor(
let settings = cx.global::<SettingsStore>();
let mut unique_server_name = None;
let edits = settings.edits_for_update::<AllAgentServersSettings>(&text, |file| {
let edits = settings.edits_for_update(&text, |settings| {
let server_name: Option<SharedString> = (0..u8::MAX)
.map(|i| {
if i == 0 {
@@ -1291,20 +1273,27 @@ async fn open_new_agent_servers_entry_in_settings_editor(
format!("your_agent_{}", i).into()
}
})
.find(|name| !file.custom.contains_key(name));
.find(|name| {
!settings
.agent_servers
.as_ref()
.is_some_and(|agent_servers| agent_servers.custom.contains_key(name))
});
if let Some(server_name) = server_name {
unique_server_name = Some(server_name.clone());
file.custom.insert(
server_name,
CustomAgentServerSettings {
command: AgentServerCommand {
settings
.agent_servers
.get_or_insert_default()
.custom
.insert(
server_name,
settings::CustomAgentServerSettings {
path: "path_to_executable".into(),
args: vec![],
env: Some(HashMap::default()),
default_mode: None,
},
default_mode: None,
},
);
);
}
});
@@ -5,11 +5,8 @@ use collections::HashSet;
use fs::Fs;
use gpui::{DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Render, Task};
use language_model::LanguageModelRegistry;
use language_models::{
AllLanguageModelSettings, OpenAiCompatibleSettingsContent,
provider::open_ai_compatible::{AvailableModel, ModelCapabilities},
};
use settings::update_settings_file;
use language_models::provider::open_ai_compatible::{AvailableModel, ModelCapabilities};
use settings::{OpenAiCompatibleSettingsContent, update_settings_file};
use ui::{
Banner, Checkbox, KeyBinding, Modal, ModalFooter, ModalHeader, Section, ToggleState, prelude::*,
};
@@ -238,14 +235,19 @@ fn save_provider_to_settings(
task.await
.map_err(|_| "Failed to write API key to keychain")?;
cx.update(|cx| {
update_settings_file::<AllLanguageModelSettings>(fs, cx, |settings, _cx| {
settings.openai_compatible.get_or_insert_default().insert(
provider_name,
OpenAiCompatibleSettingsContent {
api_url,
available_models: models,
},
);
update_settings_file(fs, cx, |settings, _cx| {
settings
.language_models
.get_or_insert_default()
.openai_compatible
.get_or_insert_default()
.insert(
provider_name,
OpenAiCompatibleSettingsContent {
api_url,
available_models: models,
},
);
});
})
.ok();
@@ -422,18 +422,17 @@ impl ConfigureContextServerModal {
workspace.update(cx, |workspace, cx| {
let fs = workspace.app_state().fs.clone();
let original_server_id = self.original_server_id.clone();
update_settings_file::<ProjectSettings>(
fs.clone(),
cx,
move |project_settings, _| {
if let Some(original_id) = original_server_id {
if original_id != id {
project_settings.context_servers.remove(&original_id.0);
}
update_settings_file(fs.clone(), cx, move |current, _| {
if let Some(original_id) = original_server_id {
if original_id != id {
current.project.context_servers.remove(&original_id.0);
}
project_settings.context_servers.insert(id.0, settings);
},
);
}
current
.project
.context_servers
.insert(id.0, settings.into());
});
});
} else if let Some(existing_server) = existing_server {
self.context_server_store
@@ -1,14 +1,11 @@
use std::{collections::BTreeMap, sync::Arc};
use agent_settings::{
AgentProfileContent, AgentProfileId, AgentProfileSettings, AgentSettings, AgentSettingsContent,
ContextServerPresetContent,
};
use agent_settings::{AgentProfileId, AgentProfileSettings};
use assistant_tool::{ToolSource, ToolWorkingSet};
use fs::Fs;
use gpui::{App, Context, DismissEvent, Entity, EventEmitter, Focusable, Task, WeakEntity, Window};
use picker::{Picker, PickerDelegate};
use settings::update_settings_file;
use settings::{AgentProfileContent, ContextServerPresetContent, update_settings_file};
use ui::{ListItem, ListItemSpacing, prelude::*};
use util::ResultExt as _;
@@ -266,15 +263,19 @@ impl PickerDelegate for ToolPickerDelegate {
is_enabled
};
update_settings_file::<AgentSettings>(self.fs.clone(), cx, {
update_settings_file(self.fs.clone(), cx, {
let profile_id = self.profile_id.clone();
let default_profile = self.profile_settings.clone();
let server_id = server_id.clone();
let tool_name = tool_name.clone();
move |settings: &mut AgentSettingsContent, _cx| {
let profiles = settings.profiles.get_or_insert_default();
move |settings, _cx| {
let profiles = settings
.agent
.get_or_insert_default()
.profiles
.get_or_insert_default();
let profile = profiles
.entry(profile_id)
.entry(profile_id.0)
.or_insert_with(|| AgentProfileContent {
name: default_profile.name.into(),
tools: default_profile.tools,
+6 -9
View File
@@ -2,7 +2,6 @@ use crate::{
ModelUsageContext,
language_model_selector::{LanguageModelSelector, language_model_selector},
};
use agent_settings::AgentSettings;
use fs::Fs;
use gpui::{Entity, FocusHandle, SharedString};
use picker::popover_menu::PickerPopoverMenu;
@@ -39,14 +38,12 @@ impl AgentModelSelector {
let model_id = model.id().0.to_string();
match &model_usage_context {
ModelUsageContext::InlineAssistant => {
update_settings_file::<AgentSettings>(
fs.clone(),
cx,
move |settings, _cx| {
settings
.set_inline_assistant_model(provider.clone(), model_id);
},
);
update_settings_file(fs.clone(), cx, move |settings, _cx| {
settings
.agent
.get_or_insert_default()
.set_inline_assistant_model(provider.clone(), model_id);
});
}
}
},
+31 -31
View File
@@ -10,6 +10,9 @@ use project::agent_server_store::{
AgentServerCommand, AllAgentServersSettings, CLAUDE_CODE_NAME, GEMINI_NAME,
};
use serde::{Deserialize, Serialize};
use settings::{
DefaultAgentView as DefaultView, LanguageModelProviderSetting, LanguageModelSelection,
};
use zed_actions::OpenBrowser;
use zed_actions::agent::{OpenClaudeCodeOnboardingModal, ReauthenticateAgent};
@@ -33,7 +36,7 @@ use agent::{
history_store::{HistoryEntryId, HistoryStore},
thread_store::{TextThreadStore, ThreadStore},
};
use agent_settings::{AgentDockPosition, AgentSettings, DefaultView};
use agent_settings::AgentSettings;
use ai_onboarding::AgentPanelOnboarding;
use anyhow::{Result, anyhow};
use assistant_context::{AssistantContext, ContextEvent, ContextSummary};
@@ -1058,17 +1061,14 @@ impl AgentPanel {
match self.active_view.which_font_size_used() {
WhichFontSize::AgentFont => {
if persist {
update_settings_file::<ThemeSettings>(
self.fs.clone(),
cx,
move |settings, cx| {
let agent_font_size =
ThemeSettings::get_global(cx).agent_font_size(cx) + delta;
let _ = settings
.agent_font_size
.insert(Some(theme::clamp_font_size(agent_font_size).into()));
},
);
update_settings_file(self.fs.clone(), cx, move |settings, cx| {
let agent_font_size =
ThemeSettings::get_global(cx).agent_font_size(cx) + delta;
let _ = settings
.theme
.agent_font_size
.insert(Some(theme::clamp_font_size(agent_font_size).into()));
});
} else {
theme::adjust_agent_font_size(cx, |size| size + delta);
}
@@ -1089,8 +1089,8 @@ impl AgentPanel {
cx: &mut Context<Self>,
) {
if action.persist {
update_settings_file::<ThemeSettings>(self.fs.clone(), cx, move |settings, _| {
settings.agent_font_size = None;
update_settings_file(self.fs.clone(), cx, move |settings, _| {
settings.theme.agent_font_size = None;
});
} else {
theme::reset_agent_font_size(cx);
@@ -1175,11 +1175,17 @@ impl AgentPanel {
.is_none_or(|model| model.provider.id() != provider.id())
&& let Some(model) = provider.default_model(cx)
{
update_settings_file::<AgentSettings>(
self.fs.clone(),
cx,
move |settings, _| settings.set_model(model),
);
update_settings_file(self.fs.clone(), cx, move |settings, _| {
let provider = model.provider_id().0.to_string();
let model = model.id().0.to_string();
settings
.agent
.get_or_insert_default()
.set_model(LanguageModelSelection {
provider: LanguageModelProviderSetting(provider),
model,
})
});
}
self.new_thread(&NewThread::default(), window, cx);
@@ -1424,11 +1430,7 @@ impl Focusable for AgentPanel {
}
fn agent_panel_dock_position(cx: &App) -> DockPosition {
match AgentSettings::get_global(cx).dock {
AgentDockPosition::Left => DockPosition::Left,
AgentDockPosition::Bottom => DockPosition::Bottom,
AgentDockPosition::Right => DockPosition::Right,
}
AgentSettings::get_global(cx).dock.into()
}
impl EventEmitter<PanelEvent> for AgentPanel {}
@@ -1447,13 +1449,11 @@ impl Panel for AgentPanel {
}
fn set_position(&mut self, position: DockPosition, _: &mut Window, cx: &mut Context<Self>) {
settings::update_settings_file::<AgentSettings>(self.fs.clone(), cx, move |settings, _| {
let dock = match position {
DockPosition::Left => AgentDockPosition::Left,
DockPosition::Bottom => AgentDockPosition::Bottom,
DockPosition::Right => AgentDockPosition::Right,
};
settings.set_dock(dock);
settings::update_settings_file(self.fs.clone(), cx, move |settings, _| {
settings
.agent
.get_or_insert_default()
.set_dock(position.into());
});
}
+2 -2
View File
@@ -23,7 +23,7 @@ use std::rc::Rc;
use std::sync::Arc;
use agent::ThreadId;
use agent_settings::{AgentProfileId, AgentSettings, LanguageModelSelection};
use agent_settings::{AgentProfileId, AgentSettings};
use assistant_slash_command::SlashCommandRegistry;
use client::Client;
use command_palette_hooks::CommandPaletteFilter;
@@ -39,7 +39,7 @@ use project::agent_server_store::AgentServerCommand;
use prompt_store::PromptBuilder;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings as _, SettingsStore};
use settings::{LanguageModelSelection, Settings as _, SettingsStore};
use std::any::TypeId;
use crate::agent_configuration::{ConfigureContextServerModal, ManageProfilesModal};
@@ -5,7 +5,6 @@ use extension::ExtensionManifest;
use fs::Fs;
use gpui::WeakEntity;
use language::LanguageRegistry;
use project::project_settings::ProjectSettings;
use settings::update_settings_file;
use ui::prelude::*;
use util::ResultExt;
@@ -69,8 +68,9 @@ fn remove_context_server_settings(
fs: Arc<dyn Fs>,
cx: &mut App,
) {
update_settings_file::<ProjectSettings>(fs, cx, move |settings, _| {
update_settings_file(fs, cx, move |settings, _| {
settings
.project
.context_servers
.retain(|server_id, _| !context_server_ids.contains(server_id));
});
+11 -9
View File
@@ -1,11 +1,10 @@
use crate::{ManageProfiles, ToggleProfileSelector};
use agent_settings::{
AgentDockPosition, AgentProfile, AgentProfileId, AgentSettings, AvailableProfiles,
builtin_profiles,
AgentProfile, AgentProfileId, AgentSettings, AvailableProfiles, builtin_profiles,
};
use fs::Fs;
use gpui::{Action, Entity, FocusHandle, Subscription, prelude::*};
use settings::{Settings as _, SettingsStore, update_settings_file};
use settings::{DockPosition, Settings as _, SettingsStore, update_settings_file};
use std::sync::Arc;
use ui::{
ContextMenu, ContextMenuEntry, DocumentationEdge, DocumentationSide, PopoverMenu,
@@ -142,10 +141,13 @@ impl ProfileSelector {
let fs = self.fs.clone();
let provider = self.provider.clone();
move |_window, cx| {
update_settings_file::<AgentSettings>(fs.clone(), cx, {
update_settings_file(fs.clone(), cx, {
let profile_id = profile_id.clone();
move |settings, _cx| {
settings.set_profile(profile_id);
settings
.agent
.get_or_insert_default()
.set_profile(profile_id.0);
}
});
@@ -216,10 +218,10 @@ impl Render for ProfileSelector {
}
}
fn documentation_side(position: AgentDockPosition) -> DocumentationSide {
fn documentation_side(position: DockPosition) -> DocumentationSide {
match position {
AgentDockPosition::Left => DocumentationSide::Right,
AgentDockPosition::Bottom => DocumentationSide::Left,
AgentDockPosition::Right => DocumentationSide::Left,
DockPosition::Left => DocumentationSide::Right,
DockPosition::Bottom => DocumentationSide::Left,
DockPosition::Right => DocumentationSide::Left,
}
}
+15 -7
View File
@@ -3,7 +3,7 @@ use crate::{
language_model_selector::{LanguageModelSelector, language_model_selector},
ui::BurnModeTooltip,
};
use agent_settings::{AgentSettings, CompletionMode};
use agent_settings::CompletionMode;
use anyhow::Result;
use assistant_slash_command::{SlashCommand, SlashCommandOutputSection, SlashCommandWorkingSet};
use assistant_slash_commands::{DefaultSlashCommand, FileSlashCommand, selections_creases};
@@ -41,7 +41,10 @@ use project::{Project, Worktree};
use project::{ProjectPath, lsp_store::LocalLspAdapterDelegate};
use rope::Point;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore, update_settings_file};
use settings::{
LanguageModelProviderSetting, LanguageModelSelection, Settings, SettingsStore,
update_settings_file,
};
use std::{
any::TypeId,
cmp,
@@ -294,11 +297,16 @@ impl TextThreadEditor {
language_model_selector(
|cx| LanguageModelRegistry::read_global(cx).default_model(),
move |model, cx| {
update_settings_file::<AgentSettings>(
fs.clone(),
cx,
move |settings, _| settings.set_model(model.clone()),
);
update_settings_file(fs.clone(), cx, move |settings, _| {
let provider = model.provider_id().0.to_string();
let model = model.id().0.to_string();
settings.agent.get_or_insert_default().set_model(
LanguageModelSelection {
provider: LanguageModelProviderSetting(provider),
model,
},
)
});
},
window,
cx,