From d9e7cec281ddfb2c26ace0911cdfeb44e746f41f Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Tue, 16 Sep 2025 15:29:34 -0600 Subject: [PATCH] project --- crates/context_server/src/context_server.rs | 4 - crates/git_hosting_providers/src/settings.rs | 4 +- crates/project/src/context_server_store.rs | 37 ++-- crates/project/src/project.rs | 49 +++-- crates/project/src/project_settings.rs | 184 +++++++++++------- crates/project/src/project_tests.rs | 4 +- crates/settings/src/settings_content.rs | 49 ++--- crates/settings/src/settings_content/agent.rs | 10 +- .../settings/src/settings_content/project.rs | 70 ++++--- crates/settings/src/settings_content/theme.rs | 2 +- 10 files changed, 234 insertions(+), 179 deletions(-) diff --git a/crates/context_server/src/context_server.rs b/crates/context_server/src/context_server.rs index 92b1b2277239a876f35d9d430beb2276361d8f90..52ed524220947430df3e63fced367ca4eb223fff 100644 --- a/crates/context_server/src/context_server.rs +++ b/crates/context_server/src/context_server.rs @@ -12,13 +12,9 @@ use std::{fmt::Display, path::PathBuf}; use anyhow::Result; use client::Client; -use collections::HashMap; use gpui::AsyncApp; use parking_lot::RwLock; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; pub use settings::ContextServerCommand; -use util::redact::should_redact; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ContextServerId(pub Arc); diff --git a/crates/git_hosting_providers/src/settings.rs b/crates/git_hosting_providers/src/settings.rs index fd706adf73cfe5890dac4133fd5be1d4ac967a05..b6aabc47f3fba9fca6c9b908de87ca7319afa616 100644 --- a/crates/git_hosting_providers/src/settings.rs +++ b/crates/git_hosting_providers/src/settings.rs @@ -60,12 +60,12 @@ pub struct GitHostingProviderSettings { impl Settings for GitHostingProviderSettings { fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self { Self { - git_hosting_providers: content.git_hosting_providers.clone().unwrap(), + git_hosting_providers: content.project.git_hosting_providers.clone().unwrap(), } } fn refine(&mut self, content: &settings::SettingsContent, _: &mut App) { - if let Some(more) = &content.git_hosting_providers { + if let Some(more) = &content.project.git_hosting_providers { self.git_hosting_providers.extend_from_slice(&more.clone()); } } diff --git a/crates/project/src/context_server_store.rs b/crates/project/src/context_server_store.rs index 20188df5c4ae38b2ae305daee5b3eecc25319951..40a14ed37b6db3e5f25dfc5e6926f7a38bc2b510 100644 --- a/crates/project/src/context_server_store.rs +++ b/crates/project/src/context_server_store.rs @@ -915,7 +915,7 @@ mod tests { set_context_server_configuration( vec![( server_1_id.0.clone(), - ContextServerSettings::Extension { + settings::ContextServerSettingsContent::Extension { enabled: true, settings: json!({ "somevalue": false @@ -934,7 +934,7 @@ mod tests { set_context_server_configuration( vec![( server_1_id.0.clone(), - ContextServerSettings::Extension { + settings::ContextServerSettingsContent::Extension { enabled: true, settings: json!({ "somevalue": false @@ -961,7 +961,7 @@ mod tests { vec![ ( server_1_id.0.clone(), - ContextServerSettings::Extension { + settings::ContextServerSettingsContent::Extension { enabled: true, settings: json!({ "somevalue": false @@ -970,7 +970,7 @@ mod tests { ), ( server_2_id.0.clone(), - ContextServerSettings::Custom { + settings::ContextServerSettingsContent::Custom { enabled: true, command: ContextServerCommand { path: "somebinary".into(), @@ -1002,7 +1002,7 @@ mod tests { vec![ ( server_1_id.0.clone(), - ContextServerSettings::Extension { + settings::ContextServerSettingsContent::Extension { enabled: true, settings: json!({ "somevalue": false @@ -1011,7 +1011,7 @@ mod tests { ), ( server_2_id.0.clone(), - ContextServerSettings::Custom { + settings::ContextServerSettingsContent::Custom { enabled: true, command: ContextServerCommand { path: "somebinary".into(), @@ -1027,6 +1027,7 @@ mod tests { cx.run_until_parked(); } + dbg!("hi"); // Ensure that mcp-2 is removed once it is removed from the settings { @@ -1038,7 +1039,7 @@ mod tests { set_context_server_configuration( vec![( server_1_id.0.clone(), - ContextServerSettings::Extension { + settings::ContextServerSettingsContent::Extension { enabled: true, settings: json!({ "somevalue": false @@ -1054,6 +1055,7 @@ mod tests { assert_eq!(store.read(cx).status_for_server(&server_2_id), None); }); } + dbg!("bye"); // Ensure that nothing happens if the settings do not change { @@ -1061,7 +1063,7 @@ mod tests { set_context_server_configuration( vec![( server_1_id.0.clone(), - ContextServerSettings::Extension { + settings::ContextServerSettingsContent::Extension { enabled: true, settings: json!({ "somevalue": false @@ -1147,7 +1149,7 @@ mod tests { set_context_server_configuration( vec![( server_1_id.0.clone(), - ContextServerSettings::Custom { + settings::ContextServerSettingsContent::Custom { enabled: false, command: ContextServerCommand { path: "somebinary".into(), @@ -1176,7 +1178,7 @@ mod tests { set_context_server_configuration( vec![( server_1_id.0.clone(), - ContextServerSettings::Custom { + settings::ContextServerSettingsContent::Custom { enabled: true, command: ContextServerCommand { path: "somebinary".into(), @@ -1194,18 +1196,17 @@ mod tests { } fn set_context_server_configuration( - context_servers: Vec<(Arc, ContextServerSettings)>, + context_servers: Vec<(Arc, settings::ContextServerSettingsContent)>, cx: &mut TestAppContext, ) { cx.update(|cx| { SettingsStore::update_global(cx, |store, cx| { - let mut settings = ProjectSettings::default(); - for (id, config) in context_servers { - settings.context_servers.insert(id, config); - } - store - .set_user_settings(&serde_json::to_string(&settings).unwrap(), cx) - .unwrap(); + store.update_user_settings(cx, |content| { + content.project.context_servers.clear(); + for (id, config) in context_servers { + content.project.context_servers.insert(id, config); + } + }); }) }); } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index e880144368160ae445db7c79141a2c016b84dd1a..571a7b5db48eaf3660aadf268d61bf1864140066 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -985,7 +985,7 @@ impl settings::Settings for DisableAiSettings { fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) { // If disable_ai is true *in any file*, it is disabled. - self.disable_ai = self.disable_ai || content.project.disable_ai.unwrap_or(false); + self.disable_ai = self.disable_ai || content.disable_ai.unwrap_or(false); } fn import_from_vscode( @@ -3244,9 +3244,22 @@ impl Project { ) { self.buffers_needing_diff.insert(buffer.downgrade()); let first_insertion = self.buffers_needing_diff.len() == 1; - let settings = ProjectSettings::get_global(cx); - let delay = settings.git.gutter_debounce; + let delay = if let Some(delay) = settings.git.gutter_debounce { + delay + } else { + if first_insertion { + let this = cx.weak_entity(); + cx.defer(move |cx| { + if let Some(this) = this.upgrade() { + this.update(cx, |this, cx| { + this.recalculate_buffer_diffs(cx).detach(); + }); + } + }); + } + return; + }; const MIN_DELAY: u64 = 50; let delay = delay.max(MIN_DELAY); @@ -5632,32 +5645,42 @@ mod disable_ai_settings_tests { #[gpui::test] async fn test_disable_ai_settings_security(cx: &mut TestAppContext) { cx.update(|cx| { - let mut store = SettingsStore::new(cx, &settings::test_settings()); - store.register_setting::(cx); + settings::init(cx); + Project::init_settings(cx); + // Test 1: Default is false (AI enabled) assert!( !DisableAiSettings::get_global(cx).disable_ai, "Default should allow AI" ); + }); - let disable_true = serde_json::json!({ - "disable_ai": true - }) - .to_string(); - let disable_false = serde_json::json!({ - "disable_ai": false - }) - .to_string(); + let disable_true = serde_json::json!({ + "disable_ai": true + }) + .to_string(); + let disable_false = serde_json::json!({ + "disable_ai": false + }) + .to_string(); + cx.update_global::(|store, cx| { store.set_user_settings(&disable_false, cx).unwrap(); store.set_global_settings(&disable_true, cx).unwrap(); + }); + cx.update(|cx| { assert!( DisableAiSettings::get_global(cx).disable_ai, "Local false cannot override global true" ); + }); + cx.update_global::(|store, cx| { store.set_global_settings(&disable_false, cx).unwrap(); store.set_user_settings(&disable_true, cx).unwrap(); + }); + + cx.update(|cx| { assert!( DisableAiSettings::get_global(cx).disable_ai, "Local false cannot override global true" diff --git a/crates/project/src/project_settings.rs b/crates/project/src/project_settings.rs index 212d3262e6fff1907cf2bd6ac91af8300cc1c739..b9bd386de361c77edbba0fb4570652983b6ed0fa 100644 --- a/crates/project/src/project_settings.rs +++ b/crates/project/src/project_settings.rs @@ -1,14 +1,10 @@ use anyhow::Context as _; -use clock::Global; use collections::HashMap; use context_server::ContextServerCommand; use dap::adapters::DebugAdapterName; use fs::Fs; use futures::StreamExt as _; -use gpui::{ - App, AsyncApp, BorrowAppContext, Context, Entity, EventEmitter, SharedString, Subscription, - Task, -}; +use gpui::{App, AsyncApp, BorrowAppContext, Context, Entity, EventEmitter, Subscription, Task}; use lsp::LanguageServerName; use paths::{ EDITORCONFIG_NAME, local_debug_file_relative_path, local_settings_file_relative_path, @@ -17,16 +13,17 @@ use paths::{ }; use rpc::{ AnyProtoClient, TypedEnvelope, - proto::{self, FromProto, Message, REMOTE_SERVER_PROJECT_ID, ToProto}, + proto::{self, FromProto, REMOTE_SERVER_PROJECT_ID, ToProto}, }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +pub use settings::DirenvSettings; +pub use settings::LspSettings; use settings::{ - InvalidSettingsError, LocalSettingsKind, Settings, SettingsKey, SettingsLocation, - SettingsStore, SettingsUi, parse_json_with_comments, watch_config_file, + InvalidSettingsError, LocalSettingsKind, Settings, SettingsLocation, SettingsStore, SettingsUi, + parse_json_with_comments, watch_config_file, }; use std::{ - collections::BTreeMap, path::{Path, PathBuf}, sync::Arc, time::Duration, @@ -51,13 +48,13 @@ pub struct ProjectSettings { /// name to the lsp value. /// Default: null // todo! should these hash map types be Map or Map - pub lsp: HashMap, + pub lsp: HashMap, /// Common language server settings. pub global_lsp_settings: GlobalLspSettings, /// Configuration for Debugger-related features - pub dap: HashMap, + pub dap: HashMap, /// Settings for context servers used for AI-related features. pub context_servers: HashMap, ContextServerSettings>, @@ -78,10 +75,35 @@ pub struct ProjectSettings { pub session: SessionSettings, } -#[derive(Debug, Clone, Default, PartialEq)] -pub struct DapSettings { - pub binary: String, - pub args: Vec, +#[derive(Copy, Clone, Debug)] +pub struct SessionSettings { + /// Whether or not to restore unsaved buffers on restart. + /// + /// If this is true, user won't be prompted whether to save/discard + /// dirty files when closing the application. + /// + /// Default: true + pub restore_unsaved_buffers: bool, +} + +#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct NodeBinarySettings { + /// The path to the Node binary. + pub path: Option, + /// The path to the npm binary Zed should use (defaults to `.path/../npm`). + pub npm_path: Option, + /// If enabled, Zed will download its own copy of Node. + pub ignore_system_version: bool, +} + +impl From for NodeBinarySettings { + fn from(settings: settings::NodeBinarySettings) -> Self { + Self { + path: settings.path, + npm_path: settings.npm_path, + ignore_system_version: settings.ignore_system_version.unwrap_or(false), + } + } } /// Common language server settings. @@ -281,7 +303,7 @@ pub struct GitSettings { /// Sets the debounce threshold (in milliseconds) after which changes are reflected in the git gutter. /// /// Default: null - pub gutter_debounce: u64, + pub gutter_debounce: Option, /// Whether or not to show git blame data inline in /// the currently focused line. /// @@ -415,7 +437,7 @@ pub struct InlineDiagnosticsSettings { /// Default: 0 pub min_column: u32, - pub max_severity: DiagnosticSeverity, + pub max_severity: Option, } #[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] @@ -432,7 +454,7 @@ pub struct LspPullDiagnosticsSettings { } impl Settings for ProjectSettings { - fn from_defaults(content: &settings::SettingsContent, cx: &mut App) -> Self { + fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self { let project = &content.project.clone(); let diagnostics = content.diagnostics.as_ref().unwrap(); let lsp_pull_diagnostics = diagnostics.lsp_pull_diagnostics.as_ref().unwrap(); @@ -441,7 +463,7 @@ impl Settings for ProjectSettings { let git = content.git.as_ref().unwrap(); let git_settings = GitSettings { git_gutter: git.git_gutter.unwrap(), - gutter_debounce: git.gutter_debounce.unwrap(), + gutter_debounce: git.gutter_debounce, inline_blame: { let inline = git.inline_blame.unwrap(); InlineBlameSettings { @@ -474,21 +496,18 @@ impl Settings for ProjectSettings { .map(|(key, value)| (LanguageServerName(key.into()), value.into())) .collect(), global_lsp_settings: GlobalLspSettings { - button: content.global_lsp_settings.unwrap().button.unwrap(), + button: content + .global_lsp_settings + .as_ref() + .unwrap() + .button + .unwrap(), }, dap: project .dap .clone() .into_iter() - .map(|(key, value)| { - ( - DebugAdapterName(key.into()), - DapSettings { - binary: value.binary.unwrap(), - args: value.args, - }, - ) - }) + .map(|(key, value)| (DebugAdapterName(key.into()), value)) .collect(), diagnostics: DiagnosticsSettings { button: diagnostics.button.unwrap(), @@ -502,17 +521,19 @@ impl Settings for ProjectSettings { update_debounce_ms: inline_diagnostics.update_debounce_ms.unwrap(), padding: inline_diagnostics.padding.unwrap(), min_column: inline_diagnostics.min_column.unwrap(), - max_severity: inline_diagnostics.max_severity.unwrap().into(), + max_severity: inline_diagnostics.max_severity.map(Into::into), }, }, git: git_settings, - node: content.node.clone(), - load_direnv: project.load_direnv.unwrap(), - session: content.session.clone(), + node: content.node.clone().unwrap().into(), + load_direnv: project.load_direnv.clone().unwrap(), + session: SessionSettings { + restore_unsaved_buffers: content.session.unwrap().restore_unsaved_buffers.unwrap(), + }, } } - fn refine(&mut self, content: &settings::SettingsContent, cx: &mut App) { + fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) { let project = &content.project; self.context_servers.extend( project @@ -521,16 +542,14 @@ impl Settings for ProjectSettings { .into_iter() .map(|(key, value)| (key, value.into())), ); - self.dap - .extend(project.dap.clone().into_iter().filter_map(|(key, value)| { - Some(( - DebugAdapterName(key.into()), - DapSettings { - binary: value.binary?, - args: value.args, - }, - )) - })); + dbg!(&self.context_servers); + self.dap.extend( + project + .dap + .clone() + .into_iter() + .filter_map(|(key, value)| Some((DebugAdapterName(key.into()), value))), + ); if let Some(diagnostics) = content.diagnostics.as_ref() { if let Some(inline) = &diagnostics.inline { self.diagnostics.inline.enabled.merge_from(&inline.enabled); @@ -543,10 +562,9 @@ impl Settings for ProjectSettings { .inline .min_column .merge_from(&inline.min_column); - self.diagnostics - .inline - .max_severity - .merge_from(&inline.max_severity.map(Into::into)); + if let Some(max_severity) = inline.max_severity { + self.diagnostics.inline.max_severity = Some(max_severity.into()) + } } self.diagnostics.button.merge_from(&diagnostics.button); @@ -595,18 +613,37 @@ impl Settings for ProjectSettings { } self.git.git_gutter.merge_from(&git.git_gutter); self.git.hunk_style.merge_from(&git.hunk_style); - self.git.gutter_debounce.merge_from(&git.gutter_debounce); + if let Some(debounce) = git.gutter_debounce { + self.git.gutter_debounce = Some(debounce); + } } - self.global_lsp_settings = content.global_lsp_settings.clone(); - self.load_direnv = content.project.load_direnv.clone(); - self.lsp.extend( - content - .project - .lsp - .clone() - .into_iter() - .map(|(key, value)| (key, lsp_settings)), + self.global_lsp_settings.button.merge_from( + &content + .global_lsp_settings + .as_ref() + .and_then(|settings| settings.button), ); + self.load_direnv + .merge_from(&content.project.load_direnv.clone()); + + for (key, value) in content.project.lsp.clone() { + self.lsp.insert(LanguageServerName(key.into()), value); + } + + if let Some(node) = content.node.as_ref() { + self.node + .ignore_system_version + .merge_from(&node.ignore_system_version); + if let Some(path) = node.path.clone() { + self.node.path = Some(path); + } + if let Some(npm_path) = node.npm_path.clone() { + self.node.npm_path = Some(npm_path); + } + } + self.session + .restore_unsaved_buffers + .merge_from(&content.session.and_then(|s| s.restore_unsaved_buffers)); } fn import_from_vscode( @@ -750,17 +787,19 @@ impl SettingsObserver { if let Some(upstream_client) = upstream_client { let mut user_settings = None; user_settings_watcher = Some(cx.observe_global::(move |_, cx| { - let new_settings = cx.global::().raw_user_settings(); - if Some(new_settings) != user_settings.as_ref() { - if let Some(new_settings_string) = serde_json::to_string(new_settings).ok() - { - user_settings = new_settings.clone(); - upstream_client - .send(proto::UpdateUserSettings { - project_id: REMOTE_SERVER_PROJECT_ID, - contents: new_settings_string, - }) - .log_err(); + if let Some(new_settings) = cx.global::().raw_user_settings() { + if Some(new_settings) != user_settings.as_ref() { + if let Some(new_settings_string) = + serde_json::to_string(new_settings).ok() + { + user_settings = Some(new_settings.clone()); + upstream_client + .send(proto::UpdateUserSettings { + project_id: REMOTE_SERVER_PROJECT_ID, + contents: new_settings_string, + }) + .log_err(); + } } } })); @@ -870,10 +909,9 @@ impl SettingsObserver { envelope: TypedEnvelope, cx: AsyncApp, ) -> anyhow::Result<()> { - let new_settings = serde_json::from_str::(&envelope.payload.contents) - .with_context(|| { - format!("deserializing {} user settings", envelope.payload.contents) - })?; + let new_settings = serde_json::from_str(&envelope.payload.contents).with_context(|| { + format!("deserializing {} user settings", envelope.payload.contents) + })?; cx.update_global(|settings_store: &mut SettingsStore, cx| { settings_store .set_raw_user_settings(new_settings, cx) diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 5daaa09b9bafc7aa6419e017f2c378a020bdba91..915ae07230bdae01bc16f2cd4318658b42dcdfb7 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -8769,8 +8769,8 @@ async fn test_rescan_with_gitignore(cx: &mut gpui::TestAppContext) { init_test(cx); cx.update(|cx| { cx.update_global::(|store, cx| { - store.update_user_settings::(cx, |project_settings| { - project_settings.file_scan_exclusions = Some(Vec::new()); + store.update_user_settings(cx, |settings| { + settings.project.worktree.file_scan_exclusions = Some(Vec::new()); }); }); }); diff --git a/crates/settings/src/settings_content.rs b/crates/settings/src/settings_content.rs index 3945db5c9ec391273a464b32ee055599ffcf30c2..3267d7cab3d13bd58f66193bbeb01d093b465cbe 100644 --- a/crates/settings/src/settings_content.rs +++ b/crates/settings/src/settings_content.rs @@ -19,7 +19,7 @@ pub use util::serde::default_true; use crate::ActiveSettingsProfileName; -#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, PartialEq, Default, Clone, Serialize, Deserialize, JsonSchema)] pub struct SettingsContent { #[serde(flatten)] pub project: ProjectSettingsContent, @@ -45,9 +45,6 @@ pub struct SettingsContent { /// Configuration for Git-related features pub git: Option, - /// The list of custom Git hosting providers. - pub git_hosting_providers: Option>, - /// Common language server settings. pub global_lsp_settings: Option, @@ -70,7 +67,7 @@ pub struct SettingsContent { pub server_url: Option, /// Configuration for session-related features - pub session: Option, + pub session: Option, /// Control what info is collected by Zed. pub telemetry: Option, @@ -86,6 +83,11 @@ pub struct SettingsContent { // Settings related to calls in Zed pub calls: Option, + + /// Whether to disable all AI features in Zed. + /// + /// Default: false + pub disable_ai: Option, } impl SettingsContent { @@ -101,7 +103,7 @@ pub struct ServerSettingsContent { pub project: ProjectSettingsContent, } -#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize, JsonSchema)] pub struct UserSettingsContent { #[serde(flatten)] pub content: SettingsContent, @@ -211,7 +213,7 @@ pub enum TitleBarVisibilityContent { } /// Configuration of audio in Zed. -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] +#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, Debug)] pub struct AudioSettingsContent { /// Opt into the new audio system. #[serde(rename = "experimental.rodio_audio", default)] @@ -231,31 +233,8 @@ pub struct AudioSettingsContent { pub control_output_volume: Option, } -/// A custom Git hosting provider. -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] -pub struct GitHostingProviderConfig { - /// The type of the provider. - /// - /// Must be one of `github`, `gitlab`, or `bitbucket`. - pub provider: GitHostingProviderKind, - - /// The base URL for the provider (e.g., "https://code.corp.big.com"). - pub base_url: String, - - /// The display name for the provider (e.g., "BigCorp GitHub"). - pub name: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum GitHostingProviderKind { - Github, - Gitlab, - Bitbucket, -} - /// Control what info is collected by Zed. -#[derive(Default, Clone, Serialize, Deserialize, JsonSchema, Debug)] +#[derive(Default, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Debug)] pub struct TelemetrySettingsContent { /// Send debug info like crash reports. /// @@ -267,7 +246,7 @@ pub struct TelemetrySettingsContent { pub metrics: Option, } -#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Clone)] pub struct DebuggerSettingsContent { /// Determines the stepping granularity. /// @@ -322,21 +301,21 @@ pub enum DockPosition { } /// Settings for slash commands. -#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema)] +#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema, PartialEq, Eq)] pub struct SlashCommandSettings { /// Settings for the `/cargo-workspace` slash command. pub cargo_workspace: Option, } /// Settings for the `/cargo-workspace` slash command. -#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema)] +#[derive(Deserialize, Serialize, Debug, Default, Clone, JsonSchema, PartialEq, Eq)] pub struct CargoWorkspaceCommandSettings { /// Whether `/cargo-workspace` is enabled. pub enabled: Option, } /// Configuration of voice calls in Zed. -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] +#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, Debug)] pub struct CallSettingsContent { /// Whether the microphone should be muted when joining a channel or a call. /// diff --git a/crates/settings/src/settings_content/agent.rs b/crates/settings/src/settings_content/agent.rs index c051491a6537808241cc2eea30c25fa0dfd40a91..1c6296f67ec4e4dbfce3e16522f5a255324ba10c 100644 --- a/crates/settings/src/settings_content/agent.rs +++ b/crates/settings/src/settings_content/agent.rs @@ -4,7 +4,7 @@ use schemars::{JsonSchema, json_schema}; use serde::{Deserialize, Serialize}; use std::{borrow::Cow, path::PathBuf, sync::Arc}; -#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug, Default)] +#[derive(Clone, PartialEq, Serialize, Deserialize, JsonSchema, Debug, Default)] pub struct AgentSettingsContent { /// Whether the Agent is enabled. /// @@ -174,7 +174,7 @@ pub struct ContextServerPresetContent { pub tools: IndexMap, bool>, } -#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema)] +#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum AgentDockPosition { Left, @@ -183,7 +183,7 @@ pub enum AgentDockPosition { Bottom, } -#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema)] +#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum DefaultAgentView { #[default] @@ -191,7 +191,7 @@ pub enum DefaultAgentView { TextThread, } -#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema, PartialEq)] #[serde(rename_all = "snake_case")] pub enum NotifyWhenAgentWaiting { #[default] @@ -263,7 +263,7 @@ impl From<&str> for LanguageModelProviderSetting { } } -#[derive(Default, Deserialize, Serialize, Clone, JsonSchema, Debug, PartialEq)] +#[derive(Default, PartialEq, Deserialize, Serialize, Clone, JsonSchema, Debug)] pub struct AllAgentServersSettings { pub gemini: Option, pub claude: Option, diff --git a/crates/settings/src/settings_content/project.rs b/crates/settings/src/settings_content/project.rs index b35c829085135ab48909b763e313ad0919c0f9f5..541b700950be45b83d54e93427f236b867cfb136 100644 --- a/crates/settings/src/settings_content/project.rs +++ b/crates/settings/src/settings_content/project.rs @@ -5,7 +5,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use util::serde::default_true; -use crate::AllLanguageSettingsContent; +use crate::{AllLanguageSettingsContent, SlashCommandSettings}; #[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize, JsonSchema)] pub struct ProjectSettingsContent { @@ -24,11 +24,11 @@ pub struct ProjectSettingsContent { /// name to the lsp value. /// Default: null #[serde(default)] - pub lsp: HashMap, LspSettingsContent>, + pub lsp: HashMap, LspSettings>, /// Configuration for Debugger-related features #[serde(default)] - pub dap: HashMap, DapSettingsContent>, + pub dap: HashMap, DapSettings>, /// Settings for context servers used for AI-related features. #[serde(default)] @@ -39,6 +39,9 @@ pub struct ProjectSettingsContent { /// Settings for slash commands. pub slash_commands: Option, + + /// The list of custom Git hosting providers. + pub git_hosting_providers: Option>, } #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema)] @@ -80,7 +83,7 @@ pub struct WorktreeSettingsContent { #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, Hash)] #[serde(rename_all = "snake_case")] -pub struct LspSettingsContent { +pub struct LspSettings { pub binary: Option, pub initialization_options: Option, pub settings: Option, @@ -92,7 +95,7 @@ pub struct LspSettingsContent { pub fetch: Option, } -impl Default for LspSettingsContent { +impl Default for LspSettings { fn default() -> Self { Self { binary: None, @@ -119,7 +122,7 @@ pub struct FetchSettings { } /// Common language server settings. -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] pub struct GlobalLspSettingsContent { /// Whether to show the LSP servers button in the status bar. /// @@ -128,31 +131,23 @@ pub struct GlobalLspSettingsContent { } // todo! binary is actually just required, shouldn't be an option -#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] -pub struct DapSettingsContent { +pub struct DapSettings { pub binary: Option, #[serde(default)] pub args: Vec, } -#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)] -pub struct SessionSettings { +#[derive(Copy, Clone, PartialEq, Eq, Debug, Serialize, Deserialize, JsonSchema)] +pub struct SessionSettingsContent { /// Whether or not to restore unsaved buffers on restart. /// /// If this is true, user won't be prompted whether to save/discard /// dirty files when closing the application. /// /// Default: true - pub restore_unsaved_buffers: bool, -} - -impl Default for SessionSettings { - fn default() -> Self { - Self { - restore_unsaved_buffers: true, - } - } + pub restore_unsaved_buffers: Option, } #[derive(Deserialize, Serialize, Clone, PartialEq, Eq, JsonSchema, Debug)] @@ -213,7 +208,7 @@ impl std::fmt::Debug for ContextServerCommand { } } -#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] +#[derive(Copy, Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema)] pub struct GitSettings { /// Whether or not to show the git gutter. /// @@ -238,7 +233,7 @@ pub struct GitSettings { pub hunk_style: Option, } -#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)] +#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum GitGutterSetting { /// Show git gutter in tracked files. @@ -248,7 +243,7 @@ pub enum GitGutterSetting { Hide, } -#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)] +#[derive(Clone, Copy, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub struct InlineBlameSettings { /// Whether or not to show git blame data inline in @@ -276,7 +271,7 @@ pub struct InlineBlameSettings { pub show_commit_summary: Option, } -#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema)] +#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub struct BranchPickerSettingsContent { /// Whether to show author name as part of the commit information. @@ -285,7 +280,7 @@ pub struct BranchPickerSettingsContent { pub show_author_name: Option, } -#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)] +#[derive(Clone, Copy, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum GitHunkStyleSetting { /// Show unstaged hunks with a filled background and staged hunks hollow. @@ -295,7 +290,7 @@ pub enum GitHunkStyleSetting { UnstagedHollow, } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] pub struct DiagnosticsSettingsContent { /// Whether to show the project diagnostics button in the status bar. pub button: Option, @@ -349,7 +344,7 @@ pub struct InlineDiagnosticsSettingsContent { pub max_severity: Option, } -#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] pub struct NodeBinarySettings { /// The path to the Node binary. pub path: Option, @@ -382,3 +377,26 @@ pub enum DiagnosticSeverityContent { #[serde(alias = "all")] Hint, } + +/// A custom Git hosting provider. +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, JsonSchema)] +pub struct GitHostingProviderConfig { + /// The type of the provider. + /// + /// Must be one of `github`, `gitlab`, or `bitbucket`. + pub provider: GitHostingProviderKind, + + /// The base URL for the provider (e.g., "https://code.corp.big.com"). + pub base_url: String, + + /// The display name for the provider (e.g., "BigCorp GitHub"). + pub name: String, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum GitHostingProviderKind { + Github, + Gitlab, + Bitbucket, +} diff --git a/crates/settings/src/settings_content/theme.rs b/crates/settings/src/settings_content/theme.rs index 640726869b6592a890937c63e1a256d7f09b7594..b0798e7c7b359b8c1d9890212d9e2b31982fe440 100644 --- a/crates/settings/src/settings_content/theme.rs +++ b/crates/settings/src/settings_content/theme.rs @@ -7,7 +7,7 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; use std::sync::Arc; /// Settings for rendering text in UI and text buffers. -#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] +#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize, JsonSchema)] pub struct ThemeSettingsContent { /// The default font size for text in the UI. #[serde(default)]